Por que este comando sed não substitui o penúltimo "e"?

Por que este comando sed não substitui o penúltimo "e"?

Atualização 26/05/2020

Parecia que isso era um bug, então registrei um bug. Seu ID é #41558.


Eu estava apenas brincando sede 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 \bpalavra limites?

Responder1

Isso é difícil de fazer, pois sednã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 \3parece ser a string  baz land, como se o \bbefore andin .*\band\bnunca 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 sedou GNU glibcsobre 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 sede GNU awk. Exceto um caso, que é mencionado abaixo.

  • Saída errada:

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

    sed -nE '/(\<co){2}/p'e awk '/(\<co){2}/'tem comportamento de buggy também, mas grep -E '(\<co){2}'corretamente não dá saída

  • Comportamento correto, sem saída:

    $ echo 'cocoa' | sed -nE '/\bco\bco/p'
    
  • Saída errada: há apenas 1 palavra inteira itdepoiswith

    $ 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, ite sit. Mas não se houver personagens no final. Por exemplo, ite sitee 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

informação relacionada