Gentoo Logo

Disclaimer : La versione originale di questo articolo è stata pubblicata da IBM developerWorks ed è di proprietà di Westtech Information Services. Questo documento è una versione aggiornata dell'articolo originale, e contiene numerosi miglioramenti apportati dal Gentoo Linux Documentation team.
Questo documento non è mantenuto attivamente.


Sed per esempi, Parte 2

Indice:

1.  Come ottenere ulteriori vantaggi dall'editor di testo UNIX

Sostituzione!

Diamo un'occhiata a uno dei più utili comandi di sed, il comando di sostituzione. Con esso, possiamo sostituire una particolare stringa o un pattern specificato da una espressione regolare con un'altra stringa. Ecco un esempio di un uso base di questo comando:

Codice 1.1: Il più semplice uso del comando di sostituzione

$ sed -e 's/foo/bar/' myfile.txt

Il comando sopra avrà come uscita nello stdout il contenuto del file myfile.txt, con la prima ricorrenza di 'foo' (se viene trovata) di ogni linea sostituita con la stringa 'bar'. Notare che abbiamo detto "prima ricorrenza di ogni linea", che normalmente non è ciò che si vuole. Infatti, quando si vuole fare una sostituzione, normalmente si decide di effettuarla in maniera globale. Ecco come sostituire tutte le ricorrenze in ogni linea:

Codice 1.2: Sostituire tutte le ricorrenze in ogni linea

$ sed -e 's/foo/bar/g' myfile.txt

L' opzione 'g' posta dopo l'ultimo slash indica a sed di eseguire la sostituzione in maniera globale.

Ci sono alcune cose che bisogna sapere del comando di sostituzione s///. Primo, è solo un comando: non ci sono espressioni specificate in nessuno degli esempi sopra. Ciò significa che il comando s/// può essere usato con il controllo tramite espressioni in modo da regolare in quali linee deve essere applicato, esempio:

Codice 1.3: Specificare le linee dove deve essere applicato il comando

$ sed -e '1,10s/enchantment/entrapment/g' myfile2.txt

L'esempio sopra farà in modo che tutte le ricorrenze di 'enchantment' verranno sostituite con 'entrapment', ma soltanto nelle linee da 1 a 10, incluse.

Codice 1.4: Specificare più opzioni

$ sed -e '/^$/,/^END/s/hills/mountains/g' myfile3.txt

Questo esempio cambierà 'hills' con 'mountains', ma solo nei blocchi di testo che cominciano con una linea bianca e terminano con una linea che inizia con i tre caratteri 'END', inclusivamente.

Un'altra cosa interessante del comando s/// è che ci sono molte opzioni riguardandi i separatori /. Se si sta eseguendo una sostituzione e l'espressione regolare o la stringa di sostituzione ha troppi slash, è possibile specificare un altro separatore inserendo un differente carattere dopo la 's'. Per esempio, questo sostituirà tutte le ricorrenze di /usr/local con /usr:

Codice 1.5: Sostituzione di tutte le occorrenze di una stringa

$ sed -e 's:/usr/local:/usr:g' mylist.txt

Nota: In questo esempio, è stato utilizzato ':' come separatore. Se si deve utilizzare lo stesso carattere del separatore nelle espressioni regolari, si deve farlo precedere da un backslash.

Regexp snafus

Fino ad ora, abbiamo soltanto seguito una semplice sostituzione. Anche se è comodo, è possibile anche usare le espressioni regolari. Per esempio, il seguente comando sed verificherà tutte le stringe che cominciano per '<', terminano per '>' e contengono un numero qualsiasi di caratteri tra di essi e le eliminerà (sostituisce con una stringa vuota):

Codice 1.6: Eliminare una frase specifica

$ sed -e 's/<.*>//g' myfile.html

Questo è un buon tentativo per creare uno script sed che rimuova i tag html da un file, ma non funziona correttamente, a causa di una stranezza delle espressioni regolari. Il motivo? Quando sed prova a confrontare le espressioni regolari su una linea, ricerca le corrispondenze più lunghe su quella linea.Ciò non produceva alcun comportamento indesiderato nel mio precedente articolo su sed, perché allora venivano utilizzati i comandi d e p, che erano proprio intesi a cancellare o stampare interamente la riga. Ma, utilizzando i comando s///, si manifesta una differenza enorme, perché l'intera porzione dell'espressione regolare riconosciuta deve essere sostituita con la stringa assegnata, o, nel nostro caso, cancellata. Ciò vuol dire che, nell'esempio precedente, si cambierà la linea qui sotto:

Codice 1.7: Sample HTML code

<b>This</b> is what <b>I</b> meant.

In quest'altra:

Codice 1.8: Not desired effect

meant.

Al contrario, questo è ciò che vorremmo:

Codice 1.9: Desired effect

This is what I meant.

Fortunamente, esiste una tecnica semplice per risolvere ciò. Anziché inserire una espressione regolare che indica "un carattere '<' seguiti da un numero arbitrario di di caratteri, e terminante con i carattere '>', basta produrre una espressione regolare che ricerchi un numero indefinito di caratteri non-'>', conclusa da un carattere '>'. In questo modo, si ottiene il risultato di riconoscere la ricorrenza più breve dell'espressione, anziché la più estesa. Il nuovo comando apparirà in questo modo:

Codice 1.10:

$ sed -e 's/<[^>]*>//g' myfile.html

Nell'esempio sopra esposto, il [^>]' specifica un carattere "non-'>'" e il '*' che segue completa l'espressione richiedendo "zero o più caratteri non-'>'.Verifica questo comando su un piccolo file html di esempio, assegna l'output in pipe a more e verifica i risultati.

Ricerca su gruppi di caratteri

La sintassi dell'espressione regolare '[ ]' presenta alcune altre opzioni.Per specificare un intervallo di caratteri si può usare un '-' esteso dalla prima all'ultima posizione, come segue:

Codice 1.11: Specificare un intervallo di caratteri

'[a-x]*'

In questo modo saranno riconosciuti zero o più caratteri, compresi tra 'a','b','c'...'v','w','x'. In aggiunta, la classe di caratteri '[:space:]' è utilizzabile per riconoscere gli spazi bianchi. Questa è una lista pressochè completa delle classi di caratteri disponibili:

Classi di caratteri Descrizione
[:alnum:] Alfanumerici [a-z A-Z 0-9]
[:alpha:] Alfabetici [a-z A-Z]
[:blank:] Spazi o caratteri di tabulazione
[:cntrl:] Caratteri di controllo generici
[:digit:] Cifre decimali [0-9]
[:graph:] Caratteri visibili (esclude gli spazi bianchi)
[:lower:] Lettere minuscole [a-z]
[:print:] Caratteri stampabili (esclude i caratteri di controllo)
[:punct:] Caratteri di punteggiatura
[:space:] Spazio bianco
[:upper:] Lettere maiuscole [A-Z]
[:xdigit:] Cifre esadecimali [0-9 a-f A-F]

E' vantaggioso utilizzare classi di caratteri ovunque possibile, perché si adattano meglio ai linguaggi di localizzazione non Inglesi (compresi i caratteri accentati, ove necessario).

Operazioni di sostituzione avanzata

Abbiamo visto fin qui come eseguire sia sostituzioni semplici che ragionevolmente complesse, ma sed può fare anche dell'altro. Ora, ci interessemo ad espressioni regolari riconosciute interamente o in parte, e le utilizzeremo per costruire la stringa di sostituzione. Ad esempio, suppiamo di rispondere ad un messaggio. Il seguente esempio aggiungerà a ciascuna linea il prefisso "ralph said: "

Codice 1.12: Preporre a ciscuna linea una data stringa

$ sed -e 's/.*/ralph said: &/' origmsg.txt

L'output apparirà come segue:

Codice 1.13: Output of the above command

ralph said: Hiya Jim,
ralph said:
ralph said: I sure like this sed stuff!
ralph said:

In questo esempio, si è usato il carattere '&' nella stringa di sostituzione, che ordina a sed di inserire l'intera espressione regolare selezionata. Così, tutto ciò che è stato selezionato con '.*' (ovvero l'insieme più esteso di zero o più caratteri contenuti sulla linea - che vuol dire l'intera linea) può essere inserito ovunque nella linea di sostituzione, e ciò può essere fatto anche contemporaneamente in più punti. Questo è grande, ma sed è anche più potente.

Delle meravigliose parentesi fatte con i backslash

Ancor meglio che '&', il comando /// ci permette di definire regioni nella nostra espressione regolare, a cui possiamo fare rifermento nella stringa di sostituzione. Come esempio, supponiamo di avere un file che contenga il seguente testo.

Codice 1.14: Sample text

foo bar oni
eeny meeny miny
larry curly moe
jimmy the weasel

Ora, supponiamo di voler scrivere uno script di sed che sostituisca "eeny meeny miny" con "Victor eeny-meeny Von miny", etc. Per fare ciò, scriveremo una espressione regolare che selezioni queste tre stringhe, separate da spazi:

Codice 1.15: Ricerca di espressioni regolari

'.* .* .*'

Dunque. Ora definiamo delle regioni inserendo parentesi di backslash per ciascuna regione di interesse:

Codice 1.16: Definire regioni

'\(.*\) \(.*\) \(.*\)'

Questa espressione regolare lavorerà esattamente come la nostra prima, ma riuscirà anche a definire tre regioni logiche nella nostra stringa di sostituzione. Questo è lo script finale:

Codice 1.17: Script finale

$ sed -e 's/\(.*\) \(.*\) \(.*\)/Victor \1-\2 Von \3/' myfile.txt

come si vede, stiamo facendo riferimento a ciascuna regione delimitata dalle parentesi, a partire da uno. L'output sarà il seguente:

Codice 1.18: Output del comando precedente

Victor foo-bar Von oni
Victor eeny-meeny Von miny
Victor larry-curly Von moe
Victor jimmy-the Von weasel

Ora che stiamo oramai acquisendo familiarità con sed, diventiamo capacità di eseguire operazioni di elaborazione di testi decisamente complesse con una discreta comodità. Riflettendo su come si potrebbe affrontare questo problema in un qualunque altro linguaggio di scripting di vostra preferenza, sapreste indicare forse uno strumento che realizzi la soluzione in una linea soltanto?

Altre cose non ancora citate

Nella creazione di script più complessi, sorge la necessità di eseguire più di un comando in succesione. Ci sono diversi modi per realizzarla. Primo, si può utilizzare il punto e virgola tra i comandi. Per esempio, questa procedura esegue il comando '=', che indica a sed di stampare il numero di linea, e successivamente il comando p, che richiede esplicitamente di stampare ls linea (poichè siamo in modalità -n):

Codice 1.19: Primo metodo, il punto e virgola

$ sed -n -e '=;p' myfile.txt

Se vengono specificati due o più comandi, ciascuno sarà applicato (nell'ordine) a ciascuna linea del file. Nell'esempio sopra, prima viene applicato il comando '=' alla linea due, e poi il comando p. Quindi, sed procede alla seconda linea e ripete il processo. Sebbene il punto e virgola sia comodo, vi sono istruzioni in cui non funziona. Una seconda alternativa è l'uso di due opzioni -e per specificare i due comandi separati:

Codice 1.20: Secondo metodo, l'opzione multipla -e

$ sed -n -e '=' -e 'p' myfile.txt

Anche qui, con comandi di inserimento e concatenazione complessi, anche l'opzione '-e' potrebbe non essere d'aiuto. Per complessi script multilinea, la soluzione migliore è quella di inserire i comandi necessari in un file separato. In questo caso, si fa riferimento al file separato in questo modo:

Codice 1.21: Terzo metodo, file di comandi esterno

$ sed -n -f mycommands.sed myfile.txt

Questo sistema, sebbene decisamente meno conveniente, funziona sempre.

Comandi multipli per un singolo indirizzo

Talora, si potrebbe aver desiderio di specificare comandi multipli da applicare ad un singolo indirizzo. Questo accade con particolare frequenza nell'eseguire numerose trasformazioni di parole o sintassi nei file sorgente. Per eseguire comandi multipli su un indirizzo, introdurre i comandi di sed in un file, costruendo il gruppo di comandi '{ }' come segue:

Codice 1.22: Eseguire comandi multipli su di un unico indirizzo

1,20{
	s/[Ll]inux/GNU\/Linux/g
	s/samba/Samba/g
	s/posix/POSIX/g
}

L'esempio precedente applicherà tre comandi di sostituzione dalla linea 1 alla 20, estremi inclusi. È possibile anche fare uso di espressioni regolari indirizzate, o una combinazione dei due:

Codice 1.23: Combinazione di entrambi i metodi

1,/^END/{
        s/[Ll]inux/GNU\/Linux/g
        s/samba/Samba/g
        s/posix/POSIX/g
	p
}

Questo esempio si applica a tutti i comandi tra '{ }' le linee inizianti per 1 fino alla linea che inizia con le lettere "END", oppure una terminazione di file se la stringa non viene rinvenuta nel file sorgente.

Accodare, inserire e modificare linee

Preparato che sia lo script di sed in un file separato, possiamo avvantaggiarci dei comandi per accodare, inserire e modificare linee. Questi comandi inseriranno la linea corrente, o la sostituiranno nel cosidetto pattern space, che è l'area attiva di memoria in cui è contenuta la riga su cui sed sta attualmente lavorando. Gli stessi possono essere anche usati per inserire linee multiple in output. Il comando di inserimento di linea è utilizzato come segue:

Codice 1.24: Uso del comando di inserimento di line

i\
Questa linea sarà inserita prima di ciascuna linea

Se non viene specificato un indirizzo per quest comando, sarà applicato a ciascuna linea e produrrà un output che appare come il seguente:

Codice 1.25: Output of the above command

Questa linea sarà inserita prima di ciascuna linea
linea 1 qui
Questa linea sarà inserita prima di ciascuna linea
linea 2 qui
Questa linea sarà inserita prima di ciascuna linea
linea 3 qui
Questa linea sarà inserita prima di ciascuna linea
linea 4 qui

Desiderando inserire linee multiple prima della linea corrente, potrete aggiungere linee addizionali concatenando un backslash alla linea precedente, come qui:

Codice 1.26: Inserire linee multiple prima di quella corrente

i\
inserisci questa linea\
e questa\
e questa\
e, uh, anche questa, poi.

Il comando di concatenazione lavora analogamente, ma inserirà la linea o le linee, nel pattern space. E' usato come segue:

Codice 1.27: Concatenare linee dopo quella corrente

a\
Inserisci questa linea dopo ogni linea., Grazie! :)

D'altra parte, il comando di sostituzione di linea rimpiazzerà la linea corrente nel pattern space, ed è usato come segue:

Siccome i comandi di concatenazione, inserimento e modifica devono essere eseguiti su linee multiple, avrai desiderio di inserirli in appositi script di testo per sed e chiamare l'esecuzione con sed attraverso l'opzione '-f'. Utilizzare metodi alternativi per passare i comandi a sed può generare problemi.

La prossima volta

La prossima volta, nell'articolo finale di questa serie su sed, vi mostrerò molti ottimi esempi di uso di sed in numerose situazioni operative. E non solo mostrerò cosa faccia lo script, ma perchè lo fa. Ciò fatto, sarete voi a trovare nuove splendide idee per usare sed in vari progetti. Io starò a guardare voi, allora!

2.  Risorse

Collegamenti utili



Stampa

Aggiornato il 29 giugno 2012

Oggetto: Sed è un potente e compatto editor di flusso. In questo articolo, il secondo della serie, Daniel mostra come eseguire la sostituzione di stringhe; come creare script sed più vasti; e usare i comandi sed accoda, inserisci e cambia riga.

Daniel Robbins
Autore

Marco Clocchiatti
Traduttore

Donate to support our development efforts.

Copyright 2001-2014 Gentoo Foundation, Inc. Questions, Comments? Contact us.