Atualização 26/05/2020
Parecia que isso era um bug, então registrei um bug. Seu ID é #41558.
Eu estava apenas brincando sed
e pensei neste exercício: para substituir a penúltima ocorrência de "e" (a palavra, não como uma substring), para criar:
dog XYZ foo and bar and baz land good
Eu pensei que isso iria funcionar
echo 'dog and foo and bar and baz land good' |
sed -E 's/(.*)\band\b((.*\band\b){2})/\1XYZ\2/'
mas na verdade substitui a penúltima ocorrência de "e". A única explicação que consigo pensar é que está incluindo "terra" como um dos \band\b
, mas não deveria ser o caso porque incluí a \b
palavra limites?
Responder1
Isso é difícil de fazer, pois sed
não oferece suporte a pesquisas etc. (como você pode fazer em um PCRE). Seria mais fácil inverter a string e substituir a terceira ocorrência da palavra invertida desde o início e depois reverter novamente.
$ 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
Quanto ao motivo pelo qual sua expressão não funciona, parece um bug. A referência anterior \3
parece ser a string baz land
, como se o \b
before and
in .*\band\b
nunca tivesse tido qualquer efeito.
O comando
sed -E 's/(.*)\<and\>((.*\<and\>){2})/\1XYZ\2/'
parece fazer a coisa certa no OpenBSD com seu nativo sed
(que usa \<
and \>
em vez de \b
).
Ainda não encontrei um relatório de bug existente contra o GNU sed
ou GNU glibc
sobre isso, embora não ficaria surpreso se fosse pelo menosrelacionadoparaerro glibc 25322(porque, veja abaixo).
Você pode contornar isso sendo um pouco mais detalhado:
sed -E 's/(.*)\band\b(.*\band\b.*\band\b)/\1XYZ\2/'
Responder2
Eu sugiro registrar um problema. Eu testei esses exemplos, o que resulta no mesmo comportamento com GNU grep
, GNU sed
e GNU awk
. Exceto um caso, que é mencionado abaixo.
Saída errada:
$ echo 'cocoa' | sed -nE '/(\bco){2}/p' cocoa
sed -nE '/(\<co){2}/p'
eawk '/(\<co){2}/'
tem comportamento de buggy também, masgrep -E '(\<co){2}'
corretamente não dá saídaComportamento correto, sem saída:
$ echo 'cocoa' | sed -nE '/\bco\bco/p'
Saída errada: há apenas 1 palavra inteira
it
depoiswith
$ echo 'it line with it here sit too' | sed -E 's/with(.*\bit\b){2}/XYZ/' it line XYZ too
Comportamento correto, a entrada não é modificada
$ echo 'it line with it here sit too' | sed -E 's/with.*\bit\b.*\bit\b/XYZ/' it line with it here sit too
Alterar os limites das palavras para
\<
e\>
resulta em um problema diferente.Isso corretamentenão modificaa entrada:
$ echo 'it line with it here sit too' | sed -E 's/with(.*\<it\>){2}/XYZ/' it line with it here sit too
Isso modifica corretamente a entrada
$ echo 'it line with it here it too' | sed -E 's/with(.*\<it\>){2}/XYZ/' it line XYZ too
Mas este não consegue modificar a entrada
$ echo 'it line with it here it too sit' | sed -E 's/with(.*\<it\>){2}/XYZ/' it line with it here it too sit
Além disso, o comportamento problemático só é observado se a palavra conflitante tiver caracteres extras no início. Por exemplo, it
e sit
. Mas não se houver personagens no final. Por exemplo, it
e site
e 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