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
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: Replacing all the occurences of one string with another one |
$ 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 nonInglesi (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
|