最近、コマンドラインでいくつかの正規表現に問題があり、バックスラッシュを一致させるために、異なる数の文字を使用できることがわかりました。この数は、正規表現に使用される引用に依存します(なし、一重引用符、二重引用符)。私の意味については、次のbashセッションを参照してください。
echo "#ab\\cd" > file
grep -E ab\cd file
grep -E ab\\cd file
grep -E ab\\\cd file
grep -E ab\\\\cd file
#ab\cd
grep -E ab\\\\\cd file
#ab\cd
grep -E ab\\\\\\cd file
#ab\cd
grep -E ab\\\\\\\cd file
#ab\cd
grep -E ab\\\\\\\\cd file
grep -E "ab\cd" file
grep -E "ab\\cd" file
grep -E "ab\\\cd" file
#ab\cd
grep -E "ab\\\\cd" file
#ab\cd
grep -E "ab\\\\\cd" file
#ab\cd
grep -E "ab\\\\\\cd" file
#ab\cd
grep -E "ab\\\\\\\cd" file
grep -E 'ab\cd' file
grep -E 'ab\\cd' file
#ab\cd
grep -E 'ab\\\cd' file
#ab\cd
grep -E 'ab\\\\cd' file
この意味は:
シェルでは1つの余分なバックスラッシュが無視されることを理解しています(bashのマニュアルページから)。
「引用符で囲まれていないバックスラッシュ(\)はエスケープ文字です。次の文字のリテラル値が保持されます。」
単一引用符でエスケープは行われないため、これは単一引用符で囲まれた例には適用されません。
また、grepコマンドでは1つの追加のバックスラッシュが無視されます(「\ c」は単に「c」でエスケープされますが、これは「c」と同じです。「c」は正規表現では特別な意味を持たないためです)。
これは一重引用符を使用した例の動作を説明していますが、他の2つの例、特に非引用符付きの二重引用符付き文字列に違いがある理由はよくわかりません。
繰り返しになりますが、bashのmanページからの引用:
「二重引用符で文字を囲むと、$、 `、\、および履歴拡張が有効な場合は!を除いて、引用符内のすべての文字のリテラル値が保持されます。」
私はGNU awk(例えばawk /ab\cd/{print} file
)、同じ結果。
ただし、Perlは異なる結果を示します(例:Perl -ne "/ab\\cd/"\&\&print file
):
Grepとawkのコマンドラインで、引用符で囲まれていない正規表現文字列と二重引用符で囲まれた正規表現文字列の違いを誰かが説明できますか?私は通常Perlのワンライナーを使用しないため、Perlの動作の説明にはそれほど興味がありません。
引用符で囲まれていない例では、各\\
ペアは1つのバックスラッシュをgrepに渡します。したがって、4つのバックスラッシュは2つをgrepに渡し、これは単一のバックスラッシュに変換されます。 6つのバックスラッシュは3つをgrepに渡し、1つのバックスラッシュと1つの\c
に変換されます。これはc
と同じです。 1つの追加のバックスラッシュは、シェルによって\c
-> c
に変換されるため、何も変更しません。シェルの8つのバックスラッシュは、grepでは4つで、2つに変換されるため、これはもう一致しません。
二重引用符で囲まれた例については、bashのマンページの2番目の引用符に続くものに注意してください。
バックスラッシュは、$、 `、"、\、または改行のいずれかの文字が後に続く場合にのみ、特別な意味を保持します。
つまり奇数のバックスラッシュを指定すると、シーケンスは\c
で終了します。引用符で囲まれていない場合はc
と等しくなりますが、引用符で囲んだ場合、バックスラッシュはその特別な意味を失うため、\c
がgrepに渡されます。そのため、「可能な」バックスラッシュ(つまり、サンプルファイルに一致するパターンを構成するもの)の範囲が1つ下にスライドします。
このリンクはbashについて説明しています 引用とエスケープ
あなたの質問は、最初の3つのセクションを扱います。
以下は、bash
としての文字列がgrep
にどのように渡されるか、およびgrep
が内部で文字列をさらに解釈する方法のグラフです。
最初にecho "#ab\\cd" > file
を見てみましょう。
weak-quoted( "")"#ab\\cd"
では、\\
はエスケープされた\
は、単一のリテラル\
としてfile
に渡されます。したがって、file
にはab\cd
が含まれます
さて、あなたのコマンドに:下の表は、実際に各呼び出しで何が起こっているかを確認するのに役立ちます。 *
は、ファイルの内容と一致するものを示します。 Webページのように、bashのエスケープルールを適用するだけの問題であり、daniel kullmann`sの回答に特に注意して、エスケープについて言及していますweak-quotingの状況での動作。
バックスラッシュは、$、 `、"、\、または改行のいずれかの文字が後に続く場合にのみ、特別な意味を保持します。
bash passes grep further
to grep resolves to
grep -E ab\cd file abcd abcd
grep -E ab\\cd file ab\cd abcd
grep -E ab\\\cd file ab\cd abcd
grep -E ab\\\\cd file ab\\cd ab\cd *
grep -E ab\\\\\cd file ab\\\cd ab\cd *
grep -E ab\\\\\\cd file ab\\\cd ab\cd *
grep -E ab\\\\\\\cd file ab\\\cd ab\cd *
grep -E ab\\\\\\\\cd file ab\\\\cd ab\\cd
grep -E "ab\cd" file ab\cd abcd
grep -E "ab\\cd" file ab\cd abcd
grep -E "ab\\\cd" file ab\\cd ab\cd *
grep -E "ab\\\\cd" file ab\\cd ab\cd *
grep -E "ab\\\\\cd" file ab\\\cd ab\cd *
grep -E "ab\\\\\\cd" file ab\\\cd ab\cd *
grep -E "ab\\\\\\\cd" file ab\\\\cd ab\\cd
grep -E 'ab\cd' file ab\cd abcd
grep -E 'ab\\cd' file ab\\cd ab\cd *
grep -E 'ab\\\cd' file ab\\\cd ab\cd *
grep -E 'ab\\\\cd' file ab\\\\cd ab\\cd