6

変数に格納されている任意のグロビング パターンを確実に使用する方法はありますか? パターンにスペースとメタ文字の両方が含まれている場合、問題が発生します。これが私の言いたいことです。スペースなしで変数に格納されたパターンがある場合、問題なく動作するようです:

<prompt> touch aa.{1,2,3} "a b".{1,2,3}
<prompt> p="aa.?"
<prompt> for f in ${p} ; do echo "|$f|" ; done
|aa.1|
|aa.2|
|aa.3|
<prompt> declare -a A=($p) ; for f in "${A[@]}" ; do echo "|$f|" ; done
|aa.1|
|aa.2|
|aa.3|

ただし、パターンにスペースを入れるとすぐに、事態は受け入れられなくなります。

<prompt> p="a b.?"
<prompt> for f in ${p} ; do echo "|$f|" ; done
|a|
|b.?|
<prompt> declare -a A=($p) ; for f in "${A[@]}" ; do echo "|$f|" ; done
|a|
|b.?|
<prompt> for f in "${p}" ; do echo "|$f|" ; done
|a b.?|
<prompt> for f in $(printf "%q" "$p") ; do echo "|$f|" ; done
|a\|
|b.\?|

明らかに、事前にパターンがわかっている場合は、手動でエスケープできます。

<prompt> for f in a\ b.* ; do echo "|$f|" ; done
|a b.1|
|a b.2|
|a b.3|

問題は、事前にパターンがわからないスクリプトを書いていることです。ある種のeval策略に頼らずに、bash に変数の内容をグロビング パターンとして確実に扱わせる方法はありますか?

4

2 に答える 2

7

単語分割をオフにする必要があります。要約すると、これは機能しません。

$ p="a b.?"
$ for f in ${p} ; do echo "|$f|" ; done
|a|
|b.?|

ただし、これは次のことを行います。

$ ( IFS=; for f in ${p} ; do echo "|$f|" ; done )
|a b.1|
|a b.2|
|a b.3|

IFSシェルの「内部フィールド セパレータ」です。通常、スペース、タブ、および改行文字に設定されます。変数展開後の単語分割に使用されます。IFS空に設定すると、単語の分割が停止するため、グロブが機能します。

配列の例

同じことが配列の例にも当てはまります。

$ declare -a A=($p) ; for f in "${A[@]}" ; do echo "|$f|" ; done
|a|
|b.?|
$ ( IFS=; declare -a A=($p) ; for f in "${A[@]}" ; do echo "|$f|" ; done )
|a b.1|
|a b.2|
|a b.3|

IFSが正常値に戻ることを確認する

上記の例では、IFS割り当てをサブシェル内に配置しました。必須ではありませんが、その利点はIFS、サブシェルが終了するとすぐに以前の値に自動的に戻ることです。サブシェルがアプリケーションに適していない場合は、別のアプローチがあります。

$ oldIFS=$IFS; IFS=; for f in ${p} ; do echo "|$f|" ; done; IFS=$oldIFS
|a b.1|
|a b.2|
|a b.3|

シェルアクティブ文字とのパターンのマッチング

*名前にリテラルが含まれるファイルがあるとします。

$ touch ab.{1,2,3} 'a*b'.{1,2,3}
$ ls
a*b.1  ab.1  a*b.2  ab.2  a*b.3  ab.3

そして、その星を一致させたいとします。スターを文字どおりに扱いたいので、エスケープする必要があります。

$ p='a\*b.?'
$ ( IFS=; for f in ${p} ; do echo "|$f|" ; done )
|a*b.1|
|a*b.2|
|a*b.3|

はエスケープされないため?、ワイルドカード文字として扱われます。*はエスケープされているため、リテラルのみに一致し*ます。

于 2014-10-24T18:46:56.140 に答える
0

で使われているパターン

p="a b.?"

は正しくありません。直接使用すると明らかです。

A=( a b.? )

質問に記載されているように、正しいパターンは

a\ b.?

したがって、正しい変数の割り当ては

p='a\ b.?'

パターンはどこから来るのですか?ソースで修正できますか? たとえば、「.?」を追加してパターンを作成する場合、ベースに、「printf」を使用して必要な引用を行うことができます。

base='a b'
printf -v p '%q.?' "$base"

「set」は次を示します。

p='a\ b.?'

残念ながら、単語分割を試みると、依然として失敗します。

A=( $p )

「セット」ショー:

A=([0]="a\\" [1]="b.?")

この問題を回避する 1 つの方法は、「eval」を使用することです。

eval "A=( $p )"

「set」は次を示します。

A=([0]="a b.1" [1]="a b.2" [2]="a b.3")

これは「eval の策略」ですが、前述の IFS の策略よりも明らかに悪いわけではありません。また、照合するファイルの名前にグロビング メタ文字が含まれている場合、IFS の策略は役に立ちません。たとえば、で作成されたファイルがある場合はどうしますか

touch ab.{1,2,3} 'a*b'.{1,2,3}

そして、ベースが 'a*b' のものと一致させたいですか? 変数 p が次のように設定されている場合、どのような IFS トリックでも、変数 p と正しく一致させることはできません。

p="a*b.?"

base='a*b'
printf -v p '%q.?' "$base"

「セット」ショー:

p='a\*b.?'

以降

eval "A=( $p )"

それが示している:

A=([0]="a*b.1" [1]="a*b.2" [2]="a*b.3")

「eval」の使用は最後の手段であると考えていますが、この場合、より良いオプションは考えられず、完全に安全です。

于 2014-10-24T23:26:46.013 に答える