Como substituir recursivamente caracteres por sed?

13

É possível substituir as ocorrências de uma sequência de caracteres recursivamente sem repetir a mesma sequência?

Ao executar um sedcomo nos seguintes cenários, 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, espero que a saída siga o seguinte comportamento.

Entrada:

XX
XXX
XXXX

Saída esperada:

XoX
XoXoX
XoXoXoX

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

Ishan Madhusanka
fonte

Respostas:

24

Você pode fazer:

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

Com:

  • -e ':loop' : Criar um rótulo "loop"
  • -e 't loop' : Pule para o rótulo "loop" se a substituição anterior foi bem-sucedida
Gohu
fonte
10

Nesse caso em particular, olhar para frente ou para trás seria útil. Eu acho que o GNU sednã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)é uma observação positiva, uma afirmação de comprimento zero que garante que tenhamos um X antes da posição atual
(?=X)é uma aparência positiva, uma afirmação de comprimento zero que garante que tenhamos um X após a posição atual

Usando em um liner perl:

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

Onde:

-p faz com que o Perl assuma um loop ao redor do programa com uma impressão implícita da linha atual

Toto
fonte
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:

sed 's/\B/o/g'

As opções \be \Bsão extensões de regex :

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

Experimente online .

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


Como alternativa, se você não possui o GNU sed, ou se os caracteres de entrada não são todos caracteres "word", ainda é possível atingir seu objetivo sem fazer loop:

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

Isso simplesmente coloca um ocaractere depois de cada caractere e remove a final oda string.

Experimente online .

Trauma Digital
fonte
1
Isso pressupõe que as seqüências de entrada consistem em algum número Xe nada mais. Ambas as soluções falham se houver outros caracteres presentes ...
AnoE
@AnoE No segundo exemplo, isso é corrigido com uma simples substituição de Xpor .. Por favor, veja editar.
Digital Trauma
Não é equivalente ao caso que o OP deu. Ele forneceu as RE exatas que ele precisa (alterar ocorrências de XX em uma sequência). Suas versões dão apenas o mesmo resultado que o dele para as mesmas seqüências de entrada que ele forneceu; não para cadeias de entrada genéricas.
AnoE 16/10
4

Eu verifiquei se existe algum tipo de bandeira para fazer isso acontecer.
Mesmo que esse comportamento existisse, ele consumiria muito recursos.

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

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
Ishan Madhusanka
fonte