回答:
シェルだけがシェル関数の実行方法を知っているため、関数を実行するにはシェルを実行する必要があります。また、エクスポートする関数をでマークする必要がありますexport -f
。そうしないと、サブシェルがそれらを継承しません。
export -f dosomething
find . -exec bash -c 'dosomething "$0"' {} \;
find . -exec bash -c 'dosomething "$0"' {} \;
する場合、ファイル名のスペース(およびその他の奇妙な文字)を処理します...
export -f
bashの一部のバージョンでのみ動作します。それはcrossplatfornではなく、POSIXではないですが、/bin/sh
それとの誤差があります
find . | while read file; do dosomething "$file"; done
while read
、forループを変更して問題を修正しました。for item in $(find . ); do some_function "${item}"; done
上記のJakの答えはすばらしいですが、簡単に克服できるいくつかの落とし穴があります。
find . -print0 | while IFS= read -r -d '' file; do dosomething "$file"; done
これは、改行ではなくnullを区切り文字として使用するため、改行付きのファイル名が機能します。また-r
、バックスラッシュのエスケープを無効にするフラグを使用します。ファイル名にバックスラッシュがないと機能しません。またIFS
、名前の末尾にある可能性のある空白が破棄されないようにクリアされます。
/bin/bash
いますが、では機能しません/bin/sh
。残念です。
{}
以下に示すように引用符を追加します。
export -f dosomething
find . -exec bash -c 'dosomething "{}"' \;
これによりfind
、たとえば名前に括弧が含まれるファイルなど、によって返される特殊文字によるエラーが修正されます。
{}
。これは、二重引用符を含むファイル名で壊れます。touch '"; rm -rf .; echo "I deleted all you files, haha
。おっとっと。
-exec bash -c 'echo $0' '{}' \;
を使用する場合bash -c
、$ 0はスクリプト名ではなく最初の引数です。
効率を上げるために、xargs
結果を大量に処理するために多くの人々が使用しますが、それは非常に危険です。そのため、find
結果を一括で実行する代替メソッドが導入されました。
この方法はPOSIX-における要件例えばのようないくつかの警告と来るかもしれないことしかし注意find
持っている{}
コマンドの最後に。
export -f dosomething
find . -exec bash -c 'for f; do dosomething "$f"; done' _ {} +
find
は、多くの結果を引数としての単一の呼び出しに渡しbash
、for
-loopはそれらの引数を反復処理してdosomething
、それぞれの関数を実行します。
上記のソリューションは引数をで開始します。その$1
ため、_
(を表す$0
)があります。
同様に、受け入れられたトップアンサーを修正して、
export -f dosomething
find . -exec bash -c 'dosomething "$1"' _ {} \;
引数は常にで始まる必要があるため、これはより健全なだけでなく、によって返されるファイル名がシェルにとって特別な意味を持つ場合、$1
使用$0
すると予期しない動作が発生する可能性find
があります。
見つかった各項目を引数として渡して、スクリプト自体を呼び出します。
#!/bin/bash
if [ ! $1 == "" ] ; then
echo "doing something with $1"
exit 0
fi
find . -exec $0 {} \;
exit 0
スクリプトを単独で実行すると、探しているものが見つかり、検索結果を引数として渡して自分自身を呼び出します。スクリプトを引数付きで実行すると、その引数に対してコマンドが実行されてから終了します。
find: ‘myscript.sh’: No such file or directory
として開始した場合、これは失敗することは言うまでもありませんbash myscript.sh
現在のディレクトリ内のすべてのファイルに対して特定のコマンドを実行するbash関数を探している方のために、上記の回答から1つをコンパイルしました。
toall(){
find . -type f | while read file; do "$1" "$file"; done
}
スペースを含むファイル名で壊れることに注意してください(以下を参照)。
例として、次の関数を見てください。
world(){
sed -i 's_hello_world_g' "$1"
}
現在のディレクトリのすべてのファイルで、helloのすべてのインスタンスをworldに変更したいとします。私はします:
toall world
ファイル名のシンボルを安全にするには、次を使用します。
toall(){
find . -type f -print0 | while IFS= read -r -d '' file; do "$1" "$file"; done
}
(ただし、GNUなどfind
を処理するが必要です)。-print0
find
その方法で関数を実行することはできません。
これを克服するには、関数をシェルスクリプトに配置して、 find
# dosomething.sh
dosomething () {
echo "doing something with $1"
}
dosomething $1
次に、次のように検索で使用します。
find . -exec dosomething.sh {} \;
dosomething $1
=> dosomething "$1"
でファイルを正しく開始してくださいfind . -exec bash dosomething.sh {} \;
exec
またはexecdir
(-exec command {} +
)の一括オプションを使用していて、すべての位置引数を取得したい場合、他のいくつかの回答に追加と説明を提供するには、$0
with の処理を考慮する必要がありますbash -c
。より具体的には、bash -c
上記のように使用し、見つかった各ディレクトリから '.wav'で終わるファイルパスをエコー出力する以下のコマンドを検討してください。
find "$1" -name '*.wav' -execdir bash -c 'echo $@' _ {} +
bashマニュアルにはこう書かれています:
If the -c option is present, then commands are read from the first non-option argument command_string. If there are arguments after the command_string, they are assigned to the
positional parameters, starting with $0.
ここで、'check $@'
はコマンド文字列であり_ {}
、コマンド文字列の後の引数です。$@
はbashの特別な定位置パラメーターであり、1から始まるすべての定位置パラメーターに展開されることに注意してください。また、この-c
オプションを使用すると、最初の引数が位置パラメータに割り当てられることに注意してください$0
。つまり、を使用してすべての位置パラメータにアクセスしようとすると$@
、最初から上方向のパラメータのみが取得されます$1
。これが、ドミニクの回答に、つまり_
パラメーターを埋めるための仮引数であるがあるため、たとえばパラメーター拡張やその回答のforループ$0
を使用すると、必要なすべての引数を後で使用できるようになり$@
ます。
もちろん、受け入れられた回答と同様に、bash -c 'shell_function $0 $@'
明示的にを渡すことによっても機能し$0
ますが$@
、期待どおりに機能しないことを覚えておく必要があります。
直接ではありません。Findはシェルではなく、別のプロセスで実行されています。
関数と同じ働きをするシェルスクリプトを作成し、それを見つけることができ-exec
ます。
$0
。