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 1
1.
Familiarizzare con il potente editor UNIX
Scegliere un editor
Nel mondo UNIX, abbiamo molte opzioni per quanto riguarda l'editing dei file.
Basti pensare a vi, emacs, jed e tanti altri. Noi tutti abbiamo il nostro
preferito che conosciamo e amiamo. Con il nostro editor fidato, siamo in grado
di svolgere tutti i compiti di amministrazione e programmazione sotto UNIX.
Sebbene gli editor interattivi sono ottimi, essi hanno delle limitazioni.
Benchè la loro natura interattiva può essere un punto di forza, essa può
essere anche un punto debole. Basti pensare a quando bisogna eseguire azioni
simili in un gruppo di file. In questi casi, istintivamente, si prende il
proprio editor e si eseguono un'infinità di modifiche banali e ripetitive,
perdendo molto tempo. Esiste però una via migliore.
Enter sed
Sarebbe piacevole se si potesse automatizzare il processi di editing dei file,
in modo da poter eseguire il "batch" editing dei file o creare script con la
capacità di eseguire sofisticate modifiche ai file esistenti. Fortunatamente
per questo tipo di situazioni c'è una via migliore: sed.
Sed è un leggero editor di flusso il quale è incluso con quasi tutti i tipi
di UNIX, Linux compreso. Sed ha molte caratteristiche interessanti. Prima di
tutto, è molto leggero, tipicamente molto più leggero di tutti gli altri
linguaggi di scripting. Secondo, perchè è un editor di flusso, esso può
eseguire modifiche ai dati ricevuti dallo standard input, per esempio da una
pipeline. Così, non si hanno dati che devono essere editati immagazzinati nel
nostro hard-disk. Questo perchè i dati possono semplicemente e facilmente
essere inviati a sed tramite una pipe, è molto facile usare sed come parte di
una lunga e complessa pipeline in uno script shell.
GNU sed
Fortunatamente per gli utenti Linux, una delle migliori versioni di sed
esistenti è GNU sed, che è correntemente arrivato alla versone 3.02. Ogni
distribuzione Linux ha sed, o almeno dovrebbe averlo. GNU sed è popolare non
solo perchè i suoi sorgenti sono liberamente distribuibili, ma perchè sembra
avere molta manegevolezza e molte estensioni per risparmiare tempo rispetto al
sed POSIX standard. GNU sed inoltre non soffre delle limitazioni che le
precedenti proprietarie versioni di sed hanno avuto, come ad esempio una
lunghezza delle stringhe limitata -- GNU sed lavora con linee di qualsiasi
lunghezza.
Il nuovo GNU sed
Facendo ricerche per questo articolo, ho notato che online molti affezionati a
sed si riferiscono a GNU sed 3.02a. Stranamente, sed 3.02a non si trovava su
ftp://ftp.gnu.org (vedi Resources per
questi link), così ho dovuto cercarlo altrove. L'ho trovato su
ftp://alpha.gnu.org, in /pub/sed. L'ho scaricato
correttamente, compilato e installato, solo per scoprire un po' di minuti più
tardi che la versione più recente è la 3.02.80 -- ed è possibile trovarne i
sorgenti dopo quelli della 3.02a, su ftp://alpha.gnu.org. Dopo aver
installato GNU sed 3.02.80, mi sono trovato finalmente pronto a partire.
The right sed
In questa serie, verrà utilizzato GNU sed 3.02.80. Alcuni (ma molto pochi)
degli esempi più avanzati che si troveranno nei miei imminenti articoli di
proseguimento non funzionerano con GNU sed 3.02 o 3.02a. Se si sta usando una
versione di sed non GNU, i risultati possono variare. Allora perchè non
installare GNU sed 3.02.80 adesso? Non soltanto si è pronti per il resto della
serie, ma si potrà utilizzare il miglior sed esistente!
Esempio su Sed
Sed lavora eseguendo un qualsiasi numero di azioni di editing specificate
dall'utente sui dati in input. Sed è line-based, quindi i comandi vengono
eseguiti riga per riga in ordine. E, Sed scrive il risultato nello standard
output (stdout); non modifica nessun file.
Diamo un'occhiata ad alcuni esempi. I primi saranno un po' strani perchè io li
sto usando per illustrare come lavora sed piuttosto che eseguire task utili.
Comunque, se si è nuovi con sed, è molto importante che si capiscono. Ecco il
nostro primo esempio:
Codice 1.1: Esempio di uso di Sed |
$ sed -e 'd' /etc/services
|
Lanciando questo comando, non si riceverà assolutamente nessun output.
Adesso, cosa è successo? In questo esempio, abbiamo lanciato sed con un
comando di editing, d. Sed ha aperto il file
/etc/services, ha letto una riga nel suo pattern buffer,
eseguendo il nostro comando di editing ("elimina riga"), e infine ha stampato
il pattern buffer (che è vuoto). In seguito ha ripetuto questi passi per ogni
riga successiva. Ciò ha provocato l'uscita di nessun output, perchè il
comando d ha eliminato ogni singola riga presente nel pattern buffer!
Ci sono un po' di cose da notare in questo esempio. Primo,
/etc/services non è stato modificato affatto. Questo perchè, di
nuovo, sed legge soltanto dal file specificato da riga di comnado, usandolo
come input -- esso non tenta di modificare il file. La seconda cosa da notere
è che sed è line-oriented. Il comando d non indica semplicemente a sed
di eliminare tutti i dati in arrivo. Invece, sed legge ogni linea di
/etc/services una ad una nel suo buffer interno, chiamato pattern buffer. Una
volta che la linea è stata letta nel pattern buffer, esso esegue il comando
d e stampa il contenuto del pattern buffer (in questo esempio nulla).
Dopo, mostrerò come usare come usare gli indirizzi di intervallo per
controllare su quali linee un comando viene applicato -- ma in assenza di
indirizzi, il comando è applicato a tutte le linee.
La terza cosa da notare è l'uso del quoting singolo attorno al comando
d. È una buona idea quella di abituarsi ad usare il quoting singolo per
i comandi sed, in modo tale che la shell expansion è disabilitata.
Un'altro esempio su Sed
Qui c'è un esempio su come usare sed per eliminare la prima linea del file
/etc/services dallo stream output:
Codice 1.2: Un altro esempio su Sed |
$ sed -e '1d' /etc/services | more
|
Come si può vedere, questo comando è molto simile al precedente d,
tranne che è preceduto da 1. Se si ha pensato che il numero 1
si riferisce alla linea numero 1, si ha ragione. Mentre nel primo esempio, è
stato usato d da solo, adesso è stato preceduto da una espressione
numerica. Usando le espressioni, si può indicare a sed di editare solo una
particolare linea o un particolare gruppo di linee.
Espressioni di campo
Adesso, diamo un'occhiata su come specificare una espressione di campo. In
questo esempio, sed eliminerà le linee 1-10 dall'output:
Codice 1.3: Specificare una espressione di campo |
$ sed -e '1,10d' /etc/services | more
|
Quando si separano due espressioni con una virgola, sed applicherà il comando
seguente al campo che comincia dalla prima espressione e termina alla seconda.
In questo esempio, il comando d verrà applicato dalla linea 1 alla 10,
incluse. Tutte le altre linee verranno ignorate.
Address con espressioni regolari
Ora è il tempo di fare quale esempio più utile. Vediamo come fare se si vuole
vedere il contenuto del file /etc/services, ma non si è
interessati a vedere tutti i commenti inclusi. Come si sa, è possibile
includere commenti nel file /etc/services facendo iniziare le
linee con il carattere '#'. Per evitare i commenti, dobbiamo dire a sed di
eliminare le linee che cominciano con un '#'. Ecco come fare:
Codice 1.4: Eliminare le righe che iniziano con # |
$ sed -e '/^#/d' /etc/services | more
|
Provare questo esempio e vedere cosa succede. Si potrà notare che sed esegue
il compito desiderato correttamente. Adesso, vediamo un po' cosa è successo.
Per capire il comando '/^#/d', prima dobbiamo sezionarlo. Primo, rimuoviamo la
'd' -- si tratta dello stesso comando di eliminazione di riga utilizzato in
precedenza. La nuova aggiunta è la parte '/^#/', la quale è un tipo di
espressione regolare. Le espressioni regolari sono sempre circondate da slash.
Esse specificano un pattern, e il comando che immediatamente precede una
espressione regolare verrà applicato solamente alle linee che corrispondono al
particolare pattern specificato.
Quindi, '/^#/' è una espressione regolare. Ma che cosa fa? Evidentemente, è
il caso di fare un ripasso delle espressione regolari.
Ripasso espressioni regolari
È possibile utilizzare le espressioni regolari per esprimere pattern che si
possono trovare nel testo. È molto simile, anche se non indentico, al
carattere '*' usato nella shell. Ecco i caratteri speciali che possono essere
utilizzati nelle espressioni regolari:
| Carattere |
Descrizione |
| ^ |
Indica l'inizio di un riga |
| $ |
Indica la fine di una riga |
| . |
Indica un qualsiasi singolo carattere |
| * |
Indica zero o più ripetizioni del carattere precedente |
| [ ] |
Indica tutti i caratteri all'interno di [ ] |
Probabilmente la via migliore per fare pratica con le espressioni regolari è
quella di vedere qualche esempio. Tutti questi esempi sono accettati da sed
come valide espressioni usate alla sinistra di un comando. Eccoli:
| Espressione regolare |
Descrizione |
| /./ |
Restituirà tutte le linee che contengono un solo carattere |
| /../ |
Restituirà tutte le linee che contengono due soli caratteri |
| /^#/ |
Restituirà tutte le linee che iniziano con '#' |
| /^$/ |
Restituirà tutte le linee vuote |
| /}^/ |
Restituirà tutte le linee che terminano con '}' (senza spazi) |
| /} *^/ |
Restituirà tutte le linee che terminano con '}' seguito da zero o più
spazi
|
| /[abc]/ |
Restituirà tutte le linee che contengono 'a', 'b', o 'c' |
| /^[abc]/ |
Restituirà tutte le linee che cominciano con 'a', 'b', o 'c' |
Io consiglio di provare molti di questi esempi. Perdere un po' di tempo per
diventare familiari con le espressioni regolari e provare a creare espressioni
regolari personali. Ecco come usarle:
Codice 1.5: Modo corretto di usare le regexp |
$ sed -e '/regexp/d' /path/to/my/test/file | more
|
In questo modo sed eliminerà tutte le linee corrispondenti all'espressione.
Comunque è più facile impararle facendo in modo che sed elimini tutte le
linee che non corrispondono piuttosto che il contrario. È possibile fare ciò
con il comando seguente:
Codice 1.6: Stampare le linee che soddisfano le espressioni regolari |
$ sed -n -e '/regexp/p' /path/to/my/test/file | more
|
Notare che l'opzione '-n' indica a sed di non stampare le righe se non è stato
esplicitamente dichiarato. Inoltre il comando d è stato sostituito con
p, il quale indica esplicitamente a sed di stampare le righe
corrispondenti. Adesso, quindi, verranno stampate solo le linee che soddisfano
l'espressione.
Nuovamente sulle espressioni
Fino ad ora, abbiamo dato un'occhiata a vari tipi di espressioni. Ma ci sono
ancora altre possibilità. È possibile specificare due espressioni regolari
separete da una virgola, esse indicano a sed di considerare le righe che
soddisfano la prima espressione, quelle che soddisfano la seconda e tutte le
righe comprese tra le due. Per esempio, il comando seguente stamperà un blocco
di testo che inizia con una linea contentente "BEGIN" e termina con "END:
Codice 1.7: Stampare un blocco di testo desiderato |
$ sed -n -e '/BEGIN/,/END/p' /my/test/file | more
|
Se "BEGIN" non viene trovato, nessun dato verrà stampato. E, se "BEGIN" viene
trovato ma non viene trovata nessuna riga contenente "END" dopo di esso,
allora verranno stampate tutte le righe consecutive. Ciò accade perchè sed è
un editor di flusso -- esso non sa se la riga contenente "END" apparirà o
no.
Esempio di sorgente C
Se si vuole stampare solo la funzione main() di un file di sorgente C, è
possibile digitare:
Codice 1.8: Stampare la funzione main() di un file sorgente C |
$ sed -n -e '/main[[:space:]]*(/,/^}/p' sourcefile.c | more
|
Questo comando ha due espressioni regolari, '/main[[:space:]]*(/' and '/^}/',
e un comando, p. La prima espressione regolare indicherà tutte le
stringhe "main" seguite da un qualsiasi numero di spazi o tab e da una
parentesi aperta. Ciò corrisponde all'inizio della comune dichiarazione ANSI
C, main().
In questa particolare espressione regolare, abbiamo incontrato il carattere
'[[:space:]]'. Si tratta di una speciale keyword la quale indica a sed che deve
fargli corrispondere sia un tab che uno spazio, si può ottenere lo stesso
effetto inserendo: '[', uno spazio, Control-V, un tab e ']' -- Control-V indica
alla bash che si vuole inserire un "vero" tab e non usare l'autocompletamento.
È più chiaro, specialmente negli script, usare il comando '[[:space:]]'.
OK, adesso passiamo alla seconda espressione. '/^}/' indica di far
corrispondere ogni carattere '}' se appare all'inizio di una riga. Se il
codice è formattato correttamente, esso dovrà far corrispondere la fine della
funzione main().
Il comando p fa ciò che ha sempre fatto, indica a sed di stampare
esplicitamente le righe, anche se esso viene lanciato in modalità quite (-n).
Se si prova a lanciare il comando su un file sorgente C, esso dovrà stampare
tutto il blocco che va da "main() {" a '}', inclusi "main()" e '}'.
La prossima volta
Adesso abbiamo imparato le basi, siamo pronti quindi per i prossimi due
articoli. Se si è impazienti di avere ulteriore materiale su sed, abbiate
pazienza -- stanno arrivando! Nel frattempo, si possono consultare le seguenti
risorse riguardanti le espressioni regolari.
2.
Risorse
Link utili
|