ワイルドカードと中括弧で囲まれた拡張子のコレクションを含む文字列を展開しようとしています。以下の例に示すように、何も機能していないようです。変数はfirstList
正常に展開されますが、、またはは適切に展開されません。のさまざまなバージョンも試しましたが、secondList
どれも機能しません。助けていただければ幸いです。thirdList
fourthList
eval
#!/bin/bash
touch a.ext1
touch b.ext1
firstList='*.ext1'
ls $firstList
touch a.ext2
touch b.ext2
secondList='*.{ext1,ext2}'
ls $secondList
ls '$secondList'
ls "$secondList"
thirdList=*.{ext1,ext2}
ls $thirdList
ls '$thirdList'
ls "$thirdList"
fourthList="*.{ext1,ext2}"
ls $fourthList
ls '$fourthList'
ls "$fourthList"
答え1
のシェル引用符で囲まれていない場合にのみ展開され*
、引用符で囲まれている場合はシェルによる展開が停止されます。
また、中括弧の展開は、シェルによって展開されるように引用符で囲まない必要があります。
この作業 (シェルが何を実行するかを確認するには echo を使用します):
$ echo *.{ext1,ext2}
a.ext1 b.ext1 a.ext2 b.ext2
他の名前のファイルが存在する場合でも:
$ touch {a,b}.{ext1,ext2} {c,d}.{ext3,ext4} none
ls
a.ext1 a.ext2 b.ext1 b.ext2 c.ext3 c.ext4 d.ext3 d.ext4 none
$ echo *.{ext1,ext2}
a.ext1 b.ext1 a.ext2 b.ext2
なぜそれが機能するのでしょうか?
なぜそれが機能するのか理解することが重要です。それは、展開の順序によるものです。最初は「中括弧展開」、その後 (最後) は「パス名展開」(別名 glob 展開) です。
Brace --> Parameter (variable) --> Pathname
「パス名拡張」を一時的にオフにすることができます。
$ set -f
$ echo *.{ext1,ext2}
*.ext1 *.ext2
「パス名拡張」は、*.ext1
と の2 つの引数を受け取ります*.ext2
。
$ set +f
$ echo *.{ext1,ext2}
a.ext1 b.ext1 a.ext2 b.ext2
問題は、括弧展開に変数を使用できないことです。
これはこれまで何度も説明してきました。「括弧展開」内で変数を使用する
「変数展開」の結果である「括弧展開」を展開するには、 を使用してコマンド ラインをシェルに再送信する必要がありますeval
。
$ list={ext1,ext2}
$ eval echo '*.'"$list"
ブレース -->変数--> グロブ || -->ブレース--> 変数 -->グロブ
........ ここで引用 -->^^^^^^ || eval ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ファイル名の値は eval の実行に問題を引き起こしません。
$ touch 'a;date;.ext1'
eval echo '*.'"$list"
a;date;.ext1 a.ext1 b.ext1 a.ext2 b.ext2
しかし、 の値は$list
安全でない可能性があります。ただし、 の値は$list
スクリプト作成者によって設定されます。 はスクリプト作成者が制御しますeval
。 には外部で設定された値を使用しないでください$list
。次を試してください。
#!/bin/bash
touch {a,b,c}.ext{1,2}
list=ext{1,2}
eval ls -l -- '*.'"$list"
より良い代替案。
代替案(evalなし)はBashの「拡張パターン」を使用する:
#!/bin/bash
shopt -s extglob
list='@(ext1|ext2)'
ls -- *.$list
注意: 両方のソリューション (eval と patterns) (記述されているとおり) は、スペースまたは改行を含むファイル名に対して安全であることに注意してください。ただし、スペースを含む の場合は、引用符が付いていないか、eval によって引用符が削除される$list
ため、失敗します。$list
答え2
考慮する:
secondList='*.{ext1,ext2}'
ls $secondList
問題はそれですブレース拡張終わらせる前に 変数の拡張つまり、上記では、中括弧の展開は実行されません。
これは、bash が最初にコマンドラインを見たときに中括弧がないためです。secondList
展開された後ではすでに遅すぎます。
次の方法が機能します:
$ s='*'
$ ls $s.{ext1,ext2}
a.ext1 a.ext2 b.ext1 b.ext2
ここで、コマンドラインには中括弧があり、ブレース拡張最初のステップとして実行することができます。その後、の値が$s
(変数の拡張)、 そして最後にパス名の拡張は発表された。
ドキュメンテーション
man bash
拡張の順序を説明します。
展開の順序は、中括弧展開、チルダ展開、パラメータと変数の展開、算術展開、コマンド置換(左から右に実行)、単語分割、パス名展開です。