Ad oggi non esiste sistema GNU/Linux che non sia dotato delle utility di elaborazione di testo: sed, awk. In specifico, in questo post si tratterà una panoramica generale su sed.

Cos’è:

Sed è un editor che si occupa della manipolazione di file di testo in modalità non interattiva. Come tutti gli editor di testo tale manipolazione opera attraverso la modifica e trasformazione di un file. La differenza però con gli editor comuni sta nel fatto che sed opera in standard non interattivo ed agisce in ordine: ricevendo un testo/file/stdin come input che poi sarà opportunamente trattato/modificato attraverso i parametri scelti con la specificazione delle righe, una alla volta; terminata questa fase si preoccuperà di inviare il risultato ad un file/stdout. Sostanzialmente più che un editor vero e proprio funge da filtro trasformatore.

NB: l’utilizzo di sed in tutte le sue varianti presuppone una discreta/media conoscenza delle regexp. A tal proposito è consigliata la lettura del post relativo; le espressioni regolari: regexp.

Sintassi del comando:

~$ sed --h

NB: sono elencate e commentate le opzioni generali di utilizzo. In specifico, le ritenute fondamentali.

-n, --quiet, --silent                  # evita che venga mostrato ciò che viene fatto "modalità silent" (uguale al parametro --quiet e/o -n)

-e script, --expression=script         # aggiunge lo script definito da riga di comando ai comandi da eseguire. Script definito all'interno degli apici '' e/o "" (più comandi sono separati dal punto e virgola ";")

-f script.sh, --file=script.sh         # aggiunge questa volta il contenuto del file script.sh ai comandi da eseguire.
-i[SUFFIX], --in-place[=SUFFIX]  # scriverà il risultato direttamente sul file originale modificandolo. 

NB: l'opzione -i può essere associata ad un suffisso che verrà preso in considerazione per creare un file backup come dagli esempi:
sed -i                  # modificherà il file originario senza creare alcun backup
sed --in-place          # identico all'esempio precedente -i
sed -i.backup           # modificherà il file originario creando salvando una copia di backup dello stesso
sed --in-place=.backup  # come l'esempio precedente  -i.backup

Comandi operatori:

NB: sed tende a suddividere i comandi generali secondo due categorie. La prima definisce quali e che tipo di operazioni eseguire. La seconda categoria invece racchiude le righe su cui operare e quindi delle specifiche delle stesse in termini di numero riga, classe, etc. Analizziamo in dettaglio:

p                      # "print" stampa la riga o il file
NB: Questa opzione è particolare. Per comprenderla analizzare gli esempi seguenti:
sed -e '' file     # stampa il file e non esegue nessuna operazione definita negli apici ''
sed -n -e '' file  # non esegue nessuna operazione e non stampa il file (opzione -n)
sed -e 'p' file    # stampa il file "operazione definita da p" (ogni riga trovata due volte)
sed -n -e 'p' file # come sopra ma questa volta associa l'operazione print alla modalità silent (stampa quindi una sola volta ogni riga)
d                      # "delete" elimina la riga

/regexp/p              # stampa solo le righe corrispondenti alla regexp espressa

/regexp/d              # elimina solo le righe corrispondenti alla regexp espressa
s/testo/sostituzione/  # sostituisce la stringa "testo" con "sostituzione" 

y/abcd/ABCD/           # come sopra ma analizza tutti i caratteri presi come riferimento nel modello1 "abcd" con i corrispondenti del modello2 "ABCD". Opera in sostituzione solo sui caratteri corrispondenti. Nell'esempio da abcd minuscolo a MAIUSCOLO.

g                      # definisce il parametro "global". Agisce su tutte le verifiche d'occorrenza di ogni riga trovata.Utile nelle sostituzioni recursive su più righe e/o per lasciare inalterate il resto delle righe (vedi più avanti nel post).

s/testo/sostituzione/g # sostituisce tutte le parole testo con sostituzione. Agisce su tutto il file e non si ferma solo alla prima riga trovata.

NB: l’ordine di suddivisione comandi vi risulterà inverso a quello descritto precedentemente. Ma vi rendererà il tutto di più facile comprensione.

Selettori di riga e specifiche operatori:

3                      # corrisponde alla terza riga

3p                     # stampa la terza riga di default due volte e il resto una sola volta (se associato ad opzione -n precedentemente descritta la stamperà una sola volta)

,                      # "virgola" corrisponde al range da prima a dopo il simbolo. Esempio: 1,3 (le righe da 1 a 3)

~                      # "infinito" è usato per definire ciò che c'è prima come partenza e ciò che c'è dopo come fine. Esempio: 1~3 (parte da riga 1 e poi ogni 3)

Esempi comandi,selettori di riga e specifiche operatori:

1,4d                    # le prime quattro righe sono eliminate

2,4d                    # le righe da 2 a 4 sono eliminate

5,10p                   # le righe da 5 a 10 sono stampate

1~2p                    # le righe dispari sono stampate (parte dalla prima riga e poi ogni due)

2~2p                    # le righe pari sono stampate (parte dalla seconda riga e poi ogni due)

4~3p                    # stampa le righe 4 7 10 13 ... etc (parte dalla quarta riga e poi ogni 3)

4~4d                    # elimina le righe 4 8 12 16 ... etc (parte dalla quarta riga e poi ogni 4)

/regexp/,/regexp-sost/p # stampa tutte le righe comprese tra la prima regexp espressa e la seconda.

2~2s/pippo/PIPPO/g      # sostituisce la parola pippo con PIPPO ma solo agendo sulle righe pari.Come da esempio la prima parte definisce le righe e la seconda l'operazione.

/^$/d                   # cancella tutte le righe vuote

/Pippo/p                # stampa tutte le righe in cui è presente Pippo (da usare con opzione -n)

/PIPPO/d                # cancella tutte le righe in cui è presente PIPPO e procede alla cancellazione del resto della riga in cui è presente la parola

s/PIPPO//g              # cancella la parola PIPPO in ogni riga sostituendola con nulla e lasciando il resto della riga intatto.

IMPORTANTE: questo esempio di doppio slash applicato alla frase seguente chiarisce il significato di una sostituzione applicata con nulla seguita dal simbolo g "global" che lascierà intatto il resto della riga.

NB: L’espressione di tipo s/PIPPO,//g nella riga io adoro PIPPO, pluto e paperone opportunamente creata modificherà la frase in modo da risultare come io adoro pluto e paperone.

Prova su campo con sed:

~$ echo io adoro PIPPO, pluto e paperone >> prova.txt
~$ cat prova.txt
io adoro PIPPO, pluto e paperone

~$ sed -i -e 's/PIPPO,//g' prova.txt
~$ cat prova.txt
io adoro pluto e paperone

NB: i più temerari ricorderanno leggendo il post precedente su regexp che spesso poteva capitare di dover usufruire dello slash / come simbolo da ricercare che quindi era considerato come un carattere speciale (come lo possono essere il punto,la virgola,etc) e che quindi per arrivare allo scopo era necessario utilizzare lo slash rovesciato ”“ seguito dal carattere speciale da ricercare. Ovviamente in molte regexp di tipo sostitutivo ‘s// ‘ potrebbe risultare fastidioso quindi scrivere continuamente / per effettuare una sostituzione come lo era l’esempio:

s/Linux/GNU/Linux/g    # lo slash rovesciato  tratta il seguente slash normale / come un carattere speciale. Altrimenti sarebbe considerato come fine regexp.Sostituzione della parola Linux con GNU/Linux in ogni riga.

Per evitare questa cosa si può scegliere di sostituire gli slash con altri limitatori come ad esempio i due punti “ : “come da esempio seguente.

s:regexp:sostituzione:g
In specifico per provare creaiamo un file prova.txt come da esempio:

~$ echo Linux >> prova.txt

~$ cat prova.txt
Linux

~$ sed -i -e 's:Linux:GNU/Linux:g' prova.txt

~$ cat prova.txt
GNU/Linux

Considerazioni: come sempre è utile capire che la potenza è nascosta nelle cose apparentemente banali. Bisogna tener conto che l’utilizzo di sed è fondamentale per la creazione di una pipe e che molto spesso è associato ad altre utility del suo genere per operazioni complesse all’interno di scripts. Il miglior modo per imparare quindi è come al solito: cominciare e provare, provare, provare. Saluti.

NB: questo post è una sorta di sommario e di appunti e quindi i riferimenti e la linea generale di trattamento del post, come nel precedente su regexp, sono legati al link ufficiale di riferimento su Advanced Bash-Scripting Guide.

# End