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 1

Indice:

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)
/} *S/ 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



Stampa

Aggiornato il 2 gennaio 2012

Oggetto: In questa serie di articoli, Daniel Robbins spiega come utilizzare il più potente editor di flusso presente in UNIX, sed. Sed è il tool ideale per il batch-editing dei file o per creare script shell per modificare file esistenti.

Daniel Robbins
Autore

Luca Marturana
Traduttore

Donate to support our development efforts.

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