コマンドラインから find コマンドを実行すると正常に動作しますが、スクリプトで実行するとこのエラーが発生します。次のようにコマンドを実行します。
FILTER=" | grep -v \"test\""
files=`find . -type f -regex \".*$ext\" $FILTER`
もし私がするなら
echo "find . -type f -regex \".*$ext\" $FILTER"
それは出力する
find . -type f -regex ".*.cpp" | grep -v "test"
コマンドラインでうまく動作します。スクリプトでどのように機能させることができますか?また、*をエスケープしようとしましたが、同じエラーが発生しました。
私も気づいた
find . -type f -regex \".*$ext\"
シェルスクリプトで実行すると出力がありません。上記のようにコマンドラインで実行すると、.cppファイルのリストが表示されるので、なぜそうなのかわかりません。
ベストアンサー1
殻が届いたら「拡張」ステップ、制御演算子(たとえば|
)が識別されました。拡張結果は、制御構造を検索するときに再解析されません。
コマンドが次に置き換えられた場合
files=`find . -type f -regex \".*$ext\" $FILTER`
拡張後、Bashはそれを単純なコマンド(find
)で解析し、その後にいくつかの引数が続きます。そのうちの2つは拡張が必要です。トレースをオンにすると、実際の拡張コマンドを表示できます。
$ set -x
$ find . -type f -regex \".*$ext\" $FILTER
+ find . -type f -regex '".*cpp"' '|' grep -v '"test"'
と組み合わせると
$ set -x
$ find . -type f -regex ".*.cpp" | grep -v "test"
+ grep --color=auto -v test
+ find . -type f -regex '.*.cpp'
|
最初のケースでは、単一文字引数が使用されることが明らかになりますfind
。
コマンド文字列を動的に作成して実行するには、新しい解析ステップを明示的に追加する必要があります。eval
方法は次のとおりです。
$ set -x
$ files=$(eval "find . -type f -regex \".*$ext\" $FILTER")
++ eval 'find . -type f -regex ".*cpp" | grep -v "test"'
+++ find . -type f -regex '.*cpp'
+++ grep --color=auto -v test
ただし、変数をスクリプトとして実行するときは、確実なセキュリティ上の理由で変数の内容を制御できることを確認することが重要です。また、プログラムを読みやすくデバッグするのが難しくなる傾向があるため、eval
最後の手段としてのみ使用することをお勧めします。
あなたの場合、より良いアプローチは次のとおりです。
filter=( -regex ".*$ext" '!' -name "*test*" )
find . -type f "${filter[@]}" -exec bash -c '
# The part of your script that works with "files" goes here
# "$@" holds a batch of file names
' mybash {} +
find
の柔軟性を活用し、改行文字を含むファイル名を正しく処理します。一般的に言えば、これはBash(バージョン4.4以降)を使用し、サポートしていない限り、出力find
を変数に保存できないようにする極端なケースです。非標準実装)。これについてもっと読むことができます。mapfile -d '' files < <(find ... -print0)
find
-print0
検索結果を繰り返すのはなぜ悪い習慣ですか?find
、パイプの出力にも関連しています。
filter
配列の要素によって任意のコードが実行される可能性があるため(考えてみてくださいfilter=( -exec something_evil ';' )
)、その内容を制御できることを確認する必要があります。