web-dev-qa-db-ja.com

コマンドラインで正規表現のバックスラッシュをエスケープするために必要なバックスラッシュの数

最近、コマンドラインでいくつかの正規表現に問題があり、バックスラッシュを一致させるために、異なる数の文字を使用できることがわかりました。この数は、正規表現に使用される引用に依存します(なし、一重引用符、二重引用符)。私の意味については、次の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

この意味は:

  • 引用符なしで、バックスラッシュを4〜7個の実際のバックスラッシュと一致させることができます
  • 二重引用符で、バックスラッシュを3〜6個の実際のバックスラッシュと一致させることができます
  • 一重引用符を使用すると、バックスラッシュを2〜3個の実際のバックスラッシュと一致させることができます

シェルでは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):

  • 引用符なしで、バックスラッシュを4〜5個の実際のバックスラッシュと一致させることができます
  • 二重引用符で、バックスラッシュを3〜4個の実際のバックスラッシュと一致させることができます
  • 一重引用符を使用すると、バックスラッシュを2つの実際のバックスラッシュと一致させることができます

Grepとawkのコマンドラインで、引用符で囲まれていない正規表現文字列と二重引用符で囲まれた正規表現文字列の違いを誰かが説明できますか?私は通常Perlのワンライナーを使用しないため、Perlの動作の説明にはそれほど興味がありません。

12
daniel kullmann

引用符で囲まれていない例では、各\\ペアは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つ下にスライドします。

10

このリンクはbashについて説明しています 引用とエスケープ

あなたの質問は、最初の3つのセクションを扱います。

  • 文字ごとのエスケープ
  • 弱い引用符 "二重引用符"
  • 強力な引用「単一引用」
  • ANSI Cのような文字列の引用
  • I18N/L10N引用(国際化とローカリゼーション)

以下は、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
6
Peter.O