二重引用符で囲まれた引数(ファイル名を表示)は、スペースが含まれている場合は役に立ちません。

二重引用符で囲まれた引数(ファイル名を表示)は、スペースが含まれている場合は役に立ちません。

二重引用符で囲まれた引数は、特定のファイル名を繰り返すときにファイル名が破損するのを防ぎません。

$ mkdir temp
$ cd temp/
$ touch a\ b
$ for f in $(find .) ; do echo "$f" ; done
.
./a
b

答え1

このディレクティブは、for f in $(find .)要素のリストを空白に基づいて繰り返し分割します。 zshのような最新のシェルでは、拡張フラグとして使用できます。

for f in "${(@f)$(find .)}" ; do echo "$f" ; done

滞在すればbash、この問題を解決できます。他の質問で述べたようにfind結果を変数に割り当てながら、入力区切り文字(要素を区切るために使用される文字)を一時的に変更します。一時的にゴービングを無効にすると、その*文字のようなワイルドカード文字の拡張が防止されます。

set -f  # Disable globbing.
IFS=$(echo -e '\0') files=( $(find .  -print0) )  # Read newline separated files into an array.
for f in ${files[@]}; do echo $f; done
set +f  # Reenable globbing.

ただし、これは問題を変更しただけで、ファイル名に改行文字が含まれている場合は機能しません。おそらく、すべてのファイルシステムで禁止されているものの1つはNULL文字です。ただし、bashの変数はそれを保持しない可能性があるため、に割り当てることはできませんIFS

findまたは、xargsを使用してパスを別のプログラムに渡すこともできます。 null文字を区切り文字として使用するto-print0コマンドを使用しfind-0toに切り替えます。一度に1つのファイル名を処理するために呼び出しにxargs追加されました。-n1xargs

find . -print0 | xargs -0 -n1 echo

答え2

引数の変数置換は二重引用符で囲まれていましたechoが、ループ反復リストのコマンド置換は二重引用符で囲まれていませんfor。ここではスペースやその他の不快な問題が発生します。

find改行文字がファイル名の一部か区切り文字の一部であるかわからないため、出力を解析するための完全に信頼できる方法はありません。

findファイル名に改行文字が含まれていないと仮定できる場合は、フィールド区切り文字を改行文字\[?*のみに制限し(スペースとタブ保護のため)、ワイルドカードをオフにして(保護のために)出力を分割するように配置できます。

IFS='
'; set +f
for f in $(find .); do
  unset IFS; set +f
  echo "$f"
done
unset IFS; set +f

しかし、これを行うより良い方法があります。確実に使用する最も簡単な方法findは、出力を解析する代わりにコマンドを実行させることです。メタデータがまだキャッシュにある間にファイルが処理される可能性が高いため、速度が速くなる可能性があります。

find . -exec echo {} \;

シェルコマンドが必要な場合は、シェルを明示的に呼び出します。引用に注意してください。ファイル名をシェルに引数として渡し、シェルコマンドに挿入しようとしないでください。

find . -exec sh -c 'echo "$0"' {} \;

各ファイルに対してシェルインスタンスを呼び出す代わりに、シェル呼び出しをグループ化して複数のファイルを繰り返すことができます。このfindコマンドは集計を実行し、コマンドラインの長さ制限を超えないようにします。$0ファイル$1などのダミー引数を渡すことを忘れないでください。

find . -exec sh -c 'for x; do echo "$x"; done' _ {} +

ksh93、bash、またはzshを使用している場合は、再帰ワイルドカード機能を使用できます。つまり、**/PATTERNディレクトリとそのサブディレクトリからワイルドカードパターンを再帰的に検索します。 ksh93では最初に実行する必要がありますset -o globstar。 Bashではshopt -s globstarこれを最初に実行する必要があり、**ディレクトリのシンボリックリンク内でも繰り返されることに注意してください。

for f in **/*; do
  echo "$f"
done

Zshもありますグローバル予選ほとんどの用途に適用されますfind。他の殻にはこのようなものはありません。

関連情報