フォルダ内のすべてのサブディレクトリを再帰的に参照する必要があります。サブディレクトリに拡張子が ".xyz"のファイルがある場合は、そのフォルダで特定のコマンドを一度実行する必要があります。
これが私が今まで持っているものです
recursive() {
for d in *; do
if [ -d "$d" ]; then
(cd -- "$d" && recursive)
fi
dir=`pwd`
pattern="*.xyz"
file_count=$(find $dir -name $pattern | wc -l)
if [[ $file_count -gt 0 ]]; then
echo "Match found. Going to execute a command"
#execute command
fi
done
}
(cd /target; recursive)
ところで、問題は、一致するものがあるときに「一致項目が見つかりました」というメッセージが各フォルダに複数回表示されることです。この問題を解決しながら、より簡単な方法はありますか?
答え1
あなたは再創造していますfind
。
次のようにしてみてください(GNUfindutils
とGNUを使用sort
)。
find /target -iname '*.xyz' -printf '%h\000' | sort -z -u |
xargs -0 -r -I {} sh -c "cd {} ; yourcommandhere"
「*.xyz」ファイルのあるディレクトリ名()を-printf
NULバイト()で区切って印刷します。重複を削除し、各ディレクトリに移動して実行します。%h
\000
sort
xargs
cd
yourcommandhere
xargs を使用して実行するスクリプトを作成することもできます。例えば
find /target -iname '*.xyz' -printf '%h\000' | sort -z -u |
xargs -0 -r /path/to/myscript.sh
単純なmyscript.shの例:
#!/bin/sh
for d in "$@" ; do
cd "$d"
echo "Match found in $d. Going to execute command"
# execute command
done
一致するディレクトリが多い場合、2番目のバージョンははるかに高速です。つまり、ディレクトリごとにシェルをフォークするのではなく、シェルを一度だけフォークしてから、各引数に対して繰り返します。
ところで、ここではどちらも実際にはprintf
必要ありません。しかし、何が起こっているのかを読んで理解しやすくなります。同様に重要なのは、(printfとsortを使用して)最初に重複するエントリを削除することでbashを使用するよりもはるかに速く実行され、特定のディレクトリでコマンドを複数回実行する危険(やや小さい)を排除することです。sort
xargs
ソートや xargs なしで同じ操作を実行する別の方法は次のとおりです。
find /target -iname '*.xyz' -exec bash -c \
'typeset -A seen
for f in "$@"; do
d="$(dirname "$f")";
if [[ ! -v $seen[$d] ]]; then
echo "Match found in $d. Going to execute command"
# Execute command
seen["$d"]=1
fi
done' {} +
これはbash()の連想配列を使用して、$seen[]
どのディレクトリが検索され処理されたかを追跡します。何千もの一致するファイルがある場合*.xml
(bashスクリプトが複数回フォークできるように最大コマンドライン長を超えるのに十分です)、次のコマンドを実行します。可能特定のディレクトリで複数回実行します。
findオプションで実行されるスクリプトは、上記-exec
のxargsバージョンなどのスタンドアロンスクリプトです。
しかし、ここのすべてのバリアントは、awkやperl、shやbashスクリプトではなく、すべてのスクリプトを簡単に実行できます。
答え2
find
文字列を印刷する組み込みフラグがあります。これは非常に便利です。
find -iname "*.xyz" -printf "%h\n"
パターンに一致するファイルを含むすべてのディレクトリの名前を印刷します(の魔法の構文は%h
もちろん、改行文字を使用してfind
ファイルディレクトリに展開されます\n
)。
だからこれがあなたが望むものです:
COMMAND='echo'
find `pwd` -iname "*.pdf" -printf "%h\n" | sort -u | while read i; do
cd "$i" && pwd && $COMMAND
done
ここで何が起こっています。コマンドを一度だけ実行するには、重複したエントリを削除するsort
フラグを使用してパイプします。-u
その後、すべてを繰り返しますwhile
。また、相対パスではなく絶対パスを出力するfind `pwd`
ために良いトリックインを使用したことに注意してください。これにより、相対パスを気にすることなくfind
使用できます。cd
編集:このスクリプトを実行するときは、ディレクトリ名に注意してください。改行(\n
)を含むディレクトリ名\
はスクリプトを破る可能性があります(そしてあまり一般的ではないかもしれませんが、まだテストされていません)。この問題を解決するのは難しく、どうすればいいかわからないので、そのようなディレクトリを使用しないようにアドバイスするしかありません。