![正規表現「.+」が期待どおりに機能しないのはなぜですか? [コピー]](https://linux33.com/image/92267/%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%8F%BE%E3%80%8C.%2B%E3%80%8D%E3%81%8C%E6%9C%9F%E5%BE%85%E3%81%A9%E3%81%8A%E3%82%8A%E3%81%AB%E6%A9%9F%E8%83%BD%E3%81%97%E3%81%AA%E3%81%84%E3%81%AE%E3%81%AF%E3%81%AA%E3%81%9C%E3%81%A7%E3%81%99%E3%81%8B%EF%BC%9F%20%5B%E3%82%B3%E3%83%94%E3%83%BC%5D.png)
[root@localhost opt]# cat cfg
key = value
[root@localhost opt]# grep 'key\s*=\s*.+' cfg
[root@localhost opt]#
私の意図は次のとおりです。=
記号の後にはゼロ個以上のスペースを含めることができますが、後ろにはスペース以外の文字を1つ以上含める必要があります。
この行はなぜ出力されないのですかkey = value
?
答え1
観察する:
$ grep 'key\s*=\s*.+' cfg
$ grep 'key\s*=\s*.\+' cfg
key = value
$ grep -E 'key\s*=\s*.+' cfg
key = value
デフォルトの正規表現(BRE、デフォルト)はプラス+
記号を表します。 GNU拡張として、1つ以上の古い文字を表すために使用できます\+
。?
、、に{
も|
同様です(
。バックスラッシュでエスケープしない限り、BREでは通常の文字として扱われます。
拡張正規表現を使用すると、ルールが変更されます-E
。 ERE の場合、バックスラッシュは必要なく、通常は+
1 つ以上の前の文字を表します。 ERE では、\+
一般的な一般プラス記号を表します。
答え2
key\s*=\s*.+
GNU ERE構文(\s
空白文字と一致し、+
1つ以上の先行原子と一致すると仮定)。したがって、GNU実装が必要であり、grep
その-E
オプションを渡します。
しかしそれさえあまり意味がない。
最初
grep 'key\s*=\s*.+'
機能的に同じ
grep 'key\s*=\s*.'
なぜなら、文字列が一致すればanything.+
文字列も一致し、anything.
その逆も同様であるからです。
また、空白文字も文字です。\s*
ゲーム以来0以上の空白文字、key\s*=\s*.
機能的にkey\s*=.
(lineを含むkey<optional-spaces>=<one-character-space-or-not>
)と同じです。
希望する場所は次のとおりです。
grep 'key\s*=\s*\S'
の右側に空白以外の文字を1つ以上見つける必要があり、=
その機能は次のとおりです。
grep 'key\s*=.*\S'
また、key = foo
一致するがnonkey = foo
。key
行の先頭でのみ検索するには、アンカーを使用して以下を要求する必要があります^
。
grep '^key\s*=.*\S'
または、-x
正規表現を使用して行全体を一致させます。
grep -x 'key\s*=.*\S.*'
に対応する標準は(for)\s
です。[[:space:]]
[^[:space:]]
\S
この要件を満たすもう1つの方法は、特定の正規表現(PCREなど)でスプレッド演算子を使用してバックトラッキングを防ぐことです。
key=\s*.
key=
正規表現エンジンが\s*
次の空白文字を貪欲に繰り返して1を見つけ、行末に達したため一致=
できないことに気づいたので一致します。.
逆追跡次の一致項目(ここでは空白文字)が一致する\s
ように、より少ない一致項目(この場合は0)を試してください。.
-P
GNUオプションと同様に、PCREを使用すると、次のようにgrep
書くことができます。
grep -P '^key\s*=(?>\s*).'
この(?>...)
構文は逆追跡を防ぎます。したがって、\s*
逆追跡できない状態でできるだけ多くの空白文字を食べることができるため、空白の後に空白以外の文字が1つ以上来る場合にのみ一致が発生します。
$ printf 'key=%s\n' '' ' ' ' a' | grep '^key\s*=\s*.'
key=
key= a
$ printf 'key=%s\n' '' ' ' ' a' | grep -P '^key\s*=(?>\s*).'
key= a
$ printf 'key=%s\n' '' ' ' ' a' | grep '^key\s*=.*\S'
key= a