コマンドの結果を変更するときは、「パスは式の前になければなりません」を探します。

コマンドの結果を変更するときは、「パスは式の前になければなりません」を探します。

findファイルからディレクトリリストを除外するために実行しようとしています。このファイルは他のコマンドでも使用されるため、ファイル形式に固執しました。つまり、各行には「as is」というディレクトリ名とスラッシュが含まれています。

My Files/

xargsのようなさまざまなアプローチを試しましたが、うまく機能しませんでしたが、ついにブラックリストを前処理して次のパラメータを生成することにしました。

find . -type f $(cat .blacklist.txt | while read -r line; do printf "! -path './%s*' -prune " "$line"; done)

ただし、ブラックリストでスペースを含むディレクトリ名が見つかると、次のエラーが発生します。

find: paths must precede expression: `<Last Part Of Name>/*''

何が起こっているのかを調べようとしましたがset -x、ディレクトリ名の一部が個別に参照されているようです。たとえば、リストに「My Files」というディレクトリ名があるとします。set -xファイルに次の特定の行の出力を表示できます。

++ printf '! -path '\''./%s*'\'' -prune ' 'My Files/'

これまではとても良いと思います。ただし、最後に組み立てられたコマンドで出力を見ると、次のようになります。

'!' -path ''\''./My' 'Files/*'\''' -prune 

問題は明らかです。ディレクトリ名を二つの部分に分けようと頑張っているということです!しかし、なぜこのようなことをするのか理解できません。私は次のようないくつかのバリエーションを試しました。

printf "! -path './%s*' -prune " "$line"

私はどういうわけかprintfから出てくるときにパスの結果出力が引用されるようにしなければならないと思います。しかし、私はそれらをすべて試しましたが、そのうちの何も動作しません。

printf "! -path \"./%s*\" -prune " "$line"
printf "! -path "'./$line*'" -prune "
printf "! -path '"./$line*"' -prune "

ただし、どちらもディレクトリ名の個々の単語が分割されるのを防ぎません。

私もこれを試しました:

printf "! -path ./%s* -prune " "$line"

これにより、ディレクトリ名が分割されるのを防ぐことができますが、参照されなくなり、そのパスの下のすべてのサブディレクトリに展開されます。これは間違っており、今では必要なサブディレクトリが1つしかないため、コマンドも中断されます。

%s別のパラメータを使用してパラメータprintf%s直接使用しても問題にならないようです。$line

ベストアンサー1

そして、ディレクトリ名の一部が個別に参照されるようです。

私はそう言わないでしょう。! -path './My Files*' -pruneたとえば、コマンドオーバーライドで印刷すると、トークン化ステップで引用符などの構文を処理せずに空白文字(または既に知っているすべての設定)のみが表示されるため、、、、!-pathトークン'./My化されます。これはglobの役割もしますが、一重引用符で終わるファイル名がない可能性があります。Files*'-pruneIFSFiles*'

出力に表示される引用符は、set -xBashが出力をシェルへの入力として表示できるようにするために存在します。エスケープされた引用は、で印刷された引用ですprintf

!ここでは、 、を-pathの他のパラメータとして生成する必要があります。 1つの方法は、パラメータを配列として収集することです。./My Files*-prunefind

#!/bin/bash
args=()
filename='./My Files'
args+=( ! -path "$filename*" -prune )
# etc.
find . -type f "${args[@]}"

しかし、本当に必要かもしれません。

find . -type f ! -path './somedir/*' ! -path './otherdir/*'

(いいえ-prune、リストされているディレクトリを含むツリー全体をナビゲートし、その中のすべてのエントリを無視します。)またはStéphane Chazelasが答えに示すように

find . \( -path ./somedir -o -path ./otherdir \) -prune -o -type f -print

(リストされたディレクトリに入ることもできません。()シェルをエスケープする必要があります。)

そして除外された名前にパターン一致findに一意の文字(*?[)を含めることができる場合は、バックスラッシュとバックスラッシュを使用してこれらの文字をエスケープする必要があります。

古いパターンを使用してエスケープを無視する場合:

#!/bin/bash
args=(-type f)
while read -r filename; do
    args+=( ! -path "./$filename/*" )
done < excluded.txt
find . "${args[@]}"

より良いアプローチは、次のように少し複雑です。

#!/bin/bash
args=()
first=1
while read -r escaped; do
    if [ "$first" != 1 ]; then
        args+=( -o )
    fi
    args+=( -path "./$escaped" )
    first=0
done < <(sed -e 's/[[?*\]/\\&/g' < excluded.txt)
find . \( "${args[@]}" \) -prune -o -type f -print

(ここでもプロセス置換(<(cmd...))を使用してエスケープのためにsedを介してファイル名のリストをパイプします)

配列を作成する際の重要な点は、引用符を追加せずにコマンドに入力したのと同じように、パラメータを割り当てに入力することです。その後、配列を扱うときは構文に集中し、引用符に"${args[@]}"注意する必要があります。

おすすめ記事