Guida avanzata di scripting Bash: Un'approfondita esplorazione dell'arte dello scripting di shell | ||
---|---|---|
Indietro | Capitolo 15. Filtri, programmi e comandi esterni | Avanti |
Comandi riguardanti il testo ed i file di testo
Utility per la classificazione di file, spesso usata come
filtro in una pipe. Questo comando ordina un flusso di testo,
o un file, in senso crescente o decrescente, o secondo le
diverse interpretazioni o posizioni dei caratteri. Usato
con l'opzione -m
unisce, in un unico
file, i file di input precedentemente ordinati. La sua
pagina info ne elenca le
funzionalità e le molteplici opzioni.
Vedi Esempio 10-9,
Esempio 10-10 e Esempio A-8.
Esegue un ordinamento topologico di stringhe lette in coppia secondo i modelli forniti nell'input. Originariamente lo scopo di tsort era quello di ordinare un elenco di dipendenze per una versione obsoleta del linker ld in una "antiquata" versione di UNIX.
I risultati di un tsort solitamente differiscono in modo marcato da quelli del precedente comando sort.
Questo filtro elimina le righe duplicate di un file che è stato ordinato. È spesso usato in una pipe in coppia con sort.
cat lista-1 lista-2 lista-3 | sort | uniq > listafinale # Vengono concatenati i file lista, # ordinati, # eliminate le righe doppie, # ed infine il risultato viene scritto in un file di output. |
L'opzione -c
premette ad ogni riga
del file di input il numero delle sue occorrenze.
bash$ cat fileprova Questa riga è presente una sola volta. Questa riga è presente due volte. Questa riga è presente due volte. Questa riga è presente tre volte. Questa riga è presente tre volte. Questa riga è presente tre volte. bash$ uniq -c fileprova 1 Questa riga è presente una sola volta. 2 Questa riga è presente due volte. 3 Questa riga è presente tre volte. bash$ sort fileprova | uniq -c | sort -nr 3 Questa riga è presente tre volte. 2 Questa riga è presente due volte. 1 Questa riga è presente una sola volta. |
La sequenza di comandi
sort FILEINPUT | uniq -c | sort -nr
produce un elenco delle frequenze di occorrenza
riferite al file FILEINPUT (le opzioni
-nr
di sort generano
un ordinamento numerico inverso). Questo modello viene
usato nell'analisi dei file di log e nelle liste dizionario, od ogni volta
che è necessario esaminare la struttura lessicale di
un documento.
Esempio 15-11. Analisi di frequenza delle parole
#!/bin/bash # wf.sh: Un'analisi sommaria, su un file di testo, della #+ frequenza delle parole. # È una versione più efficiente dello script "wf2.sh". # Verifica la presenza di un file di input passato da riga di comando. ARG=1 E_ERR_ARG=65 E_NOFILE=66 if [ $# -ne "$ARG" ] # Il numero di argomenti passati allo script è corretto? then echo "Utilizzo: `basename $0` nomefile" exit $E_ERR_ARG fi if [ ! -f "$1" ] # Verifica se il file esiste. then echo "Il file \"$1\" non esiste." exit $E_NOFILE fi ############################################################################### # main () sed -e 's/\.//g' -e 's/\,//g' -e 's/ /\ /g' "$1" | tr 'A-Z' 'a-z' | sort | uniq -c | sort -nr # ========================= # Frequenza delle occorrenze # Filtra i punti e le virgole, e cambia gli spazi tra le parole in #+ linefeed, quindi trasforma tutti i caratteri in caratteri minuscoli ed #+ infine premette il conteggio delle occorrenze e le ordina in base al numero. # Arun Giridhar suggerisce di modificare il precedente in: # . . . | sort | uniq -c | sort +1 [-f] | sort +0 -nr # In questo modo viene aggiunta una chiave di ordinamento secondaria, per cui #+ nel caso di occorrenze uguali queste vengono ordinate alfabeticamente. # Ecco la spiegazione: # "In effeti si tratta di un ordinamento di radice, prima sulla #+ colonna meno significativa #+ (parola o stringa, opzionalmente senza distinzione minuscolo-maiuscolo) #+ infine sulla colonna più significativa (frequenza)." # # Frank Wang spiega che il precedente equivale a #+ . . . | sort | uniq -c | sort +0 -nr #+ così pure il seguente: #+ . . . | sort | uniq -c | sort -k1nr -k ############################################################################### exit 0 # Esercizi: # --------- # 1) Aggiungete dei comandi a 'sed' per filtrare altri segni di # + punteggiatura, come i punti e virgola. # 2) Modificatelo per filtrare anche gli spazi multipli e gli altri # + caratteri di spaziatura. |
bash$ cat fileprova Questa riga è presente una sola volta. Questa riga è presente due volte. Questa riga è presente due volte. Questa riga è presente tre volte. Questa riga è presente tre volte. Questa riga è presente tre volte. bash$ ./wf.sh fileprova 6 riga 6 questa 6 presente 6 è 5 volte 3 tre 2 due 1 volta 1 una 1 sola |
Il filtro expand trasforma le tabulazioni in spazi. È spesso usato in una pipe.
Il filtro unexpand trasforma gli spazi in tabulazioni. Esegue l'azione opposta di expand.
Strumento per estrarre i campi dai file. È
simile alla serie di comandi
print $N di
awk, ma con capacità
più limitate. In uno script è più
semplice usare cut che non
awk. Particolarmente importanti sono le
opzioni -d
(delimitatore) e
-f
(indicatore di campo - field
specifier).
Usare cut per ottenere l'elenco dei filesystem montati:
cut -d ' ' -f1,2 /etc/mtab |
Uso di cut per visualizzare la versione del SO e del kernel:
uname -a | cut -d" " -f1,3,11,12 |
Usare cut per estrarre le intestazioni dei messaggi da una cartella e-mail:
bash$ grep '^Subject:' read-messages | cut -c10-80 Re: Linux suitable for mission-critical apps? MAKE MILLIONS WORKING AT HOME!!! Spam complaint Re: Spam complaint |
Usare cut per la verifica di un file:
# Elenca tutti gli utenti presenti nel file /etc/passwd. FILE=/etc/passwd for utente in $(cut -d: -f1 $FILE) do echo $utente done # Grazie, Oleg Philon per il suggerimento. |
cut -d ' ' -f2,3 nomefile equivale a awk -F'[ ]' '{ print $2, $3 }' nomefile
È anche possibile usare l'a_capo come delimitatore. Il trucco consiste nell'inserire veramente un a_capo (INVIO) nella sequenza dei comandi.
Grazie a Jaka Kranjc per la precisazione. |
Vedi anche Esempio 15-43.
Strumento per riunire più file in un unico file impaginato su diverse colonne. In combinazione con cut è utile per creare file di log di sistema.
Lo si può considerare il cugino specializzato di paste. Questa potente utility consente di fondere due file in modo da fornire un risultato estremamente interessante. Crea, in sostanza, una versione semplificata di un database relazionale.
Il comando join opera solo su due file, ma unisce soltanto quelle righe che possiedono una corrispondenza di campo comune (solitamente un'etichetta numerica) e visualizza il risultato allo stdout. I file che devono essere uniti devono essere anche ordinati in base al campo comune, se si vuole che l'abbinamento delle righe avvenga correttamente.
File: 1.dat 100 Scarpe 200 Lacci 300 Calze |
File: 2.dat 100 EUR 40.00 200 EUR 1.00 300 EUR 2.00 |
bash$ join 1.dat 2.dat File: 1.dat 2.dat 100 Scarpe EUR 40.00 200 Lacci EUR 1.00 300 Calze EUR 2.00 |
Il campo comune, nell'output, compare una sola volta. |
visualizza la parte iniziale di un file -- il valore preimpostato è di 10 righe, ma questo valore può essere modificato -- allo stdout. Possiede un certo numero di opzioni interessanti.
Esempio 15-12. Quali file sono degli script?
#!/bin/bash # script-detector.sh: Rileva gli script presenti in una directory. VERCAR=2 # Verifica i primi 2 caratteri. INTERPRETE='#!' # Gli script iniziano con "#!". for file in * # Verifica tutti i file della directory corrente. do if [[ `head -c$VERCAR "$file"` = "$INTERPRETE" ]] # head -c2 #! # L'opzione '-c' di "head" agisce sul numero di caratteri specificato #+ anziché sulle righe (comportamento di default). then echo "Il file \"$file\" è uno script." else echo "Il file \"$file\" *non* è uno script." fi done exit 0 # Esercizi: # -------- # 1) Modificate lo script in modo che possa avere come argomento opzionale #+ la directory dove ricercare gli script #+ (invece della sola directory di lavoro corrente). # # 2) Così com'è, lo script rileva dei "falsi positivi" in presenza #+ di script Perl, awk e di altri linguaggi di scripting. # Correggete questa falla. |
Esempio 15-13. Generare numeri casuali di 10 cifre
#!/bin/bash # rnd.sh: Visualizza un numero casuale di 10 cifre # Script di Stephane Chazelas. head -c4 /dev/urandom | od -N4 -tu4 | sed -ne '1s/.* //p' # ===================================================================== # # Analisi # -------- # head: # l'opzione -c4 considera solamente i primi 4 byte. # od: # L'opzione -N4 limita l'output a 4 byte. # L'opzione -tu4 seleziona, per l'output, il formato decimale senza segno. # sed: # L'opzione -n, in combinazione con l'opzione "p" del comando "s", prende #+ in considerazione, per l'output, solo le righe verificate. # L'autore di questo script spiega l'azione di 'sed' come segue. # head -c4 /dev/urandom | od -N4 -tu4 | sed -ne '1s/.* //p' # ----------------------------------> | # Assumiamo che l'output fino a "sed">| # sia 0000000 1198195154\n # sed inizia leggendo i caratteri: 0000000 1198195154\n. # Qui trova il carattere di ritorno a capo, quindi è pronto per elaborare #+ la prima riga (0000000 1198195154), che assomiglia alla sua direttiva #+ <righe><comandi>. La prima ed unica è # righe comandi # 1 s/.* //p # Il numero della riga è nell'intervallo, quindi entra in azione: #+ cerca di sostituire la stringa più lunga terminante con uno spazio #+ ("0000000 ") con niente "//" e in caso di successo, visualizza il risultato #+ ("p" è l'opzione del comando "s", ed è differente dal comando "p"). # sed ora continua la lettura dell'input. (Notate che prima di continuare, se #+ non fosse stata passata l'opzione -n, sed avrebbe visualizzato la riga #+ un'altra volta). # sed adesso legge la parte di caratteri rimanente, e trova la fine del file. # Si appresta ad elaborare la seconda riga (che può anche essere numerata #+ con '$' perché è l'ultima). # Constata che non è compresa in <righe> e quindi termina il lavoro. # In poche parole, questo comando sed significa: "Solo sulla prima riga, togli #+ qualsiasi carattere fino allo spazio, quindi visualizza il resto." # Un modo migliore per ottenere lo stesso risultato sarebbe stato: # sed -e 's/.* //;q' # Qui abbiamo due <righe> e due <comandi> (si sarebbe potuto anche scrivere # sed -e 's/.* //' -e q): # righe comandi # niente (verifica la riga) s/.* // # niente (verifica la riga) q (quit) # In questo esempio, sed legge solo la sua prima riga di input. # Esegue entrambi i comandi e visualizza la riga (con la sostituzione) prima #+ di uscire (a causa del comando "q"), perché non gli è stata passata #+ l'opzione "-n". # ======================================================================= # # Un'alternativa ancor più semplice al precedente script di una sola riga, #+ potrebbe essere: # head -c4 /dev/urandom| od -An -tu4 exit 0 |
visualizza la parte finale di un file -- il valore preimpostato
è di 10 righe -- allo
stdout . Viene comunemente usato per
tenere traccia delle modifiche al file di log di sistema con
l'uso dell'opzione -f
, che permette di visualizzare
le righe accodate al file.
Esempio 15-14. Utilizzare tail per controllare il log di sistema
#!/bin/bash nomefile=sys.log cat /dev/null > $nomefile; echo "Creazione / cancellazione del file." # Crea il file nel caso non esista, mentre lo svuota se è già stato creato. # vanno bene anche : > nomefile e > nomefile. tail /var/log/messages > $nomefile # /var/log/messages deve avere i permessi di lettura perché lo script funzioni. echo "$nomefile contiene la parte finale del log di sistema." exit 0 |
Per individuare una riga specifica in un file di testo, si colleghi con una pipe l'output di head a tail -n 1. Per esempio head -n 8 database.txt | tail -n 1 rintraccia l'8va riga del file database.txt. Per impostare una variabile ad un determinato blocco di un file di testo:
|
Le più recenti implementazioni di tail deprecano l'uso del precedente tail -$RIGHE nomefile. Il comando standard tail -n $RIGHE nomefile è corretto. |
Vedi anche Esempio 15-5, Esempio 15-35 e Esempio 29-6.
Strumento di ricerca multifunzione che fa uso delle Espressioni Regolari. In origine era un comando/filtro del venerabile editor di linea ed: g/re/p -- global - regular expression - print.
grep modello [file...]
Ricerca nel/nei file indicato/i l'occorrenza di modello, dove modello può essere o un testo letterale o un'Espressione Regolare.
bash$ grep '[rst]ystem.$' osinfo.txt The GPL governs the distribution of the Linux operating system. |
Se non vengono specificati i file, grep funziona come filtro sullo stdout, come in una pipe.
bash$ ps ax | grep clock 765 tty1 S 0:00 xclock 901 pts/1 S 0:00 grep clock |
L'opzione -i
abilita una ricerca che
non fa distinzione tra maiuscole e minuscole.
L'opzione -w
verifica solo le parole
esatte.
L'opzione -l
elenca solo i file in
cui la ricerca ha avuto successo, ma non le righe
verificate.
L'opzione -r
(ricorsivo) ricerca i
file nella directory di lavoro corrente e in tutte le sue
sottodirectory.
L'opzione -n
visualizza le righe
verificate insieme al loro numero.
bash$ grep -n Linux osinfo.txt 2:This is a file containing information about Linux. 6:The GPL governs the distribution of the Linux operating system. |
L'opzione -v
(o --invert-match
)
scarta le righe verificate.
grep modello1 *.txt | grep -v modello2 # Verifica tutte le righe dei file "*.txt" contenenti "modello1", # ma ***non*** quelle contenenti "modello2". |
L'opzione -c
(--count
) fornisce il numero delle
occorrenze, ma non le visualizza.
grep -c txt *.sgml # ((numero di occorrenze di "txt" nei file "*.sgml") # grep -cz . # ^ punto # significa conteggio (-c) zero-diviso (-z) elementi da cercare "." # cioè, quelli non vuoti (contenenti almeno 1 carattere). # printf 'a b\nc d\n\n\n\n\n\000\n\000e\000\000\nf' | grep -cz . # 3 printf 'a b\nc d\n\n\n\n\n\000\n\000e\000\000\nf' | grep -cz '$' # 5 printf 'a b\nc d\n\n\n\n\n\000\n\000e\000\000\nf' | grep -cz '^' # 5 # printf 'a b\nc d\n\n\n\n\n\000\n\000e\000\000\nf' | grep -c '$' # 9 # Per default, i caratteri di a capo (\n) separano gli elementi da cercare. # Notate che l'opzione -z è specifica del "grep" di GNU. # Grazie, S.C. |
Quando viene invocato con più di un file, grep specifica qual'è il file contenente le occorrenze.
bash$ grep Linux osinfo.txt misc.txt osinfo.txt:This is a file containing information about Linux. osinfo.txt:The GPL governs the distribution of the Linux operating system. misc.txt:The Linux operating system is steadily gaining in popularity. |
Per forzare grep a visualizzare il nome del file quando ne è presente soltanto uno, si deve indicare come secondo file /dev/null
|
Se la ricerca ha avuto successo,
grep restituisce
come exit status 0.
Questo lo rende utile per un costrutto di verifica in uno
script, specialmente in abbinamento con l'opzione
-q
che sopprime l'output.
SUCCESSO=0 # se la ricerca di grep è riuscita parola=Linux file=file.dati grep -q "$parola" "$file" # L'opzione "-q" non visualizza nulla allo stdout. if [ $? -eq $SUCCESSO ] # if grep -q "$parola" "$file" può sostituire le righe 5 - 8. then echo "$parola è presente in $file" else echo "$parola non è presente in $file" fi |
L'Esempio 29-6 dimostra come usare grep per cercare una parola in un file di log di sistema.
Esempio 15-15. Simulare grep in uno script
#!/bin/bash # grp.sh: Una reimplementazione molto sommaria di 'grep'. E_ERR_ARG=65 if [ -z "$1" ] # Verifica se sono stati passati argomenti allo script. then echo "Utilizzo: `basename $0` modello" exit $E_ERR_ARG fi echo for file in * # Verifica tutti i file in $PWD. do output=$(sed -n /"$1"/p $file) # Sostituzione di comando. if [ ! -z "$output" ] # Cosa succede se si usa "$output" #+ senza i doppi apici? then echo -n "$file: " echo $output fi # sed -ne "/$1/s|^|${file}: |p" equivale al precedente. echo done echo exit 0 # Esercizi: # --------- # 1) Aggiungete nuove righe di output nel caso ci sia più di una #+ occorrenza per il file dato. # 2) Aggiungete altre funzionalità. |
È possibile far ricercare a grep due (o più) differenti modelli? Cosa si può fare se volessimo che grep visualizzi tutte le righe di un file o i file che contengono sia "modello1" che "modello2"?
Un metodo consiste nel collegare con una pipe il risultato di grep modello1 a grep modello2.
Ad esempio, dato il file seguente:
# File: tstfile Questo è un file d'esempio. Questo è un file di testo ordinario. Questo file non contiene testo strano. Questo file non è insolito. Altro testo. |
Ora cerchiamo nel file le righe contenenti entrambe le parole "file" e "testo" . . .
bash$ grep file tstfile # File: tstfile Questo è un file d'esempio. Questo è un file di testo ordinario. Questo file non contiene testo strano. Questo file non è insolito.. bash$ grep file tstfile | grep testo Questo è un file di testo ordinario. Questo file non contiene testo strano. |
--
egrep - (extended grep) grep esteso - è uguale a grep -E. Tuttavia usa una serie leggermente diversa ed estesa di Espressioni Regolari che possono rendere la ricerca un po' più flessibile. Permette anche l'uso dell'operatore booleano | (or).
bash $ egrep 'matches|Matches' file.txt Line 1 matches. Line 3 Matches. Line 4 contains matches, but also Matches |
fgrep -- fast grep grep veloce -- è uguale a grep -F. Esegue la ricerca letterale della stringa (niente Espressioni Regolari), il che solitamente accelera un po' l'operazione.
In alcune distribuzioni Linux, egrep e
fgrep sono link simbolici, o alias, di
grep, invocato però con le opzioni
|
Esempio 15-16. Cercare una definizione nel Webster's Dictionary ed. 1913
#!/bin/bash # dict-lookup.sh # Questo script ricerca delle definizioni nel Webster's Dictionary ed. 1913. # Si tratta di un dizionario di Dominio Pubblico disponibile per il download #+ presso vari siti, compreso il #+ Project Gutenberg (http://www.gutenberg.org/etext/247). # # Prima di utilizzarlo va convertito dal formato DOS a quello UNIX #+ (solo gli LF a fine riga). # Deve essere salvato nel formato testo ASCII non compresso. # Impostate la variabile DEFAULT_DICTFILE a percorso/nome_file. E_ERR_ARG=65 MAXRIGHE=50 # Numero massimo di righe #+ da visualizzare. DEFAULT_DICTFILE="/usr/share/dict/webster1913-dict.txt" # Percorso/nome del dizionario #+ preimpostato. # Modificatelo se necessario. # Nota: # ---- # In questa particolare edizione del 1913 del Webster #+ ogni voce inizia con una lettera maiuscola #+ (la parte restante in caratteri minuscoli). # Solo la "prima riga" di ciascuna voce inizia in questo modo #+ ed è per questo motivo che l'algoritmo di ricerca seguente funziona. if [[ -z $(echo "$1" | sed -n '/^[A-Z]/p') ]] # Deve essere specificata almeno una voce da ricercare e #+ deve iniziare con una lettera maiuscola. then echo "Utilizzo: `basename $0` Voce [file-dizionario]" echo echo "Nota: La voce da ricercare deve iniziare con una lettera maiuscola," echo "la parte rimanente in minuscolo." echo "--------------------------------------------" echo "Esempi: Abandon, Dictionary, Marking, ecc." exit $E_ERR_ARG fi if [ -z "$2" ] # Potete specificare un dizionario #+ diverso come argomento #+ dello script. then dictfile=$DEFAULT_DICTFILE else dictfile="$2" fi # --------------------------------------------------------- Definizione=$(fgrep -A $MAXRIGHE "$1 \\" "$dictfile") # Definizioni nella forma "Voce \..." # # E, sì, "fgrep" è sufficientemente veloce #+ anche nella ricerca di un file di testo molto grande. # Ora seleziona la parte inirente alla definizione. echo "$Definizione" | sed -n '1,/^[A-Z]/p' | # Visualizza dalla prima riga della definizione #+ fino alla prima riga della voce successiva. sed '$d' | sed '$d' # Cancellando le ultime due righe #+ (la riga vuota e la prima riga della voce successiva). # --------------------------------------------------------- exit 0 # Esercizi: # -------- # 1) Modificate lo script in modo che accetti un input alfabetico arbitrario # + (lettere maiuscole, minuscole o alternate) che verrà convertito # + nel formato usato per l'elaborazione. # # 2) Trasformate lo script in un'applicazione GUI, # + usando qualcosa tipo "gdialog" . . . # Lo script non riceverà più, di conseguenza, lo/gli argomento(i) # + da riga di comando. # # 3) Modificate lo script per una verifica in uno degli altri Dizionari # + di Dominio Pubblico disponibili, quale il U.S. Census Bureau Gazetteer. |
agrep (approximate grep) grep d'approssimazione, estende le capacità di grep per una ricerca per approssimazione. La stringa da ricercare differisce per un numero specifico di caratteri dalle occorrenze effettivamente risultanti. Questa utility non è, di norma, inclusa in una distribuzione Linux.
Per la ricerca in file compressi vanno usati i comandi zgrep, zegrep o zfgrep. Sebbene possano essere usati anche con i file non compressi, svolgono il loro compito più lentamente che non grep, egrep, fgrep. Sono invece utili per la ricerca in una serie di file misti, alcuni compressi altri no. Per la ricerca in file compressi con bzip si usa il comando bzgrep. |
Il comando look opera come grep, ma la ricerca viene svolta in un "dizionario", un elenco di parole ordinate. In modo predefinito, look esegue la ricerca in /usr/dict/words. Naturalmente si può specificare un diverso dizionario.
Esempio 15-17. Verificare la validità delle parole con un dizionario
#!/bin/bash # lookup: Esegue una verifica di dizionario di tutte le parole di un file dati. file=file.dati # File dati le cui parole devono essere controllate. echo while [ "$Parola" != fine ] # Ultima parola del file dati. do read parola # Dal file dati, a seguito della redirezione a fine ciclo. look $parola > /dev/null # Per non visualizzare le righe del #+ file dizionario. verifica=$? # Exit status del comando 'look'. if [ "$verifica" -eq 0 ] then echo "\"$parola\" è valida." else echo "\"$parola\" non è valida." fi done <"$file" # Redirige lo stdin a $file, in modo che "read" agisca #+ su questo. echo exit 0 # ---------------------------------------------------------------- # Le righe di codice seguenti non vengono eseguite a causa del #+ precedente comando "exit". # Stephane Chazelas propone la seguente, e più concisa, alternativa: while read parola && [[ $parola != fine ]] do if look "$parola" > /dev/null then echo "\"$parola\" è valida." else echo "\"$parola\" non è valida." fi done <"$file" exit 0 |
Linguaggi di scripting particolarmente adatti per la verifica di file di testo e dell'output dei comandi. Possono essere inseriti, singolarmente o abbinati, nelle pipe e negli script di shell.
"Editor di flusso" non interattivo, consente l'utilizzo di molti comandi ex in modalità batch. Viene impiegato principalmente negli script di shell.
Analizzatore e rielaboratore programmabile di file, ottimo per manipolare e/o localizzare campi (colonne) in file di testo strutturati. Ha una sintassi simile a quella del linguaggio C.
wc fornisce il "numero di parole (word count)" presenti in un file o in un flusso I/O:
bash $ wc /usr/share/doc/sed-4.1.2/README 13 70 447 README [13 lines 70 words 447 characters] |
wc -w fornisce solo il numero delle parole.
wc -l fornisce solo il numero di righe.
wc -c fornisce solo il numero dei byte.
wc -m fornisce solo il numero dei caratteri.
wc -L fornisce solo la dimensione della riga più lunga.
Uso di wc per contare quanti file .txt sono presenti nella directory di lavoro corrente:
$ ls *.txt | wc -l # Il conteggio si interrompe se viene trovato un carattere di #+ linefeed nel nome di uno dei file "*.txt". # Modi alternativi per svolgere lo stesso compito: # find . -maxdepth 1 -name \*.txt -print0 | grep -cz . # (shopt -s nullglob; set -- *.txt; echo $#) # Grazie, S.C. |
Uso di wc per calcolare la dimensione totale di tutti i file i cui nomi iniziano con le lettere comprese nell'intervallo d - h.
bash$ wc [d-h]* | grep total | awk '{print $3}' 71832 |
Uso di wc per contare le occorrenze della parola "Linux" nel file sorgente di questo libro.
bash$ grep Linux abs-book.sgml | wc -l 50 |
Vedi anche Esempio 15-35 e Esempio 19-8.
Alcuni comandi possiedono, sotto forma di opzioni, alcune delle funzionalità di wc.
... | grep foo | wc -l # Questo costrutto, frequentemente usato, può essere reso in modo più conciso. ... | grep -c foo # Un semplice impiego dell'opzione "-c" (o "--count") di grep. # Grazie, S.C. |
filtro per la sostituzione di caratteri.
Si deve usare il "quoting" e/o le parentesi quadre, in modo appropriato. Il quoting evita la reinterpretazione dei caratteri speciali nelle sequenze di comandi tr. Va usato il quoting delle parentesi quadre se si vuole evitarne l'espansione da parte della shell. |
Sia tr "A-Z" "*" <nomefile che tr A-Z \* <nomefile cambiano tutte le lettere maiuscole presenti in nomefile in asterischi (allo stdout). Su alcuni sistemi questo potrebbe non funzionare. A differenza di tr A-Z '[**]'.
L'opzione -d
cancella un intervallo
di caratteri.
echo "abcdef" # abcdef echo "abcdef" | tr -d b-d # aef tr -d 0-9 <nomefile # Cancella tutte le cifre dal file "nomefile". |
L'opzione --squeeze-repeats
(o
-s
) cancella tutte le occorrenze di una
stringa di caratteri consecutivi, tranne la prima.
È utile per togliere gli
spazi in eccesso.
bash$ echo "XXXXX" | tr --squeeze-repeats 'X' X |
L'opzione -c
"complemento"
inverte la serie di caratteri da verificare.
Con questa opzione, tr agisce soltanto
su quei caratteri che non verificano
la serie specificata.
bash$ echo "acfdeb123" | tr -c b-d + +c+d+b++++ |
È importante notare che tr riconosce le classi di caratteri POSIX. [1]
bash$ echo "abcd2ef1" | tr '[:alpha:]' - ----2--1 |
Esempio 15-18. toupper: Trasforma tutte le lettere di un file in maiuscole
#!/bin/bash # Modifica tutte le lettere del file in maiuscole. E_ERR_ARG=65 if [ -z "$1" ] # Verifica standard degli argomenti da riga di comando. then echo "Utilizzo: `basename $0` nomefile" exit $E_ERR_ARG fi tr a-z A-Z <"$1" # Stesso effetto del precedente, ma usando la notazione POSIX: # tr '[:lower:]' '[:upper:]' <"$1" # Grazie, S.C. exit 0 # Esercizio: # Riscrivete lo script in modo che accetti come opzione il nome, "sia" #+ in lettere maiuscole che minuscole, del file da madificare, . |
Esempio 15-19. lowercase: Cambia in lettere minuscole tutti i nomi dei file della directory corrente
#! /bin/bash # # Cambia ogni nome di file della directory di lavoro in lettere minuscole. # # Ispirato da uno script di John Dubois, #+ che è stato tradotto in Bash da Chet Ramey #+ e semplificato considerevolmente dall'autore di Guida ABS. for file in * # Controlla tutti i file della directory. do fnome=`basename $file` n=`echo $fnome | tr A-Z a-z` # Cambia il nome del file in tutte #+ lettere minuscole. if [ "$fnome" != "$n" ] # Rinomina solo quei file che non #+ sono già in minuscolo. then mv $fnome $n fi done exit $? # Il codice che si trova oltre questa riga non viene eseguito a causa #+ del precedente "exit". #---------------------------------------------------------------------# # Se volete eseguirlo, cancellate o commentate le righe precedenti. # Lo script visto sopra non funziona con nomi di file conteneti spazi #+ o ritorni a capo. # Stephane Chazelas, quindi, suggerisce l'alternativa seguente: for file in * # Non è necessario usare basename, perché "*" non #+ restituisce i nomi di file contenenti "/". do n=`echo "$file/" | tr '[:upper:]' '[:lower:]'` # Notazione POSIX dei set di caratteri. # È stata aggiunta una barra, in modo che gli # eventuali ritorni a capo non vengano cancellati # dalla sostituzione di comando. # Sostituzione di variabile: n=${n%/} # Rimuove le barre, aggiunte precedentemente, dal #+ nome del file. [[ $file == $n ]] || mv "$file" "$n" # Verifica se il nome del file è già in minuscolo. done exit $? |
Esempio 15-20. du: Conversione di file di testo DOS al formato UNIX
#!/bin/bash # Du.sh: converte i file di testo DOS in formato UNIX . E_ERR_ARG=65 if [ -z "$1" ] then echo "Utilizzo: `basename $0` nomefile-da-convertire" exit $E_ERR_ARG fi NUOVONOMEFILE=$1.unx CR='\015' # Ritorno a capo. # 015 è il codice ottale ASCII di CR # Le righe dei file di testo DOS terminano con un CR-LF. # Le righe dei file di testo UNIX terminano con il solo LF. tr -d $CR < $1 > $NUOVONOMEFILE # Cancella i CR e scrive il file nuovo. echo "Il file di testo originale DOS è \"$1\"." echo "Il file di testo tradotto in formato UNIX è \"$NOMENUOVOFILE\"." exit 0 # Esercizio: #----------- # Modificate lo script per la conversione inversa (da UNIX a DOS). |
Esempio 15-21. rot13: cifratura ultra debole
#!/bin/bash # rot13.sh: Classico algoritmo rot13, cifratura che potrebbe beffare solo un # bambino di 3 anni. # Utilizzo: ./rot13.sh nomefile # o ./rot13.sh <nomefile # o ./rot13.sh e fornire l'input da tastiera (stdin) cat "$@" | tr 'a-zA-Z' 'n-za-mN-ZA-M' # "a" corrisponde a "n", "b" a "o", ecc. # Il costrutto 'cat "$@"' consente di gestire un input proveniente sia dallo #+ stdin che da un file. exit 0 |
Esempio 15-22. Generare "Rompicapi Cifrati" di frasi celebri
#!/bin/bash # crypto-quote.sh: Cifra citazioni # Cifra frasi famose mediante una semplice sostituzione monoalfabetica. # Il risultato è simile ai rompicapo "Crypto Quote" delle pagine Op Ed #+ del Sunday. chiave=ETAOINSHRDLUBCFGJMQPVWZYXK # La "chiave" non è nient'altro che l'alfabeto rimescolato. # Modificando la "chiave" cambia la cifratura. # Il costrutto 'cat "$@"' permette l'input sia dallo stdin che dai file. # Se si usa lo stdin, l'input va terminato con un Control-D. # Altrimenti occorre specificare il nome del file come parametro da riga # di comando. cat "$@" | tr "a-z" "A-Z" | tr "A-Z" "$chiave" # | in maiuscolo | cifra # Funziona con frasi formate da lettere minuscole, maiuscole o entrambe. # I caratteri non alfabetici non vengono modificati. # Provate lo script con qualcosa di simile a # "Nothing so needs reforming as other people's habits." # --Mark Twain # # Il risultato è: # "CFPHRCS QF CIIOQ MINFMBRCS EQ FPHIM GIFGUI'Q HETRPQ." # --BEML PZERC # Per decodificarlo: # cat "$@" | tr "$chiave" "A-Z" # Questa semplice cifratura può essere spezzata da un dodicenne con il #+ semplice uso di carta e penna. exit 0 # Esercizio: # --------- # Modificate lo script in modo che sia in grado sia di cifrare che di #+ decifrare, in base al(i) argomento(i) passato(i) da riga di comando. |
Filtro che dimensiona le righe di input ad una
larghezza specificata. È particolarmente utile con
l'opzione -s
che interrompe le righe in
corrispondenza degli spazi tra una parola e l'altra
(vedi Esempio 15-23 e
Esempio A-1).
Semplice formattatore di file usato come filtro, in una pipe, per "ridimensionare" lunghe righe di testo per l'output.
Esempio 15-23. Dimensionare un elenco di file
#!/bin/bash AMPIEZZA=40 # Ampiezza di 40 colonne. b=`ls /usr/local/bin` # Esegue l'elenco dei file... echo $b | fmt -w $AMPIEZZA # Si sarebbe potuto fare anche con # echo $b | fold - -s -w $AMPIEZZA exit 0 |
Vedi anche Esempio 15-5.
Una potente alternativa a fmt è l'utility par di Kamil Toman, disponibile presso http://www.cs.berkeley.edu/~amc/Par/. |
Questo filtro, dal nome fuorviante, rimuove i cosiddetti line feed inversi dal flusso di input. Cerca anche di sostituire gli spazi con caratteri di tabulazione. L'uso principale di col è quello di filtrare l'output proveniente da alcune utility di elaborazione di testo, come groff e tbl.
Riordina il testo in colonne. Questo filtro trasforma l'output di un testo, che apparirebbe come un elenco, in una "graziosa" tabella, inserendo caratteri di tabulazione in posizioni appropriate.
Esempio 15-24. Utilizzo di column per impaginare un elenco di directory
#!/bin/bash # L'esempio seguente corrisponde, con piccole modifiche, a quello #+ contenuto nella pagina di manuale di "column". (printf "PERMISSIONS LINKS OWNER GROUP SIZE MONTH DAY HH:MM PROG-NAME\n" \ ; ls -l | sed 1d) | column -t # "sed 1d" nella pipe cancella la prima riga di output, che sarebbe #+ "total N", #+ dove "N" è il numero totale di file elencati da "ls -l". # L'opzione -t di "column" visualizza l'output in forma tabellare. exit 0 |
Filtro per la rimozione di colonne. Elimina le colonne (caratteri) da un file. Il risultato viene visualizzato allo stdout. colrm 2 4 <nomefile cancella dal secondo fino al quarto carattere di ogni riga del file di testo nomefile.
Se il file contiene caratteri non visualizzabili, o di tabulazione, il risultato potrebbe essere imprevedibile. In tali casi si consideri l'uso, in una pipe, dei comandi expand e unexpand posti prima di colrm. |
Filtro per l'enumerazione delle righe: nl nomefile visualizza nomefile allo stdout inserendo, all'inizio di ogni riga non vuota, il numero progressivo. Se nomefile viene omesso, l'azione viene svolta sullo stdin.
L'output di nl assomiglia molto a quello di cat -b, perché, in modo predefinito, nl non visualizza le righe vuote.
Esempio 15-25. nl: Uno script che numera le proprie righe
#!/bin/bash # line-number.sh # Questo script si auto-visualizza due volte con le righe numerate. # 'nl' considera questa riga come la nr. 4 perché le righe #+ vuote vengono saltate. # 'cat -n' vede la riga precedente come la numero 6. nl `basename $0` echo; echo # Ora proviamo con 'cat -n' cat -n `basename $0` # La differenza è che 'cat -n' numera le righe vuote. # Notate che lo stesso risultato lo si ottiene con 'nl -ba'. exit 0 #----------------------------------------------------------------- |
Filtro di formato di visualizzazione. Impagina i file (o lo stdout) in sezioni adatte alla visualizzazione su schermo o per la stampa hard copy. Diverse opzioni consentono la gestione di righe e colonne come, tra l'altro, abbinare e numerare le righe, impostare i margini, aggiungere intestazioni ed unire file. Il comando pr riunisce molte delle funzionalità di nl, paste, fold, column e expand.
pr -o 5 --width=65 fileZZZ | more visualizza sullo schermo una piacevole impaginazione del contenuto del file fileZZZ con i margini impostati a 5 e 65.
L'opzione -d
è
particolarmente utile per forzare la doppia spaziatura
(stesso effetto di sed -G).
Il pacchetto GNU gettext è una serie di utility per la localizzazione e traduzione dei messaggi di output dei programmi in lingue straniere. Originariamente progettato per i programmi in C, ora supporta diversi linguaggi di scripting e di programmazione.
Il programma gettext viene usato anche negli script di shell. Vedi la relativa pagina info.
Programma per generare cataloghi di messaggi in formato binario. Viene utilizzato per la localizzazione.
Utility per cambiare la codifica (set di caratteri) del/dei file. Utilizzato principalmente per la localizzazione.
# Converte una stringa dal formato UTF-8 a UTF-16 visualizzandola nella BookList function scrivi_stringa_utf8 { STRINGA=$1 BOOKLIST=$2 echo -n "$STRINGA" | iconv -f UTF8 -t UTF16 | cut -b 3- | tr -d \\n >> "$BOOKLIST" } # Dallo script di Peter Knowles "booklistgen.sh" #+ per la conversione di file nel formato Sony Librie. # (http://booklistgensh.peterknowles.com) |
Va considerato come la versione più elaborata del precedente iconv. Questa versatile utility viene usata per modificare la codifica di un file. Occorre notare che recode> non fa parte dell'installazione standard di Linux.
TeX e Postscript sono linguaggi per la composizione di testo usati per preparare copie per la stampa o per la visualizzazione a video.
TeX è l'elaborato sistema di composizione di Donald Knuth. Spesso risulta conveniente scrivere uno script di shell contenente tutte le opzioni e gli argomenti che vanno passati ad uno di questi linguaggi.
Ghostscript (gs) è l'interprete Postscript rilasciato sotto licenza GPL.
Utility per l'elaborazione di file TeX e pdf. Si trova in /usr/bin su molte distribuzioni Linux, e in realtà si tratta di un shell wrapper che richiama Perl per invocare Tex.
texexec --pdfarrange --result=Concatenato.pdf *pdf # Concatena tutti i file pdf della directory di lavoro corrente #+ nel'''unico file Concatenato.pdf . . . # (L'opzione --pdfarrange reimpagina il file pdf. Vedi anche --pdfcombine.) # La precedente riga di comando potrebbe essere parametrizzata e #+ inserita in uno script di shell. |
Utility per la conversione in PostScript di un file in formato testo.
Per esempio, enscript nomefile.txt -p nomefile.ps dà come risultato il file PostScript nomefile.ps.
Un altro linguaggio a marcatura e visualizzazione formattata di testo è groff. Si tratta della versione GNU, migliorata, dell'ormai venerabile pacchetto UNIX roff/troff. Le pagine di manuale utilizzano groff.
L'utility per l'elaborazione delle tabelle tbl viene considerata come parte di groff perché la sua funzione è quella di trasformare le istruzioni per la composizione delle tabelle in comandi groff.
Anche l'utility per l'elaborazione di equazioni eqn fa parte di groff e il suo compito è quello di trasformare le istruzioni per la composizione delle equazioni in comandi groff.
Esempio 15-26. manview: visualizzazione formattata di pagine di manuale
#!/bin/bash # manview.sh: impagina il sorgente di una pagina di manuale #+ per la visualizzazione. # Lo script è utile nella fase di scrittura di una pagina di manuale. # Permette di controllare i risultati intermedi al volo, #+ mentre ci si sta lavorando. E_ERRARG=65 if [ -z "$1" ] then echo "Utilizzo: `basename $0` nomefile" exit $E_ERRARG fi # --------------------------- groff -Tascii -man $1 | less # Dalla pagina di manuale di groff. # --------------------------- # Se la pagina di manuale include tabelle e/o equazioni, #+ allora il precedente codice non funzionerà. # La riga seguente è in grado di gestire tali casi. # # gtbl < "$1" | geqn -Tlatin1 | groff -Tlatin1 -mtty-char -man # # Grazie, S.C. exit 0 |
L'analizzatore lessicale lex genera programmi per la verifica d'occorrenza. Sui sistemi Linux è stato sostituito dal programma non proprietario flex.
L'utility yacc crea un analizzatore lessicale basato su una serie di specifiche. Sui sistemi Linux è stato sostituito dal non proprietario bison.
[1] | Questo è vero solo per la versione GNU di tr, non per la versione generica che si trova spesso sui sistemi commerciali UNIX. |