私はSolaris 10を使用しており、ksh(88)、bash(3.00)、およびzsh(4.2.1)を使用して以下をテストしました。
次のコードは結果を生成しません:
function foo {
echo "Hello World"
}
find somedir -exec foo \;
検索はいくつかのファイルと一致し(-exec ...
を-print
で置き換えることで示されているように)、関数はfind
呼び出しから外部で呼び出されたときに完全に機能します。
man find
ページの-exec
についてのコメントは次のとおりです:
-exec command実行されたコマンドが終了ステータスとして のゼロ値を返す場合は真。 コマンドの終わりは、エスケープされた セミコロン(;)で区切る必要があります。コマンド引数{}は で現在のパス名に置き換えられます。 -execの の最後の引数が{}であり、 にセミコロン(;)ではなく+を指定すると、 コマンドは、 {}はパス名のグループに置き換えられました。 コマンドの呼び出しで終了ステータスとして ゼロ以外の値が返される場合、find はゼロ以外の終了ステータスを返します。
私はおそらくこのようなことをして逃げることができました:
for f in $(find somedir); do
foo
done
しかし、フィールドセパレータの問題に対処することを恐れています。
find ... -exec ...
呼び出しからシェル関数(同じスクリプトで定義され、スコープの問題に悩まないでください)を呼び出すことはできますか?
/usr/bin/find
と/bin/find
の両方で試したところ、同じ結果が得られました。
function
はシェルに対してローカルであるため、シェルを生成してその関数をそのシェルで定義してから使用できるようにするには、find -exec
が必要です。何かのようなもの:
find ... -exec ksh -c '
function foo {
echo blan: "$@"
}
foo "$@"' ksh {} +
bash
を使用すると、export -f
を使用して環境を介して関数をエクスポートできるため、(bashで)以下を実行できます。
foo() { ...; }
export -f foo
find ... -exec bash -c 'foo "$@"' bash {} +
ksh88
には、関数をエクスポートするためのtypeset -fx
があります(環境経由ではありません)が、ksh
によって実行されるシバンより少ないスクリプトでのみ使用できるため、ksh -c
では使用できません。
別のオプションは以下を実行することです:
find ... -exec ksh -c "
$(typeset -f foo)"'
foo "$@"' ksh {} +
つまり、typeset -f
を使用して、インラインスクリプト内のfoo
関数の定義をダンプします。 foo
が他の関数を使用する場合、それらもダンプする必要があることに注意してください。
これは常に当てはまるわけではありませんが、当てはまる場合、それは簡単な解決策です。 globstar
オプションを設定します(ksh93では_set -o globstar
_、bash≥4では_shopt -s globstar
_。zshではデフォルトでオンになっています)。次に、_**/
_を使用して、現在のディレクトリとそのサブディレクトリを再帰的に照合します。
たとえば、_find . -name '*.txt' -exec somecommand {} \;
_の代わりに、次を実行できます。
_for x in **/*.txt; do somecommand "$x"; done
_
_find . -type d -exec somecommand {} \;
_の代わりに、
_for d in **/*/; do somecommand "$d"; done
_
_find . -newer somefile -exec somecommand {} \;
_の代わりに、
_for x in **/*; do
[[ $x -nt somefile ]] || continue
somecommand "$x"
done
_
_**/
_が機能しない場合(シェルにシェルが存在しないため、またはシェルアナログがないfind
オプションが必要なため)、 _find -exec
_引数 の関数。
\ 0を区切り文字として使用し、次のように、生成されたコマンドから現在のプロセスにファイル名を読み取ります。
_foo() {
printf "Hello {%s}\n" "$1"
}
while read -d '' filename; do
foo "${filename}" </dev/null
done < <(find . -maxdepth 2 -type f -print0)
_
何が起きてる:
read -d ''
_は次の\ 0バイトまで読み取るので、ファイル名の奇妙な文字について心配する必要はありません。-print0
_は\ nの代わりに\ 0を使用して、生成された各ファイル名を終了します。cmd2 < <(cmd1)
は、cmd2がサブシェルではなくメインシェルで実行されることを除いて、_cmd1 | cmd2
_と同じです。/dev/null
_からリダイレクトされ、誤ってパイプから読み取られないようにします。$filename
_が引用されているため、シェルは空白を含むファイル名を分割しようとしません。現在、_read -d
_と<(...)
はzsh、bash、ksh 93uにありますが、以前のkshバージョンについてはよくわかりません。
スクリプトから生成された子プロセスに、事前定義されたシェル関数を使用させる場合は、export -f <function>
でエクスポートする必要があります。
注:export -f
はbash固有です
ShellのみShell関数を実行できるため、
find / -exec /bin/bash -c 'function "$1"' bash {} \;
編集:基本的に、スクリプトは次のようになります。
#!/bin/bash
function foo() { do something; }
export -f foo
find somedir -exec /bin/bash -c 'foo "$0"' {} \;