findを再帰的に呼び出す方法はありますか?
他のパターンと一致するディレクトリでのみ特定のパターンと一致するファイルを検索したいと思います。
オリジナル:
for each (sub)directory D matching "*.data"
do
for each file F in directory D (with subdirs) matching "*.important.txt"
do
run command foo on F
done
done
今最も内側の要件(Fでfooコマンドを実行する)これは非常に簡単です。
find . -type d -name "*.data" \
-exec find \{\} -type f -name "*.important.txt" \;
しかし、内部でコマンドを渡す方法が見つかりませんでしたfind
。たとえば、次のように印刷します。検索: '-exec'引数がありません内部ルックアップが呼び出されるたびに:
find . -type d -name "*.data" \
-exec find \{\} -type f -name "*.important.txt" \
-exec "foo \{\} \;" \;
すべてのソリューションはposix仕様(スクリプトで実行可能)でなければなりません/bin/sh
。私は解決策を探していません。
- 内部ルックアップを別のシェルスクリプトにラップする
- 内部ルックアップをbash関数でラップする
答え1
find
結果を独自に実行するには、(or)-c
引数を使用して、外部findコマンドが内部結果を特別に処理しないようにすることができます。ただし、外部照会の結果を、以下を使用して拡張可能なパラメーターとして渡す必要があります。sh
bash
{}
sh
$0
find . -type d -name "*.data" \
-exec sh -c 'find "$0" -type f -name "*.important.txt" -exec echo \{\} \;' \{\} \;
注:スペースを含むディレクトリ名の問題を回避するには、$0
引用符("$0"
)を使用する必要があります。
find -exec
これは、毛深いエスケープなしでランダムに深いネストを許可しないため、実際には「再帰的」ソリューションではありませんが、例で要求された2つのレベルをサポートします。
あなたの例が実際の問題に似ている場合は、パラメータを使用して次のものを見つける-path
こともできます。
find . -path '*.data/*important.txt'
答え2
bashバージョン(POSIXと互換性がありません)
#!/bin/bash
find . -type d -name 'a *' -print0 \
| while IFS= read -r -d '' dir ; do
find "$dir" -type f -name "*.c" -exec echo \{\} \;
done
質問:
(悪い)shバージョン(POSIX互換)
#!/bin/sh
# WARNING: while this will work on directory names with spaces,
# it will break on directory names that contain newlines
find . -type d -name 'a *' -print \
| while IFS= read -r dir ; do
find "$dir" -type f -name "*.c" -exec echo \{\} \;
done
問題:コメントで述べたように、改行文字を含むディレクトリ名で中断されます。これは実際の問題だとは思わないかもしれませんが、スクリプトに不要な脆弱性を追加します。たぶん他のプログラムにそのようなディレクトリなどを生成するバグがあるかもしれません。
(より良い)shバージョン
バラよりイアン・ロバートソンの答え
ところで。 POSIX と互換性のあるスクリプトが必要な場合は、次を使用します。shellcheck
。たとえば、read -d
POSIXには定義がないことがわかります。