私はgrepがファイル内の一致するものを見つけるために各フォルダからコマンドを実行するエレガントな方法を探しています。find
フラグは、-execdir
コンテンツではなくフォルダ/ファイル名のみを検索することを除いて、findと非常によく似ています。
現在私は次のようなものを使用しています。
grep -r "pattern" --include=\*.out -l | xargs -L 1 bash -c 'cd `dirname "$0"` && some_script.sh'
より直接的な方法はありますか?それはまるでgrep ... -execdir
?
答え1
find
実行することができますgrep
:
find . -type f -name '*.out' \
-exec grep -q -e 'pattern' {} \; \
-execdir somescript.sh \;
見つかったパス名ごとに、パターンがファイルの行と一致することを確認するために.out
使用されます。grep
その場合は、見つかったファイルのディレクトリを作業ディレクトリとして使用して実行-execdir
します。somescript.sh
somescript.sh
これが機能するには、どこかで利用できる必要があり、スクリプトは一致するファイルを含む各ディレクトリ$PATH
に対して1回ではなく、見つかった各ファイルに対して1回実行されます.out
。
.out
一致するファイルを含む各ディレクトリでスクリプトを一度だけ実行します。
find . -type d -exec sh -c '
for dirpath do
if grep -q -e "pattern" "$dirpath"/*.out 2>/dev/null; then
( cd "$dirpath" && exec somescript.sh )
fi
done' sh {} +
find
ファイルではなくディレクトリを検索するために使用されます。バッチディレクトリが見つかると、短いインラインシェルスクリプトが実行されます。シェルスクリプトは、各ディレクトリ内のすべてのファイル(隠しディレクトリを探している間に隠しファイルを除く.out
)に対してパターンを一致させようとします。一致するファイルがある場合は、作業ディレクトリが変更されるサブシェルを起動してスクリプトを実行します。find
somescript.sh
答え2
一致するファイルが 1 つ以上含まれる各ディレクトリに対してout
一度だけスクリプトを実行し、grep
各ファイルに対して 1 つずつ実行したくない場合は、次のようにします。
P="pattern" find . -name '*.out' -type f -exec gawk '
BEGINFILE {
dir = FILENAME; sub("/[^/]*$", "", dir)
if (dir in found) nextfile
}
$0 ~ ENVIRON["P"] {
printf "%s\0", dir
found[dir]
nextfile
}' {} + | xargs -r0 sh -c '
for dir do
(cd "$dir" && exec somescript.sh)
done' sh
これはできるだけ少ないプロセスを実行し、gawk
できるだけ少ないファイルを読み取ります(各ファイルの内容はできるだけ少なくなります)。
GNUxargs
または互換ファイル名には、現在のロケールで有効な文字を形成しない一連のバイトが含まれていないとします。
GNUgrep
呼び出し(およびGNU 、、、dirname
)の使用:sort
xargs
grep -rlZ --include='*.out' pattern . |
xargs -r0 dirname -z |
sort -zu |
xargs -r0 sh -c '
for dir do
(cd "$dir" && exec somescript.sh)
done' sh
out
以前の解決策とは異なり、この方法では、一致するファイルが見つかったディレクトリ内のファイルを含むすべてのファイルを検索します。 GNU はgrep
grep よりも効率的であるため、どれだけの過剰なgawk
データが greping されるかに応じて、依然として効率的である可能性があります。