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
ようです。\b
and
.*\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 sed
GNU 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'
出力が間違っています
it
。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
単語の境界を および に変更すると
\<
、\>
別の問題が発生します。これは正しく変更しない入力:
$ 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
また、問題のある動作は、競合する単語の先頭に余分な文字がある場合にのみ発生します。たとえば、it
and sit
。ただし、末尾に文字がある場合は発生しません。たとえば、it
and site
and 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