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


12

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

回答:


10

引用符で囲まれていない例では、各\\ペアが1つのバックスラッシュをgrepに渡すため、4つのバックスラッシュが2つをgrepに渡し、1つのバックスラッシュに変換されます。6つのバックスラッシュは3つをgrepに渡し、1つのバックスラッシュと1に変換さ\ccます。それが翻訳されているため、一つの追加のバックスラッシュは、何も変わりません\c> - cシェルによって。シェルの8つのバックスラッシュはgrepで4つあり、2つに変換されるため、これはもう一致しません。

二重引用符で囲まれた例については、bashのマンページからの2番目の引用に続くものに注意してください。

バックスラッシュは、$、 `、"、\、または改行のいずれかの文字が後に続く場合にのみ、特別な意味を保持します。

つまり、奇数個のバックスラッシュを指定すると、シーケンスはで終わります\c。これはc引用符で囲まれていない場合と同じですが、引用符で囲むとバックスラッシュはその特別な意味を失うため\c、grepに渡されます。そのため、「可能な」バックスラッシュの範囲(つまり、サンプルファイルに一致するパターンを構成するもの)が1つ下にスライドします。


...そして、いくつかの奇妙な点があります:exempleの場合:二重引用符で囲まれているため、シェルによって変換されるべきprintf "\ntest"であるにもかかわらず、「test」の前に改行を挿入します...(期待される結果は、 「\ ntest」、「ntest」。私たちは書く習慣を身につける必要があります:または 、しかし、どういうわけか奇妙なことに依存する多くのスクリプトが表示されます"\n""n"printf "\\ntest"printf '\ntest'
Olivier Dulac

6

このリンクでは、bash QuotesとEscapingについて説明しました

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

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

以下は、文字列がどのように文字列にbash渡されるか、さらに文字列を内部でgrepどのようgrepに解釈するかを示すグラフです。

最初に見てみましょうecho "#ab\\cd" > file弱い引用(「」) 、 エスケープされているに渡される単一リテラルとして。だから、含まれています
"#ab\\cd"\\\file\fileab\cd

さて、あなたのコマンドについて:以下のチャートは、各呼び出しで実際に何が起こっているかを見るのに役立つかもしれません。*ファイルの内容と一致するものを示しています。Webページのように、bashのエスケープルールを適用するだけです。特に、弱い引用の状況で行動を逃れることを指すダニエルカルマンの答えに注意してください。

バックスラッシュは、$、 `、"、\、または改行のいずれかの文字が後に続く場合にのみ、特別な意味を保持します。


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