web-dev-qa-db-ja.com

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

2020年5月26日更新

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


私はsedをいじくり回していて、この演習を思い付きました。3番目から最後までの「and」(サブストリングではなくWord)を置き換えて、以下を作成します。

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」が置き換えられます。私が考えることができる唯一の説明は、それが\band\bの1つとして「土地」を含んでいるということですが、私は\bの単語の境界を含めたのでそうではありませんか?

3
b0yfriend

sedはルックアラウンドなどをサポートしていないため、これは困難です(PCREで行うことができます)。文字列を逆にして、逆のWordの最初から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

式が機能しない理由については、これはバグのようです。 \3andの前の baz landがまったく効果がないかのように、逆参照\bは文字列.*\band\bのようです。

コマンド

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

openBSDでネイティブsed\<の代わりに\>\bを使用)を使用して、正しいことをしているようです。

私はGNU sedまたはGNU glibcに対する既存のバグレポートをまだ見つけていませんが、 glibcバグ25322 (以下を参照)と少なくとも関連であったとしても驚かないでください。

もう少し冗長にすることで回避できます。

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

問題を報告することをお勧めします。これらの例をテストしたところ、_GNU grep_、_GNU sed_、および_GNU awk_で同じ動作が発生しました。以下に示す1つのケースを除きます。

  • 間違った出力:

    _$ 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'
    _
  • 間違った出力:itの後にWord全体が1つしかないwith

    _$ 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
    _
  • Wordの境界を_\<_および_\>_に変更すると、別の問題が発生します。

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

    _$ 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
    _

また、問題のある動作は、競合するWordの先頭に余分な文字がある場合にのみ発生します。たとえば、itおよびsitです。しかし、最後に文字がある場合はそうではありません。たとえば、itおよびsiteおよび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
_
0
Sundeep