find .
これらのコマンドで入力すること/home/user/*
の違いは何ですかfor
?たとえば、
for var in $(find .)
do echo "$var"
done
または
for var in /home/user/*
do
echo "$var"
done
最初の場合、for
コマンドは名前にスペースを含むファイルを爆発させます。 2番目のケースではそうではありません。なぜ?
答え1
これはシェルの標準動作です。作業順序は、コマンドの置換($(find .)
)、単語の分割、グローバル拡張(/home/user/*
)です。
~からPOSIX規格(単語分割 = フィールド分割、グローバル拡張 = パス名拡張):
単語拡張の順序は次のとおりです。
チルダ拡張(チルダ拡張を参照)、パラメータ拡張(パラメータ拡張を参照)、コマンド置換(コマンド置換を参照)、および算術拡張(算術拡張を参照)は、最初から最後まで実行する必要があります。トークン認識のトピック5を参照してください。
IFSが空でない場合は、手順1で作成したフィールド部分に対してフィールド分割を実行する必要があります(フィールド分割を参照)。
set -fが適用されない限り、パス名拡張を実行する必要があります(パス名拡張を参照)。
見積もりの削除(見積もりの削除を参照)は、常に最後に実行する必要があります。
したがって、単語分割がファイル名を妨げないように、可能であれば常にglobを使用することをお勧めします。構成$(find)
は実際には例です。バッシュトラップ #1。
答え2
シェルは順次操作を実行します。 $(find .)
これをコマンド置換と呼びます。コマンド置換の結果は次の影響を受けます。
- 噴射、
- パス名拡張
- 見積もりの削除
単語の区切りは、ファイル名にスペースが含まれると問題を引き起こす原因です。
/home/user/*
パス名拡張子です。これは上記のリストの最後から2番目のエントリです。見積もりの削除にのみ影響します。
答え3
2つの違いは、シェルglob(globsなど/home/user/*
)には通常、「隠しファイル」(ドットで始まるファイル名)が含まれていないことです。一方、find
特殊ディレクトリ「.」を除くすべてのファイル名は一致します。と「..」(現在のディレクトリと親ディレクトリ)。
答え4
これを説明する別の方法は、使用時にシェルが個々の要素が何である*
かを知ることができることです。出力はfind
単に長い文字列です。シェルは、要素(および対応する区切り文字)が何であるかを知りません。
引用符付き出力を生成するプログラムでコマンド置換を特別な方法で使用できる場合、問題は発生しません。しかし、例えばシェルが必要な引用符を認識するには、式eval
全体がより複雑になることがよくあります。
eval for var in $(ls --quoting-style=shell)\; do echo '"$var"'\; done