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
É 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?
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
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. 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.
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