"検索: パスは式の前になければなりません:" スクリプト内で実行するときにのみエラーが発生します。

"検索: パスは式の前になければなりません:" スクリプト内で実行するときにのみエラーが発生します。

コマンドラインから 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 ';' ))、その内容を制御できることを確認する必要があります。

答え2

FILTER=" | grep -v \"test\""

演算子が|拡張の結果である場合、特別な意味はありません。引用符も同じです。

フィルタを維持し、参照しやすい関数を作成します。

filter() {
    grep -v "test"
}

files=$(find . -type f -regex ".*$ext" | filter)

このようなコマンド置換を使用すると、find出力が単一の文字列に圧縮されるため、ファイル名にスペースが含まれていると問題が発生する可能性があります。


フィルタを変数部分に入れると、その理由は時々いいえフィルタがある場合は、関数を再定義して空のフィルタに置き換えることができます。

filter() {
    cat
}

または、2つの関数を作成し、変数を使用してどの関数を呼び出すかを決定します。

filter() {
    grep -v "test"
}
filter_null() {
    cat
}
filter_used=filter
files=$(find . -type f -regex ".*$ext" | $filter_used)

関連情報