web-dev-qa-db-ja.com

$()のコマンドによって出力された{1,2}が補間されないのはなぜですか?

2つのテキストファイルがあるディレクトリにいます。

_$ touch test1.txt
$ touch test2.txt
_

いくつかのパターンを使用してファイルを(Bashで)リストしようとすると、機能します。

_$ ls test?.txt
test1.txt  test2.txt
$ ls test{1,2}.txt
test1.txt  test2.txt
_

ただし、パターンが$()で囲まれたコマンドによって生成される場合、パターンの1つだけが機能します。

_$ ls $(echo 'test?.txt')
test1.txt  test2.txt
$ ls $(echo 'test{1,2}.txt')
ls: cannot access test{1,2}.txt: No such file or directory
_

何が起きてる? _{1,2}_というパターンが機能しないのはなぜですか?

それは2つのことの組み合わせです。まず、中かっこ展開はファイル名と一致するパターンではありません。それは純粋にテキストによる置換です。参照 `a [bc] d`(大括弧)と` a {b、c} d`の違いは何ですか(中括弧)? 。第2に、二重引用符(ls $(…))の外側でコマンド置換の結果を使用すると、パターンマッチング(およびWord分割:「split + glob」演算子)のみが発生し、完全な再解析は行われません。 。

ls $(echo 'test?.txt')を使用すると、echo 'test?.txt'コマンドは文字列test?.txtを出力します(最後の改行付き)。コマンド置換の結果、文字列test?.txtになります(コマンド置換は末尾の改行を削除するため、最後の改行なし)。この引用符で囲まれていない置換はWord分割され、空白文字(正確にはtest?.txt内の文字)がないため、単一の文字列$IFSで構成されるリストが生成されます。次に、この1要素リストの各要素は条件付きのワイルドカード展開を受けます。文字列にワイルドカード文字?があるため、ワイルドカード展開が行われます。パターンtest?.txtは少なくとも1つのファイル名に一致するため、リスト要素test?.txtは、パターンに一致するファイル名のリストに置き換えられ、test1.txtおよびtest2.txtを含む2要素のリストが生成されます。最後に、lsが2つの引数test1test2で呼び出されます。

ls $(echo 'test{1,2}')を使用すると、echo 'test{1,2}'コマンドは文字列test{1,2}を出力します(最後の改行付き)。コマンド置換の結果、文字列test{1,2}になります。この引用符で囲まれていない置換はWord分割を受け、単一の文字列test{1,2}で構成されるリストが生成されます。次に、この1要素リストの各要素は、条件付きのワイルドカード展開を受けます。文字列にはワイルドカード文字がないため、何も行われません(要素はそのままです)。したがって、lsは単一の引数test{1,2}で呼び出されます。

比較のために、以下にls $(echo test{1,2})で何が起こるかを示します。コマンドecho test{1,2}は、文字列test1 test2を出力します(最後の改行付き)。コマンド置換の結果、文字列test1 test2(最後の改行なし)になります。この引用符で囲まれていない置換はWord分割を受け、2つの文字列test1test2が生成されます。次に、どちらの文字列にもワイルドカード文字が含まれていないため、文字列はそのまま残されるため、lsは、test1test2の2つの引数で呼び出されます。

展開の順序は次のとおりです。チルダ展開、パラメーターおよび変数展開、算術展開、およびコマンド置換(左から右に実行)。単語分割;およびファイル名の拡張。

コマンドの置換後、中括弧の展開は行われません。 evalを使用して、次のラウンドの拡張を強制できます。

eval echo $(echo '{1,2}lala')

結果は次のとおりです。

1lala 2lala
10
dedowsdi

その問題はbashに固有のものであり、bashで中括弧の展開をファイル名の展開(グロビング)から分離し、他のすべての展開の前に最初に実行することに決めたためです。

bashマンページから:

展開の順序は次のとおりです。チルダ展開、パラメーターと変数の展開、算術展開、およびコマンド置換(左から右に実行)。単語分割;およびパス名の展開。

あなたの例では、bashは、それが遅すぎる場合に、コマンド置換($(echo ...))を実行した後にのみ中括弧を表示します。

これは、他のすべてのシェルとは異なり、パス名の展開(グロビング)の直前(および一部として)にブレースを展開します。これには、ブレース展開が最初に発明されたcshが含まれますが、これに限定されません。

_$ csh -c 'ls `echo "test{1,2}.txt"`'
test1.txt test2.txt
$ ksh -c 'ls $(echo "test{1,2}.txt")'
test1.txt  test2.txt

$ var=nope var1=one var2=two bash -c 'echo $var{1,2}'
one two
$ var=nope var1=one var2=two csh -c 'echo $var{1,2}'
nope1 nope2
_

後者の例は、cshzsh、_ksh93_、mkshまたはfishでも同じです。

また、ブレース展開globbingの一部としては、glob(3)ライブラリ関数(少なくともLinuxおよびすべてのBSDの場合)、および他の独立した実装(たとえば、 Perl:_Perl -le 'print join " ", <test{1,2}.txt>'_)。

bashでこれが異なる方法で行われた理由はおそらくその裏に物語がありますが、FWIW論理的な説明を見つけることができず、すべての事後的な合理化は納得できませんでした。

6
mosvy

してみてください:::

ls $(echo test {1,2} \。txt)

バックスラッシュ付き。現在は機能しています。以前のポスターが言ったように、引用符も削除します。ドットは一致するパターン用ではありませんが、ここでは文字通りピリオドと見なされます。

1
mkzia

引用符を削除すると機能します

$ ls $(echo test{1,2})
test1  test2
0
darxmurf