ワイルドカード展開とブレース展開を組み合わせたbash


8

ワイルドカードと中かっこ内で指定された拡張子のコレクションを含む文字列を展開しようとしています。以下の例が示すように、何も機能しないようです。変数にはfirstList罰金を展開しませんが、どちらもsecondListthirdListあるいは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"

fwiw eval ls $secondListはここで正常に動作します...何を達成しようとしていますか?
don_crissti 2016年

bash展開順序を確認する必要があります-ブレース展開はパラメーター展開の前に発生します。期待する効果を得るにはeval、2回目の拡張を行う必要があります。
グレンジャックマン、


@glennjackman evalなしのソリューションがあります。私の回答の最後にある2番目の解決策を見てください。

回答:


7

シェルが展開する*非引用された場合にのみ、任意の引用は、シェルによって展開を停止します。

また、ブレース展開は、シェルによって展開されるために引用符で囲まれていない必要があります。

この作業(エコーを使用してシェルの動作を確認します):

$ 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-expansion)。

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の実行問題を引き起こしません:

$ 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

注:両方のソリューション(評価とパターン)(記述どおり)は、スペースまたは改行のあるファイル名に対して安全であることに注意してください。ただし$list、スペースのあるaの場合は失敗し$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 拡張の順序を説明します:

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.