
私はSolaris 10を使用しており、ksh(88)、bash(3.00)、およびzsh(4.2.1)を使用して以下をテストしました。
次のコードは結果を生成しません。
function foo {
echo "Hello World"
}
find somedir -exec foo \;
findは複数のファイルと一致し(代替として表示されます-exec ...
)、-print
関数はfind
外部から呼び出されると完全に機能します。
man find
このページの内容は次のとおりです-exec
。
-exec コマンド 実行されたコマンドが以下を返すと真です。 0 値は終了状態として使用されます。終わり コマンドはエスケープ文字で区切る必要があります。 セミコロン(;)。コマンドパラメータ{}は 現在のパス名に置き換えます。もし -execの最後の引数は{}です。 セミコロン(;)の代わりに+を指定してください。 コマンドはあまり頻繁に呼び出されません。 {} はパス名グループに置き換えられます。もし このコマンドを呼び出すと返されます。 終了状態でゼロ以外の値を探します。 ゼロ以外の終了状態を返します。
おそらく次のようにすることができます。
for f in $(find somedir); do
foo
done
しかし、フィールド区切り記号の問題に対処するのは怖いです。
呼び出しでシェル関数を呼び出すことは可能ですか(同じスクリプトで定義されているので、スコープの問題を心配する必要はありません)find ... -exec ...
。
私は両方とも試してみましたが、/usr/bin/find
同じ/bin/find
結果を得ました。
答え1
Afunction
はシェル固有の関数なので、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
ps -f
あるいは、コマンドラインではなく環境変数を介して関数定義を渡すこともできます(出力に表示されます)。
FUNCDEFS=$(typeset -f foo) find ... -exec ksh -c '
eval "$FUNCDEFS" &&
unset -v FUNCDEFS &&
foo "$@"' ksh {} +
(unset -v FUNCDEFS
この機能で実行されるコマンドの環境を汚染しないようにfoo
(存在する場合))
答え2
\ 0を区切り文字として使用し、生成されたコマンドのファイル名を次のように現在のプロセスに読み込みます。
foo() {
printf "Hello {%s}\n" "$1"
}
while IFS= read -d '' -r filename; do
foo "${filename}" </dev/null
done < <(find . -maxdepth 2 -type f -print0)
ここで何が起こっていますか?
read -d ''
次の \0 バイトまで読み取るので、ファイル名の奇妙な文字について心配する必要はありません。- 同様に
-print0
、生成された各ファイル名を終了するには、\ nの代わりに\ 0を使用します。 - オプション
-r
(raw)は、ファイル名からバックスラッシュがエスケープされるのを防ぎます。 cmd2 < <(cmd1)
cmd2と同じですcmd1 | cmd2
が、cmd2はサブシェルの代わりにプライマリシェルで実行されます。- fooへの呼び出しは、
/dev/null
誤ってパイプから読み込まれないようにリダイレクトされます。 $filename
引用されているため、シェルはスペースを含むファイル名を分割しようとしません。IFS=
Bashがファイル名から末尾のスペースを削除するのを防ぎます(ありがとうダニエル・スメドガードブースこの修正のために)
zsh、bash、ksh 93uread -d
では、<(...)
以前のバージョンのkshがわかりません。
答え3
事前定義されたシェル機能を使用するためにスクリプトによって生成されたサブプロセスが必要な場合は、それをエクスポートする必要があります。export -f <function>
注:export -f
bashのみ
ただ一つだけだからシェル実行できますシェル機能:
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"' {} \;
答え4
常に適用されるわけではありませんが、適用される場合は簡単な解決策です。このglobstar
オプションを設定します(set -o globstar
ksh93ではshopt -s globstar
bash ≥4、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
パラメータで関数を定義する。