この sed コマンドはなぜ最後から 3 番目の「and」を置き換えないのでしょうか?

この sed コマンドはなぜ最後から 3 番目の「and」を置き換えないのでしょうか?

2020年5月26日更新

これはバグのようだったので、バグを報告しました。ID は #41558 です。


ちょっといじっていsedたら、この演習を思いつきました。最後から 3 番目の「and」(単語、部分文字列ではない) を置き換えて、次を作成します。

dog XYZ foo and bar and baz land good

これはうまくいくと思った

echo 'dog and foo and bar and baz land good' |
    sed -E 's/(.*)\band\b((.*\band\b){2})/\1XYZ\2/'

しかし、実際には最後から 2 番目の「and」が置き換えられます。私が考えられる唯一の説明は、「land」が の 1 つとして含まれているということなのです\band\bが、単語の境界を含めたので、そうではないはずです\b

答え1

これは、ルックアラウンドなどがサポートされていないため(PCRE では可能ですが)、実行が困難ですsed。文字列を逆にして、逆順にされた単語の 3 番目の出現を先頭から置き換え、もう一度逆にする方が簡単です。

$ echo 'dog and foo and bar and baz land good' | rev | sed 's/\<dna\>/XXX/3' | rev
dog XXX foo and bar and baz land good

式が機能しない理由については、バグのようです。後方参照は\3文字列のようですが、 before in には何の効果もなかった baz landようです。\band.*\band\b

コマンド

sed -E 's/(.*)\<and\>((.*\<and\>){2})/\1XYZ\2/'

sedは、OpenBSD のネイティブ(の代わりに\<と を使用)で正しく動作するようです。\>\b

sed私はまだこれに関してGNUやGNUに対する既存のバグ報告を見つけていないglibcが、少なくとも関連しているglibc バグ 25322(理由は下記を参照)。

もう少し詳しく説明することで、これを回避できます。

sed -E 's/(.*)\band\b(.*\band\b.*\band\b)/\1XYZ\2/'

答え2

問題を報告することを提案します。これらの例をテストしたところ、、およびで同じ動作になりました。GNU grep以下に示す 1 つのケースを除きます。GNU sedGNU awk

  • 出力が間違っています:

    $ echo 'cocoa' | sed -nE '/(\bco){2}/p'
    cocoa
    

    sed -nE '/(\<co){2}/p'awk '/(\<co){2}/'バグのある動作もありますが、正しくgrep -E '(\<co){2}'出力されません

  • 正しい動作、出力なし:

    $ echo 'cocoa' | sed -nE '/\bco\bco/p'
    
  • 出力が間違っていますitwith

    $ echo 'it line with it here sit too' | sed -E 's/with(.*\bit\b){2}/XYZ/'
    it line XYZ too
    
  • 正しい動作、入力は変更されません

    $ echo 'it line with it here sit too' | sed -E 's/with.*\bit\b.*\bit\b/XYZ/'
    it line with it here sit too
    
  • 単語の境界を および に変更すると\<\>別の問題が発生します。

    これは正しく変更しない入力:

    $ echo 'it line with it here sit too' | sed -E 's/with(.*\<it\>){2}/XYZ/'
    it line with it here sit too
    

    これにより、入力が正しく修正されます

    $ echo 'it line with it here it too' | sed -E 's/with(.*\<it\>){2}/XYZ/'
    it line XYZ too
    

    しかし、これは入力を変更できない

    $ echo 'it line with it here it too sit' | sed -E 's/with(.*\<it\>){2}/XYZ/'
    it line with it here it too sit
    

また、問題のある動作は、競合する単語の先頭に余分な文字がある場合にのみ発生します。たとえば、itand sit。ただし、末尾に文字がある場合は発生しません。たとえば、itand siteand item

$ echo 'it line with it here item too' | sed -E 's/with(.*\bit\b){2}/XYZ/'
it line with it here item too
$ echo 'it line with it here it too item' | sed -E 's/with(.*\<it\>){2}/XYZ/'
it line XYZ too item

関連情報