
私のディレクトリには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?.txt
1つ以上のファイル名と一致するため、リスト要素はパターンと一致するファイル名のリストに置き換えられ、およびを含む2つの要素のリストがtest?.txt
生成されます。最後に、2つのパラメータとを使用して呼び出しが行われます。test1.txt
test2.txt
ls
test1
test2
を使用すると、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
csh
zsh
後者の例は、またはの例と同じですksh93
。mksh
fish
また、支柱の拡張に注意してください。ワイルドカードの一部としてglob(3)
また、ライブラリ機能(少なくともLinuxおよびすべてのBSDでは)や他の独立した実装(perl:など)を介して使用できますperl -le 'print join " ", <test{1,2}.txt>'
。
なぜこれが異なって行われたのか、後に話があるかもしれませんが、bash
FWIWの論理的な説明が見つからず、すべての事後合理化が説得力がないと思います。
答え4
試してみてください:::
ls $(エコーテスト{1,2}\.txt)
バックスラッシュを使用してください。これでうまくいきます。また、以前のポスターで述べたように、引用符を削除しました。ここで、ドットはパターンのマッチングには使用されず、文字通りピリオドと見なされます。