Come sostituire ricorsivamente i personaggi con sed?


13

È possibile sostituire ricorsivamente le occorrenze di una sequenza di caratteri senza iterare nuovamente sulla stessa sequenza?

Eseguendo un sedcome nei seguenti scenari posso ottenere l'output menzionato.

$ 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  

Tuttavia, mi aspetto che l'output segua il seguente comportamento.

Ingresso:

XX
XXX
XXXX

Uscita prevista:

XoX
XoXoX
XoXoXoX

È possibile raggiungere il comportamento previsto con sed solo?

Risposte:


24

Tu puoi fare:

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

Con:

  • -e ':loop' : Crea un'etichetta "loop"
  • -e 't loop' : Passa all'etichetta "loop" se la sostituzione precedente ha avuto esito positivo

10

In questo caso particolare sarebbe utile guardare avanti o guardare dietro. Penso che GNU sednon supporti questi. Con perl:

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

Puoi anche usare lookbehind e lookahead come:

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

Dove:

(?<=X)è un lookbehind positivo, un'asserzione di lunghezza zero che si assicura che abbiamo una X prima della posizione corrente
(?=X)è un lookahead positivo, un'asserzione di lunghezza zero che si assicura che abbiamo una X dopo la posizione corrente

Utilizzando in un perl one-liner:

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

Dove:

-p fa sì che Perl assuma un ciclo attorno al programma con una stampa implicita della riga corrente


5

La risposta ciclica è il modo generale di fare ciò che stai chiedendo.

Tuttavia, nel caso dei tuoi dati, supponendo che tu stia utilizzando GNU puoi semplicemente fare:

sed 's/\B/o/g'

Le opzioni \be \Bsono estensioni regex :

  • \b abbina i confini delle parole, ovvero la transizione da un carattere "parola" a un carattere "non parola" o viceversa
  • \Bcorrisponde al contrario di \b. cioè le lacune "dentro" le parole. Questo ci consente di inserire caratteri all'interno di una parola ma non all'esterno, come richiesto.

Provalo online .

Ciò presuppone che i caratteri di input siano in realtà tutti i caratteri "word".


In alternativa, se non si dispone di GNU sed o se i caratteri di input non sono tutti caratteri "word", è comunque possibile raggiungere il proprio obiettivo senza loop:

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

Questo posiziona semplicemente un ocarattere dopo ogni carattere e quindi rimuove il finale odalla stringa.

Provalo online .


1
Ciò presuppone che le stringhe di input siano costituite da un numero di Xe nient'altro. Entrambe le soluzioni falliscono se sono presenti altri personaggi ...
AnoE

@AnoE Nel secondo esempio, che è fissato con una semplice sostituzione di Xby .. Vedi modifica.
Trauma digitale

Non equivalente al caso fornito dall'OP. Ha dato le RE esatte di cui ha bisogno (cambia le occorrenze di XX in una stringa). Le tue versioni danno lo stesso risultato del suo solo per le stesse stringhe di input che ha dato; non per stringhe di input generiche.
AnoE

4

Ho verificato se esiste una sorta di bandiera per farlo accadere.
Anche se quel comportamento fosse lì, consumerà molto risorse.

Tuttavia, in questo particolare caso d'uso, è possibile avere l'espressione solo due volte e ottenere la funzionalità richiesta. cioè con 2 ripetizionised espressioni .

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.