$()のコマンドで{1,2}が印刷されないのはなぜですか?

$()のコマンドで{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

ただし、次のコマンドでパターンが生成される場合$()

$ 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}機能しないのはなぜですか?

答え1

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

を使用すると、ls $(echo 'test?.txt')このコマンドはecho 'test?.txt'文字列test?.txt(末尾に改行を含む)を出力します。コマンド置換は文字列を生成しますtest?.txt(コマンド置換は末尾の改行を削除するため、最後の改行なし)。引用符のないこの置換はトークン化を行い、空白文字がないので単一の文字列リストを生成しますtest?.txt(より正確には文字はありません)。単一要素リストの各要素は条件付きワイルドカード拡張を受け、$IFS文字列にワイルドカードが含まれているため、ワイルドカード拡張が発生します。?パターンはtest?.txt1つ以上のファイル名と一致するため、リスト要素はパターンと一致するファイル名のリストに置き換えられ、およびを含む2つの要素のリストがtest?.txt生成されます。最後に、2つのパラメータとを使用して呼び出しが行われます。test1.txttest2.txtlstest1test2

を使用すると、ls $(echo 'test{1,2}')このコマンドはecho 'test{1,2}'文字列test{1,2}(末尾に改行を含む)を出力します。コマンド置換の結果は文字列ですtest{1,2}。引用符のないこの置換はトークン化を実行して個々の文字列のリストを生成しますtest{1,2}。単一要素リストの各要素は条件付きワイルドカード拡張を受けますが、文字列にワイルドカードがないため、何もしません(要素はそのままです)。ls単一の引数で呼び出されますtest{1,2}

比較のために、次のことが起こりますls $(echo test{1,2})。このコマンドはecho test{1,2}文字列test1 test2(末尾に改行を含む)を出力します。コマンド置換の結果は文字列ですtest1 test2(最後の改行文字はありません)。引用符のないこの置換は単語分割を実行して2つの文字列のtest1合計を生成しますtest2。その後、両方の文字列にワイルドカードが含まれていないため、単独で残すため、両方のls引数test1とを使用して呼び出しが行われますtest2

答え2

拡張順序は、中かっこ拡張、引数と変数拡張、算術拡張、コマンド置換(左から右へ)です。

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

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

結果は次のとおりです。

1lala 2lala

答え3

この質問は、ファイル名拡張子(ワイルドカード)から中括弧拡張子を分離し、他のすべての拡張子の前に実行することを決定した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後者の例は、またはの例と同じですksh93mkshfish

また、支柱の拡張に注意してください。ワイルドカードの一部としてglob(3)また、ライブラリ機能(少なくともLinuxおよびすべてのBSDでは)や他の独立した実装(perl:など)を介して使用できますperl -le 'print join " ", <test{1,2}.txt>'

なぜこれが異なって行われたのか、後に話があるかもしれませんが、bashFWIWの論理的な説明が見つからず、すべての事後合理化が説得力がないと思います。

答え4

試してみてください:::

ls $(エコーテスト{1,2}\.txt)

バックスラッシュを使用してください。これでうまくいきます。また、以前のポスターで述べたように、引用符を削除しました。ここで、ドットはパターンのマッチングには使用されず、文字通りピリオドと見なされます。

関連情報