なぜ `|`がglobパターンで文字通り処理されないのですか?


13

私の質問は、シェル変数に正規表現を格納することで、シェルに特殊な文字を引用符で囲む際の問題をどのように回避できるかということです。

  1. エラーがあるのはなぜですか:

    $ [[ $a = a|b ]]  
    bash: syntax error in conditional expression: unexpected token `|'
    bash: syntax error near `|b'

    [[ ... ]]の第2オペランドの内側は=、グロビングパターンであると予想されます。

    a|b有効なグロビングパターンではありませんか?違反している構文規則を指摘できますか?

  2. 以下のコメント|は、パイプとして解釈されることを指摘しています。

    次に=、globパターンを=~regexパターンに変更すると |機能します

    $ [[ $a =~ a|b ]]

    私はから学んだ学習のBashでP180 私の以前の記事|でも(例では、解析条件式を含む)の解釈のいずれかの他のステップの前に、解釈の先頭にパイプとして認識されています。では、を使用するときに、無効な使用時にパイプとして認識されることなく|、を使用するときに正規表現演算子としてどのように認識でき ますか?そのため、パート1の構文エラーは、それがパイプとして解釈されることを意味しないと思います。=~=|

    シェルが標準入力またはスクリプトから読み取る各行は、パイプラインと呼ばれます。0個以上のパイプ文字(|)で区切られた1つ以上のコマンドが含まれています。読み取るパイプラインごとに、シェルはそれをコマンドに分割し、パイプラインのI / Oを設定してから、コマンドごとに以下を実行します(図7-1)。

ありがとう。


1
bashの一部のバージョンでは、extglob解析(|特別な場合)はデフォルトでの右側でオンになっていることに注意してください[[ $var = $pattern ]]shoptこの動作が見られるバージョンとオプションの構成を分離することは興味深いでしょう。extglobデフォルトまたは明示的な構成のいずれかでオンになっているものだけである場合は、そうです。
チャールズダフィー

2
ところで、パイプ文字が解析の前の段階に干渉する場合をより包括的に除外したい場合(これは起こっていないことに同意しますが、読者にとってはそれほど明白ではありません)使用してpattern='a|b'から$pattern、RHSで引用符なしで展開します。
チャールズダフィー

@CharlesDuffy、それがこの質問のフォローアップであるQ&Aで指摘されてました。
ステファンシャゼラス

ああ-コンテキストは理にかなっています。ここでのあなたの答えは素晴らしいです。両方の点でありがとう。
チャールズダフィー

ティム、以下の答えのどれかがあなたの質問に答えますか?もしそうなら受け入れてください。ありがとうございました!
ジェフシャラー

回答:


13

理由はありません

[[ $a = a|b ]]

$ aがa|b文字列かどうかをテストする代わりにエラーを報告する必要がありますが、[[ $a =~ a|b ]]エラーは返されません。

唯一の理由は、|一般的に(外側と内側[[ ... ]])特殊文字であるということです。その[[ $a =位置でbashは、通常のシェルコマンドラインの引数またはリダイレクトのターゲットのような通常のWORDであるトークンのタイプを期待します(ただしextglob、bash 4.1以降でオプションが有効になっているかのように)。

(ここではWORDで、POSIX仕様説明されているような架空のシェル文法の単語を参照します。これは、シェルが英語のような単語の他の定義ではなく、単純なシェルコマンドラインで1つのトークンとして解析するものです文字または非間隔文字の配列の配列のいずれか。、、二つのそのようなあるWORD)S。foo"bar baz"$(echo x y)

通常のシェルコマンドラインでは:

echo a|b

echo aパイプされbます。a|bWORDではなく、3つのトークン:a WORD|トークン、およびb WORDトークンです。

で使用する場合[[ $a = a|b ]]、取得bashするWORDa予期し|ますが()、エラーを引き起こす予期しないトークンを検出します。

興味深いことに、bash不満はありません:

[[ $a = a||b ]]

それは今だからa続くトークン||が続くトークンb:それは同じように解析されていますので、

[[ $a = a || b ]]

それがテストで$aあるaか、それともb文字列が空でないことです。

今:

[[ $a =~ a|b ]]

bash同じ解析ルールを持つことはできません。同じ解析ルールを持つことは、上記がエラーを与え、それ|a|b単一のWORDであることを保証するためにそれを引用する必要があることを意味するでしょう。ただし、bash 3.2以降では、次のようになります。

[[ $a =~ 'a|b' ]]

これは、a|b正規表現ではなく、正規表現と一致しa\|bます。つまり、シェルの引用には、正規表現演算子の特別な意味を削除するという副作用があります。これは機能であるため、動作は[[ $a = "?" ]]1つに似ていますが、ワイルドカードパターン(で使用[[ $a = pattern ]])はシェルWORDS(たとえばglobsで使用)であり、正規表現はそうではありません。

だから、bashそうでない場合は、通常のような特殊なシェル文字であるすべての拡張正規表現の演算子を処理するために持って|()の引数の解析異なったときに=~オペレータを。

それでも、その間

 [[ $a =~ (ab)*c ]]

動作します

 [[ $a =~ [)}] ]]

しません。必要なもの:

 [[ $a =~ [\)}] ]]
 [[ $a =~ [')'}] ]]

の以前のバージョンでbashは、バックスラッシュが間違って一致していました。これは修正されましたが、

 [[ $a =~ [^]')'] ]]

たとえば、バックスラッシュと一致しません。かっこ内にあるbashことを認識できないため))toをエスケープして、と以外の[^]\)]すべての文字に一致する正規表現を生成します。]\)

ksh93 その前部にはるかに悪いバグがあります。

zsh、これは通常のシェル単語であり、正規表現演算子を引用しても正規表現演算子の意味には影響しません。

[[ $a =~ 'a|b' ]]

a|b正規表現と照合します。

これは、/ コマンドに=~も追加できることを意味します。[test

[ "$a" '=~' 'a|b' ]
test "$a" '=~' 'a|b'

(でも動作しyashます。特別なシェル演算子と同様=~に引用符で囲む必要があります)。zsh=something

bash 3.1はのように振る舞っていましたzsh。それはおそらくに合わせて、3.2に変更ksh93(たとえbash最初に思いついたことをシェルだった[[ =~ ]]あなたはまだやることができます)が、BASH_COMPAT=31あるいはshopt -s compat31その中には除いて(以前の動作に戻す[[ $a =~ a|b ]]には、エラーが返されますbash、それはもうない、3.1のbash -O compat31新しいバージョンでbash)。

ルールが混乱していると言った理由と、以下を使用する理由が明確になることを願っています。

[[ $a =~ $var ]]

他のシェルへの移植性を含めるのに役立ちます。


zshもでエラーを報告しています[[ $a = a|b ]]
アイザック

@isaac、はい、それが私がここで作っているポイントです。a|bシェルではありませんWORD、それはだ、ここではa|およびbトークン。同様にecho a|b、出力しないa|bか、展開されませんa|bグロブは、あなたはそれを引用する必要があり|、それがそのコンテキストで無効である特殊なシェル文字だとして。zshワイルドカード演算子と[[ $a = (a|b) ]]同じようecho (a|b)に機能し(a|b)ます。
ステファンシャゼラス

あなたの答えの文言と説明はbashのみです。それがすべての真実ではありません。
アイザック

11

:標準グロブ(「ファイル名展開」)があり*?[ ... ]|標準(非拡張)設定では有効なglob演算子ではありません。

試してください:

shopt -s extglob
[[ a = @(a|b) ]] && echo matched

1
ありがとう。しかし、なぜ|文字通りに統合されないのですか?なぜ構文エラーがあるのですか?
ティム

1
引用されていません。
ジェフシャラー

3
標準設定で|は、グロブ演算子ではないので、|引用符なしで文字通り解釈されませんか?では、なぜ構文エラーがあるのですか?
ティム

1
|制御文字です。文字や数字と同じようにリテラル文字として扱われることはありません。
chepner

3
そのモードでは、シェルはまだ閉じられていない[[]]の途中でパイプリダイレクト文字を予期していなかったためです。[[ $a = a出力が別のプロセスにパイプされる可能性のある有効なコマンドではありません(少なくとも、シェルはあなたがやろうとしていると考えていました)。
ジェイソンC

5

正規表現の一致が必要な場合、テストは次のようになります。

[[ "$a" =~ a|b ]]

@Tim現在の質問を継続的に編集するのではなく、新しい質問を開く必要があります。
gardenhead

@gardenhead:私の更新は、あなたがそれを見逃した場合に備えて、質問を変更するのではなく、明確にすることです。追加した2番目の部分は、元の質問に関する1つのコメントのパイプの説明(構文エラー)が正しくないことを示すことです。
ティム
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.