nullマッチング正規表現がsedとgrepで異なる動作をするのはなぜですか?

nullマッチング正規表現がsedとgrepで異なる動作をするのはなぜですか?

たとえば、Debian または Debian 派生システムのツールを使用すると、空のN*文字列に一致する次の正規表現を sed で一致させることができます。

$ echo 'Hello' | sed 's/N*/ xx&xx /g'
  xxxx H xxxx e xxxx l xxxx l xxxx o xxxx

xxxxこれは、各文字列文字の前の空の一致(つまり、間に文字がない文字列)の正しい結果ですxx&xx(から6回Hello。末尾の改行は計算されず、一致しません)。

xxそして、文字(または文字グループ)が一致すると、および間に表示されますxx

$ echo  'Hello' | sed 's/e*/ xx&xx /g'
 xxxx H xxexx l xxxx l xxxx o xxxx

しかし、grepの同じ正規表現はいいえ空の文字列と一致します。

$ echo 'Hello' | grep -o 'N*'

しかし印刷します。ただ空でない一致:

$ echo 'Hello' | grep -o 'e*'
e

正規表現の一致を避けるためにgrepに追加の内部ルールがありますかempty

答え1

grep -ogrep --help次のように記録

  -o, --only-matching       show only nonempty parts of lines that match

そしてマニュアルに〜のように

一致する行の一致する(空でない)部分のみが印刷され、各部分は別々の出力行に表示されます。

はい。追加のルールがありますgrep -o。一致が空でない場合のみ出力されます。

ではecho 'Hello' | grep -o 'N*'正規表現は一致しますが(戻りコードを表示または使用してわかりますecho 'Hello' | grep 'N*')、一致は空であるため何も出力されません。

答え2

長さがゼロの文字列マッチングの動作は、コード内の特別なケースである場合とそうでない場合があります。例ではsedありませんが、perl

$ echo aabb | sed 's/a*/X/g'
XbXbX
$ echo aabb | gsed 's/a*/X/g'
XbXbX
$ echo aabb | perl -ple 's/a*/X/g'
XXbXbX

行動はviが歴史的にどのように行動したかに依存します。深さからわかるようにex/ex_subst.c

    /*
     * !!!
     * It's possible to match 0-length strings -- for example, the
     * command s;a*;X;, when matched against the string "aabb" will
     * result in "XbXbX", i.e. the matches are "aa", the space
     * between the b's and the space between the b's and the end of
     * the string.  There is a similar space between the beginning
     * of the string and the a's.  The rule that we use (because vi
     * historically used it) is that any 0-length match, occurring
     * immediately after a match, is ignored.  Otherwise, the above
     * example would have resulted in "XXbXbX".  Another example is
     * incorrectly using " *" to replace groups of spaces with one
     * space.

(もう1つの問題は、幅0の一致が絶対一致しないことです。これを防ぐために、「次の文字に移動します...」コードを追加できると確信しています。後ろに誰かのCPUが数回100%に達し、彼の手のひらが額に触れました。 )

BSDとGNUはどちらも式edに失敗するので、s/a*/X/g珍しい動作ex-vised

$ echo aabb > foo
$ ed foo
5
s/a*/X/g
?
s/a*/X
Xbb
Q

答え3

$ echo 'Hello' | grep -o 'N*'
$ echo $?
0

それする終了状態として示されているように、この入力行の空の部分文字列と一致します。 (たとえば、他のモードを使用すると、Nstdoutでは何も得られませんが、終了状態は1、failureです。)

-oそうしないように印刷空の一致がありますが、正規表現が入力行と一致するかどうかには関係ありません。 (はい、違いがわかります。空の文字列一致を印刷すると、各一致の後に改行が印刷されるため、プロンプトの前に空白行があります。または各一致に対して1つずつ6になります。)

それ以外の場合は、-o一致する行全体を印刷します。

$ echo 'Hello' | grep  'N*'          # same as grep '' empty pattern
Hello

関連情報