Como recursivamente substituir caracteres com sed?

13

É possível substituir ocorrências de uma sequência de caracteres recursivamente sem iterar novamente sobre a mesma sequência?

Ao executar um sed , como nos cenários a seguir, posso obter a saída mencionada.

$ echo XX | sed -e 's/XX/XoX/g'
XoX  
$ echo XXX | sed -e 's/XX/XoX/g'
XoXX  
$ echo XXXX | sed -e 's/XX/XoX/g'
XoXXoX  

No entanto, estou esperando que a saída siga o seguinte comportamento.

Entrada:

XX
XXX
XXXX

Resultado esperado:

XoX
XoXoX
XoXoXoX

É possível alcançar o comportamento esperado apenas com sed?

    
por Ishan Madhusanka 15.10.2018 / 08:59

4 respostas

23

Você pode fazer:

> echo XXXX | sed -e ':loop' -e 's/XX/XoX/g' -e 't loop'
XoXoXoX

Com:

  • -e ':loop' : crie um rótulo "loop"
  • -e 't loop' : Ir para o rótulo "loop" se a substituição anterior foi bem sucedida
por 15.10.2018 / 09:25
10

Neste caso particular, look-ahead ou look-behind seria útil. Eu acho que o GNU sed não suporta isso. Com perl :

perl -ne 's/X(?=X)/Xo/g; print;'

Você também pode usar lookbehind e lookahead como:

s/(?<=X)(?=X)/o/g

Onde:

(?<=X) is a positive lookbehind, a zero-length assertion that make sure we have an X before the current position
(?=X) is a positive lookahead, a zero-length assertion that make sure we have an X after the current position

Usando um perl one-liner:

perl -pe 's/(?<=X)(?=X)/o/g' inputfile

Onde:

-p causes Perl to assume a loop around the program with an implicit print of the current line

    
por 15.10.2018 / 11:26
5

A resposta em loop é a maneira geral de fazer o que você está pedindo.

No entanto, no caso de seus dados, supondo que você esteja usando o GNU, você pode simplesmente fazer:

sed 's/\B/o/g'

As opções \b e \B são extensões regex :

  • \b corresponde a limites de palavras, ou seja, a transição de um caractere "palavra" para um caractere "sem palavra" ou vice-versa
  • \B corresponde ao oposto de \b . isto é, as lacunas "dentro" das palavras. Isso nos permite inserir caracteres dentro de uma palavra, mas não fora dela, conforme necessário.

Tente online .

Isso pressupõe que os caracteres de entrada são, na verdade, todos os caracteres "word".

Alternativamente, se você não tem o GNU sed, ou se os caracteres de entrada não são todos caracteres "word", você ainda pode alcançar seu objetivo sem loop:

sed 's/./&o/g;s/o$//'

Isso simplesmente coloca um o após cada caractere e remove o% finalo da string.

Experimente on-line .

    
por 15.10.2018 / 17:55
4

Eu verifiquei se há algum tipo de sinalizador para que isso aconteça.
Mesmo que esse comportamento esteja presente, ele será altamente consumidor de recursos.

No entanto, neste caso de uso específico, é possível ter a expressão apenas duas vezes e obter a funcionalidade necessária. ou seja, com 2 expressões repetidas de sed .

echo XX | sed -e 's/XX/XoX/g' -e 's/XX/XoX/g'     # outputs XoX
echo XXX | sed -e 's/XX/XoX/g' -e 's/XX/XoX/g'    # outputs XoXoX
echo XXXX | sed -e 's/XX/XoX/g' -e 's/XX/XoX/g'   # outputs XoXoXoX
    
por 15.10.2018 / 09:26