これはバグのようだったので、バグを報告しました。その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
の単語の境界を含めたのでそうではありませんか?
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
式が機能しない理由については、これはバグのようです。 \3
のand
の前の 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/'
問題を報告することをお勧めします。これらの例をテストしたところ、_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
_