Introduzione

Abbastanza spesso quando si lavora con file di testo è necessario trovare e sostituire stringhe di testo in uno o più file.

sed è una stream editor. Può eseguire manipolazioni di testo di base su file e flussi di input come pipeline. Con sed puoi cercare, trovare e sostituire, inserire ed eliminare parole e righe. Supporta espressioni regolari di base ed estese che consentono di abbinare modelli complessi.

In questo articolo, parleremo di come trovare e sostituire le stringhe con sed. Ti mostreremo anche come eseguire una ricerca ricorsiva e sostituirla.

Trova e sostituisci

Esistono diverse versioni di sed, con alcune differenze funzionali tra loro. macOS utilizza la versione BSD e la maggior parte delle distribuzioni Linux viene fornita con GNU sed preinstallato di default. Useremo la versione GNU.

La forma generale di ricerca e sostituzione del testo utilizzando sed assume la forma seguente:

sed -i 's/SEARCH_REGEX/REPLACEMENT/g' INPUTFILE
  • -i- Per impostazione predefinita sed scrive l'output nell'output standard. Questa opzione indica sed di modificare i file in atto. Se viene fornita un'estensione (ex -i.bak) verrà creato un backup del file originale.
  • s - Il comando sostitutivo, probabilmente il comando più utilizzato in sed.
  • / / /- Carattere delimitatore. Può essere qualsiasi carattere ma di solito  viene utilizzato il carattere slash (/).
  • SEARCH_REGEX - Stringa normale o espressione regolare da cercare.
  • REPLACEMENT - La stringa di sostituzione.
  • g- Flag di sostituzione globale. Per impostazione predefinita, sed legge il file riga per riga e modifica solo la prima occorrenza di SEARCH_REGEX su una riga. Quando viene fornito il flag di sostituzione, tutte le occorrenze verranno sostituite.
  • INPUTFILE - Il nome del file su cui si desidera eseguire il comando.

È buona norma inserire virgolette attorno all'argomento in modo che i meta-characters della shell non si espandano.

Vediamo esempi su come utilizzare il comando sed per cercare e sostituire il testo nei file con alcune delle sue opzioni e flag più comunemente utilizzate.

A scopo dimostrativo, utilizzeremo il seguente file file.txt:

123 Foo foo foo 
foo /bin/bash Ubuntu foobar 456

Se si omette il flag g, verrà sostituita solo la prima istanza della stringa di ricerca in ciascuna riga:

sed -i 's/foo/linux/' file.txt
123 Foo linux foo 
linux /bin/bash Ubuntu foobar 456

Con il flag di sostituzione globale sed sostituisce tutte le occorrenze del modello di ricerca:

sed -i 's/foo/linux/g' file.txt
123 Foo linux linux
linux /bin/bash Ubuntu linuxbar 456

Come avrai notato, nell'esempio precedente viene sostituita anche la sottostringa foo all'interno della stringa foobar. Se questo non è il comportamento desiderato, utilizzare l'espressione word-boundery (\b) su entrambe le estremità della stringa di ricerca. Questo assicura che le parole parziali non siano abbinate.

sed -i 's/\bfoo\b/linux/g' file.txt
123 Foo linux linux
linux /bin/bash Ubuntu foobar 456

Per rendere insensibile la corrispondenza del motivo, utilizzare il flag I. Nell'esempio riportato di seguito stiamo usando sia il flag g che il flag I:

sed -i 's/foo/linux/gI' file.txt
123 linux linux linux 
linux /bin/bash Ubuntu linuxbar 456

Se vuoi trovare e sostituire una stringa che contiene il carattere delimitatore (/) dovrai usare la barra rovesciata (\) per sfuggire alla barra. Ad esempio, per sostituire /bin/bash con /usr/bin/zsh te sarebbe usare

sed -i 's/\/bin\/bash/\/usr\/bin\/zsh/g' file.txt

L'opzione più semplice e molto più leggibile è usare un altro carattere delimitatore. Molte persone usano la barra verticale pipe | o i due punti : ma puoi usare qualsiasi altro carattere:

sed -i 's|/bin/bash|/usr/bin/zsh|g' file.txt
123 Foo foo foo 
foo /usr/bin/zsh Ubuntu foobar 456

Puoi anche usare espressioni regolari. Ad esempio, per cercare tutti i numeri di 3 cifre e sostituirli con la stringa number useresti:

sed -i 's/\b[0-9]\{3\}\b/number/g' file.txt
number Foo foo foo 
foo /bin/bash demo foobar number

Un'altra caratteristica utile di sed è che puoi usare il carattere e commerciale & che corrisponde al modello abbinato. Il personaggio può essere usato più volte.

Ad esempio, se si desidera aggiungere parentesi graffe {} attorno a ciascun numero di 3 cifre, digitare:

sed -i 's/\b[0-9]\{3\}\b/{&}/g' file.txt
{123} Foo foo foo 
foo /bin/bash demo foobar {456}

Ultimo ma non meno importante, è sempre una buona idea fare un backup quando si modifica un file sed. Per fare ciò basta fornire un'estensione per l'opzione -i. Ad esempio, per modificare file.txt e salvare il file originale come file.txt.bak si usa:

sed -i.bak 's/foo/linux/g' file.txt

Se si desidera assicurarsi che il backup sia stato creato, elencare i file con il comando ls:

ls
file.txt file.txt.bak

Trova e sostituisci ricorsivo

A volte si desidera cercare ricorsivamente nelle directory i file contenenti una stringa e sostituire la stringa in tutti i file. Questo può essere fatto usando comandi come find o grep per trovare ricorsivamente i file nella directory e reindirizzare i nomi dei file sed.

Il seguente comando cercherà ricorsivamente i file nella directory di lavoro corrente e passerà i nomi dei file in sed.

find . -type f -exec sed -i 's/foo/bar/g' {} +

Per evitare problemi con i file che contengono spazio nei loro nomi, utilizzare l'opzione -print0 che indica al comando find di stampare il nome del file, seguito da un carattere null e reindirizzare l'output verso sed utilizzando xargs -0:

find . -type f -print0 | xargs -0 sed -i 's/foo/bar/g'

Per escludere una directory utilizzare l'opzione -not -path. Ad esempio, se si sta sostituendo una stringa nel repository git locale per escludere tutti i file che iniziano con dot (.), utilizzare:

find . -type f -not -path '*/\.*' -print0 | xargs -0 sed -i 's/foo/bar/g'

Se si desidera cercare e sostituire il testo solo su file con estensione specifica, utilizzare:

find . -type f -name "*.md" -print0 | xargs -0 sed -i 's/foo/bar/g'

Un'altra opzione è utilizzare il comando grep per trovare ricorsivamente tutti i file contenenti il ​​modello di ricerca e quindi reindirizzare i nomi dei file a sed:

grep -rlZ 'foo' . | xargs -0 sed -i.bak 's/foo/bar/g'

Conclusione

Per saperne di più sui comandi sed, opzioni e flag, visita il manuale GNU sed.