誰かが次のことを行うためのコードを提供してください:ファイルのディレクトリがあると仮定してください。そのすべてはプログラムを通して実行される必要があります。プログラムは結果を標準出力に出力します。ディレクトリに入り、各ファイルに対してコマンドを実行し、出力を1つの大きな出力ファイルに連結するスクリプトが必要です。
たとえば、1ファイルに対してコマンドを実行するには、次のようにします。
$ cmd [option] [filename] > results.out
次のbashコードは$ fileをcommandに渡します。$ fileは/ dir内のすべてのファイルを表します。
for file in /dir/*
do
cmd [option] "$file" >> results.out
done
例
el@defiant ~/foo $ touch foo.txt bar.txt baz.txt
el@defiant ~/foo $ for i in *.txt; do echo "hello $i"; done
hello bar.txt
hello baz.txt
hello foo.txt
これはどう:
find /some/directory -maxdepth 1 -type f -exec cmd option {} \; > results.out
-maxdepth 1
引数はfindが再帰的にサブディレクトリに降りるのを防ぎます。 (このようなネストされたディレクトリを処理したい場合は、これを省略することができます。)-type -f
は、プレーンファイルのみを処理することを指定します。-exec cmd option {}
は、見つかった各ファイルについて、指定されたcmd
を使用してoption
を実行し、{}
の代わりにファイル名を使用するよう指示します\;
はコマンドの終わりを表します。cmd
実行からの出力はresults.out
にリダイレクトされます。しかし、ファイルが処理される順序を気にしているのであれば、ループを書くことをお勧めします。私はfind
がinode順にファイルを処理していると思いますが(それは間違っているかもしれませんが)、それはあなたが望むものではないかもしれません。
Raspberry Piのコマンドラインから次のコマンドを実行しています。
for i in *;do omxplayer "$i";done
あるディレクトリから別のディレクトリにすべての.mdファイルをコピーする必要があったので、ここでこれを行いました。
for i in **/*.md;do mkdir -p ../docs/"$i" && rm -r ../docs/"$i" && cp "$i" "../docs/$i" && echo "$i -> ../docs/$i"; done
これはかなり読みにくいので、分解してみましょう。
まずあなたのファイルと一緒にディレクトリにcdしてください。
パターン内の各ファイルのfor i in **/*.md;
mkdir -p ../docs/"$i"
はあなたのファイルを含んでいるフォルダの外のdocsフォルダにそのディレクトリを作ってください。そのファイルと同じ名前の追加のフォルダが作成されます。
rm -r ../docs/"$i"
は、mkdir -p
の結果として作成された余分なフォルダーを削除します
cp "$i" "../docs/$i"
実際のファイルをコピーする
echo "$i -> ../docs/$i"
あなたがしたことをエコーする
; done
その後もずっと幸せに暮らしています
仕事を終わらせるための1つの迅速で汚い方法は:
find directory/ | xargs Command
たとえば、現在のディレクトリにあるすべてのファイルの行数を調べるには、次のようにします。
find . | xargs wc -l
@Jim Lewisのアプローチに基づきます。
これはfind
を使用し、ファイルを変更日順にソートする簡単な解決策です。
$ find directory/ -maxdepth 1 -type f -print0 | \
xargs -r0 stat -c "%y %n" | \
sort | cut -d' ' -f4- | \
xargs -d "\n" -I{} cmd -op1 {}
並べ替えについては、以下を参照してください。
http://www.commandlinefu.com/commands/view/5720/find-files-and-list-them-sorted-by-modification-time
私は簡単な解決策だと思います:
sh /dir/* > ./result.txt
承認された/賛成された回答は素晴らしいですが、彼らはいくつかの重要な詳細を欠いています。この記事では、シェルのパス名展開(glob)が失敗したとき、ファイル名に改行/ダッシュ記号が埋め込まれているとき、およびコマンド出力方向をforループの外側に移動するときの対処方法について説明します。
*
を使用してShell glob展開を実行するとき、ディレクトリに no ファイルが存在せず、展開されていないglob文字列がコマンドに渡されると、展開が失敗する可能性があります。望ましくない結果をもたらす可能性があるファイル上で実行してください。 bash
シェルはnullglob
を使用してこれのための拡張シェルオプションを提供します。ですから、ループは基本的にあなたのファイルを含むディレクトリの中で次のようになります。
shopt -s nullglob
for file in ./*; do
cmdToRun [option] -- "$file"
done
これにより、式./*
がファイルを返すときにforループを安全に終了できます(ディレクトリが空の場合)。
あるいはPOSIX準拠の方法で(nullglob
はbash
特有です)
for file in ./*; do
[ -f "$file" ] || continue
cmdToRun [option] -- "$file"
done
これにより、式が1回失敗したときにループ内に入り、[ -f "$file" ]
という条件で、展開されていない文字列./*
がそのディレクトリ内で有効なファイル名であるかどうかを確認できます。そのため、この状態で失敗した場合は、continue
を使用して、その後は実行されないfor
ループに戻ります。
ファイル名引数を渡す直前に--
を使用することにも注意してください。前述のように、シェルのファイル名にはファイル名のどこかにダッシュが含まれているため、これが必要です。シェルコマンドの中にはそれを解釈してそれらをコマンドオプションとして扱い、フラグが与えられていればコマンドを実行します。
その場合、--
はコマンドラインオプションの終わりを示します。つまり、この時点以降の文字列はコマンドフラグとして解析されるのではなく、ファイル名としてのみ解析されます。
ファイル名を二重引用符で囲むと、名前にglob文字または空白が含まれる場合は正しく解決されます。しかし、* nixファイル名には改行を含めることができます。そのため、有効なファイル名の一部にできない唯一の文字、つまりnullバイト(\0
)を使用してファイル名の制限を解除します。 bash
は内部的にnullバイトが文字列の終わりを示すために使われるC
スタイルの文字列を使っているので、これは正しい候補です。
そのため、Shellのprintf
オプションを使用して、read
コマンドの-d
オプションを使用してこのNULLバイトでファイルを区切ると、以下のようになります。
( shopt -s nullglob; printf '%s\0' ./* ) | while read -rd '' file; do
cmdToRun [option] -- "$file"
done
nullglob
とprintf
は(..)
で囲まれています。これは、コマンドが終了した後でnullglob
オプションが親シェルに反映されるのを避けるため、基本的にサブシェル(子シェル)で実行されることを意味します。 read
コマンドの-d ''
オプションは POSIXに準拠していないため、これを行うにはbash
シェルが必要です。 find
コマンドを使用すると、これは次のように実行できます。
while IFS= read -r -d '' file; do
cmdToRun [option] -- "$file"
done < <(find -maxdepth 1 -type f -print0)
-print0
をサポートしないfind
の実装(GNUとFreeBSDの実装を除く)では、これはprintf
を使ってエミュレートすることができます。
find . -maxdepth 1 -type f -exec printf '%s\0' {} \; | xargs -0 cmdToRun [option] --
もう1つの重要な修正は、ファイルの入出力回数を減らすためにforループの外にリダイレクトを移動することです。ループ内で使用する場合、シェルはforループの繰り返しごとに2回、ファイルに関連付けられたファイル記述子を開くために1回、閉じるために1回、システムコールを実行する必要があります。これは、大きな繰り返しを実行するためのパフォーマンスのボトルネックになります。推奨される提案はそれをループの外側に移動することです。
この修正で上記のコードを拡張すると、
( shopt -s nullglob; printf '%s\0' ./* ) | while read -rd '' file; do
cmdToRun [option] -- "$file"
done > results.out
これは基本的に、ファイルの入力が繰り返されるたびにコマンドの内容をstdoutに出力し、ループが終了したら、ターゲットファイルを1回開いてstdoutの内容を書き込んで保存します。同じfind
バージョンは、
while IFS= read -r -d '' file; do
cmdToRun [option] -- "$file"
done < <(find -maxdepth 1 -type f -print0) > results.out
Jim Lewisの答えでうまくいくことがわかりました。
$ export DIR=/path/dir && cd $DIR && chmod -R +x *
$ find . -maxdepth 1 -type f -name '*.sh' -exec {} \; > results.out
ソート順に実行したい場合は、次のように変更してください。
$ export DIR=/path/dir && cd $DIR && chmod -R +x *
find . -maxdepth 2 -type f -name '*.sh' | sort | bash > results.out
一例として、これは以下の順序で実行されます。
bash: 1: ./assets/main.sh
bash: 2: ./builder/clean.sh
bash: 3: ./builder/concept/compose.sh
bash: 4: ./builder/concept/market.sh
bash: 5: ./builder/concept/services.sh
bash: 6: ./builder/curl.sh
bash: 7: ./builder/identity.sh
bash: 8: ./concept/compose.sh
bash: 9: ./concept/market.sh
bash: 10: ./concept/services.sh
bash: 11: ./product/compose.sh
bash: 12: ./product/market.sh
bash: 13: ./product/services.sh
bash: 14: ./xferlog.sh
特定の条件で無限の深さで実行したい場合は、これを使用できます。
export DIR=/path/dir && cd $DIR && chmod -R +x *
find . -type f -name '*.sh' | sort | bash > results.out
次に、このように子ディレクトリの各ファイルの上に配置します。
#!/bin/bash
[[ "$(dirname `pwd`)" == $DIR ]] && echo "Executing `realpath $0`.." || return
そして親ファイルの本文のどこかに:
if <a condition is matched>
then
#execute child files
export DIR=`pwd`
fi