AdvancedBashScripting Guide
AdvancedBashScripting Guide
Mendel Cooper
[email protected]
Guida avanzata di scripting Bash: Unapprofondita esplorazione dellarte dello scripting di shell Mendel Cooper Pubblicato 16 novembre 2005
Questo manuale, per la cui comprensione non necessaria una precedente conoscenza di scripting o di programmazione, permette di raggiungere rapidamente un livello di apprendimento intermedio/avanzato . . . tempo che tranquillamente ed inconsapevolmente si trasforma in piccoli frammenti di conoscenza e saggezza UNIX. Pu essere utilizzato come libro di testo, come manuale per lautoapprendimento e come guida di riferimento per le tecniche di scripting di shell. Gli esercizi e gli esempi ampiamente commentati coinvolgono il lettore interessato, con lavvertenza che per imparare veramente lo scripting, lunico modo quello di scrivere script. Questo libro adatto per linsegnamento scolastico, come introduzione generale ai concetti della programmazione. Lultimo aggiornamento di questo documento (http://personal.riverusers.com/~thegrendel/abs-guide-3.7.tar.bz2), in forma di archivio compresso bzip2 tarball comprendente sia i sorgenti SGML che il formato HTML, pu essere scaricato dal sito dellautore. anche disponibile una versione pdf (http://www.tldp.org/LDP/abs/abs-guide.pdf). Vedi il change log (http://personal.riverusers.com/~thegrendel/Change.log) per la cronologia delle revisioni. Per la versione in lingua italiana possibile reperirne una copia presso il PLUTO (http://www.pluto.linux.it/ildp/guide.html), ovvero il sito italiano collegato a tldp.
Diario delle revisioni Revisione 3.5 04 giugno 2005 Corretto da: mc BOXBERRY release: aggiornamento importante. Revisione 3.6 28 agosto 2005 Corretto da: mc POKEBERRY release: aggiornamento per correzioni. Revisione 3.7 23 ottobre 2005 Corretto da: mc WHORTLEBERRY release: aggiornamento per correzioni.
Dedica
Per Anita, fonte di ogni magia
Sommario
Part 1. Introduzione..................................................................................................................................xi 1. Perch programmare la shell? ........................................................................................................1 2. Iniziare con #! ................................................................................................................................4 2.1. Eseguire uno script ............................................................................................................7 2.2. Esercizi preliminari ...........................................................................................................8 Part 2. I fondamenti.................................................................................................................................10 3. Caratteri speciali ..........................................................................................................................11 4. Introduzione alle variabili ed ai parametri ...................................................................................37 4.1. Sostituzione di variabile ..................................................................................................37 4.2. Assegnamento di variabile...............................................................................................40 4.3. Le variabili Bash non sono tipizzate................................................................................42 4.4. Tipi speciali di variabili ...................................................................................................43 5. Quoting.........................................................................................................................................49 5.1. Quoting di variabili..........................................................................................................50 5.2. Escaping ..........................................................................................................................51 6. Exit ed exit status .........................................................................................................................59 7. Veriche .......................................................................................................................................62 7.1. Costrutti condizionali ......................................................................................................62 7.2. Operatori di verica di le...............................................................................................70 7.3. Altri operatori di confronto .............................................................................................74 7.4. Costrutti condizionali if/then annidati .............................................................................81 7.5. Test sulla conoscenza delle veriche...............................................................................82 8. Operazioni ed argomenti correlati................................................................................................83 8.1. Operatori..........................................................................................................................83 8.2. Costanti numeriche..........................................................................................................91 Part 3. Oltre i fondamenti .......................................................................................................................94 9. Variabili riviste .............................................................................................................................95 9.1. Variabili interne ...............................................................................................................95 9.2. Manipolazione di stringhe .............................................................................................118 9.3. Sostituzione di parametro ..............................................................................................126 9.4. Tipizzare le variabili: declare o typeset........................................................................138 9.5. Referenziazione indiretta delle variabili........................................................................141 9.6. $RANDOM: genera un intero casuale ..........................................................................145 9.7. Il costrutto doppie parentesi ..........................................................................................156 10. Cicli ed alternative ...................................................................................................................159 10.1. Cicli .............................................................................................................................159 10.2. Cicli annidati ...............................................................................................................172 10.3. Controllo del ciclo .......................................................................................................173 10.4. Veriche ed alternative ................................................................................................177 11. Comandi interni e builtin .........................................................................................................187 11.1. Comandi di controllo dei job.......................................................................................218 12. Filtri, programmi e comandi esterni.........................................................................................224 12.1. Comandi fondamentali ................................................................................................224 12.2. Comandi complessi .....................................................................................................231 12.3. Comandi per ora/data ..................................................................................................242
iv
12.4. Comandi per lelaborazione del testo ..........................................................................246 12.5. Comandi inerenti ai le e allarchiviazione.................................................................273 12.6. Comandi per comunicazioni........................................................................................295 12.7. Comandi per il controllo del terminale........................................................................313 12.8. Comandi per le operazioni matematiche .....................................................................314 12.9. Comandi diversi...........................................................................................................326 13. Comandi di sistema e damministrazione ................................................................................342 13.1. Analisi di uno script di sistema ...................................................................................381 14. Sostituzione di comando ..........................................................................................................383 15. Espansione aritmetica ..............................................................................................................392 16. Redirezione I/O ........................................................................................................................394 16.1. Uso di exec ..................................................................................................................397 16.2. Redirigere blocchi di codice........................................................................................401 16.3. Applicazioni ................................................................................................................406 17. Here document .........................................................................................................................409 17.1. Here String...................................................................................................................420 18. Ricreazione...............................................................................................................................422 Part 4. Argomenti avanzati ...................................................................................................................423 19. Espressioni Regolari.................................................................................................................424 19.1. Una breve introduzione alle Espressioni Regolari.......................................................424 19.2. Globbing ......................................................................................................................431 20. Subshell ....................................................................................................................................434 21. Shell con funzionalit limitate. ................................................................................................438 22. Sostituzione di processo...........................................................................................................440 23. Funzioni ...................................................................................................................................444 23.1. Funzioni complesse e complessit delle funzioni .......................................................447 23.2. Variabili locali .............................................................................................................459 23.3. Ricorsivit senza variabili locali..................................................................................461 24. Alias .........................................................................................................................................465 25. Costrutti lista ............................................................................................................................468 26. Array ........................................................................................................................................472 27. /dev e /proc ...............................................................................................................................503 27.1. /dev ............................................................................................................................503 27.2. /proc ..........................................................................................................................505 28. Zero e Null ...............................................................................................................................511 29. Debugging ................................................................................................................................515 30. Opzioni.....................................................................................................................................527 31. Precauzioni...............................................................................................................................531 32. Stile dello scripting ..................................................................................................................541 32.1. Regole di stile non ufciali per lo scripting di shell....................................................541 33. Miscellanea ..............................................................................................................................545 33.1. Shell e script interattivi e non ......................................................................................545 33.2. Shell wrapper...............................................................................................................546 33.3. Veriche e confronti: alternative .................................................................................552 33.4. Ricorsivit....................................................................................................................552 33.5. Colorare con gli script..............................................................................................555 33.6. Ottimizzazioni .............................................................................................................571
33.7. Argomenti vari.............................................................................................................572 33.8. Sicurezza......................................................................................................................584 33.9. Portabilit ....................................................................................................................585 33.10. Lo scripting di shell in Windows...............................................................................586 34. Bash, versioni 2 e 3 ..................................................................................................................587 34.1. Bash, versione 2...........................................................................................................587 34.2. Bash, versione 3...........................................................................................................592 35. Note conclusive.................................................................................................................................595 35.1. Nota dellautore.....................................................................................................................595 35.2. A proposito dellautore .........................................................................................................595 35.3. Nota del traduttore ................................................................................................................596 35.4. Dove cercare aiuto.................................................................................................................596 35.5. Strumenti utilizzati per la produzione del libro ....................................................................597 35.5.1. Hardware ..................................................................................................................597 35.5.2. Software e Printware ................................................................................................597 35.6. Ringraziamenti ......................................................................................................................597 Bibliograa .............................................................................................................................................601 A. Script aggiuntivi................................................................................................................................611 B. Tabelle di riferimento .......................................................................................................................761 C. Una breve introduzione a Sed e Awk...............................................................................................767 C.1. Sed ..........................................................................................................................................767 C.2. Awk.........................................................................................................................................771 D. Codici di Exit con signicati speciali...............................................................................................775 E. Una dettagliata introduzione allI/O e alla redirezione I/O ..........................................................777 F. Opzioni standard da riga di comando .............................................................................................780 G. File importanti ..................................................................................................................................783 H. Importanti directory di sistema.......................................................................................................784 I. Localizzazione.....................................................................................................................................787 J. Cronologia dei comandi ....................................................................................................................791 K. Un esempio di le .bashrc ..............................................................................................................793 L. Conversione dei le batch di DOS in script di shell .......................................................................806 M. Esercizi ..............................................................................................................................................810 M.1. Analisi di script......................................................................................................................810 M.2. Scrivere script ........................................................................................................................811 N. Cronologia delle revisioni.................................................................................................................823 O. Siti per il download...........................................................................................................................825 P. Ancora da fare ...................................................................................................................................826 Q. Copyright...........................................................................................................................................828
vi
vii
9-28. Cambiare il seme di RANDOM .....................................................................................................154 9-30. Gestire le variabili in stile C...........................................................................................................156 10-19. Cicli annidati ................................................................................................................................172 13-11. killall, da /etc/rc.d/init.d ...................................................................................................381 14-2. Generare una variabile da un ciclo .................................................................................................387 16-1. Redirigere lo stdin usando exec...................................................................................................398 16-2. Redirigere lo stdout utilizzando exec ..........................................................................................398 16-3. Redirigere, nello stesso script, sia lo stdin che lo stdout con exec...........................................399 16-4. Evitare una subshell........................................................................................................................400 16-5. Ciclo while rediretto .......................................................................................................................401 16-6. Una forma alternativa di ciclo while rediretto ................................................................................402 16-7. Ciclo until rediretto ........................................................................................................................403 16-8. Ciclo for rediretto ...........................................................................................................................404 16-9. Ciclo for rediretto (rediretti sia lo stdin che lo stdout).............................................................405 16-10. Costrutto if/then rediretto .............................................................................................................405 16-11. File dati nomi.data usato negli esempi precedenti ....................................................................406 16-12. Eventi da registrare in un le di log .............................................................................................406 17-1. broadcast: invia un messaggio a tutti gli utenti connessi ..............................................................409 17-2. File di prova: crea un le di prova di due righe ............................................................................410 17-3. Messaggio di pi righe usando cat .................................................................................................411 17-4. Messaggio di pi righe con cancellazione dei caratteri di tabulazione ..........................................412 17-5. Here document con sostituzione di parametro ...............................................................................413 17-6. Caricare due le nella directory incoming di Sunsite.................................................................413 17-7. Sostituzione di parametro disabilitata ............................................................................................414 17-8. Uno script che genera un altro script..............................................................................................415 17-9. Here document e funzioni ..............................................................................................................416 17-10. Here document anonimo...........................................................................................................417 17-11. Commentare un blocco di codice .................................................................................................417 17-12. Uno script che si auto-documenta ................................................................................................418 17-13. Anteporre una riga in un le.........................................................................................................420 20-1. Ambito di una variabile in una subshell .........................................................................................434 20-2. Elenco dei proli utente .................................................................................................................435 20-3. Eseguire processi paralleli tramite le subshell ...............................................................................437 21-1. Eseguire uno script in modalit ristretta.........................................................................................438 23-1. Semplici funzioni ...........................................................................................................................444 23-2. Funzione con parametri ..................................................................................................................447 23-3. Funzioni e argomenti passati allo scrip da riga di comando ..........................................................448 23-4. Passare una referenziazione indiretta a una funzione.....................................................................449 23-5. Dereferenziare un parametro passato a una funzione.....................................................................450 23-6. Ancora, dereferenziare un parametro passato a una funzione........................................................450 23-13. Ricorsivit per mezzo di una variabile locale...............................................................................460 23-14. La torre di Hanoi ..........................................................................................................................462 24-1. Alias in uno script...........................................................................................................................465 24-2. unalias: abilitare e disabilitare un alias..........................................................................................467 26-1. Un semplice uso di array ................................................................................................................472 26-2. Impaginare una poesia....................................................................................................................474 26-3. Operazioni diverse sugli array........................................................................................................474 26-4. Operazioni sulle stringhe negli array..............................................................................................475
viii
26-5. Inserire il contenuto di uno script in un array ................................................................................478 26-6. Alcune propriet particolari degli array .........................................................................................478 26-7. Array vuoti ed elementi vuoti.........................................................................................................480 26-8. Inizializzare gli array......................................................................................................................483 26-9. Copiare e concatenare array ...........................................................................................................485 26-10. Ancora sulla concatenazione di array...........................................................................................486 26-11. Una vecchia conoscenza: il Bubble Sort ......................................................................................488 26-12. Array annidati e referenziazioni indirette.....................................................................................491 26-13. Applicazione complessa di array: Crivello di Eratostene ............................................................493 26-14. Simulare uno stack push-down.....................................................................................................495 26-15. Applicazione complessa di array: Esplorare strane serie matematiche .......................................498 26-16. Simulazione di un array bidimensionale, con suo successivo rovesciamento..............................499 27-1. Uso di /dev/tcp per la verica di una connessione.....................................................................504 27-2. Trovare il processo associato al PID ..............................................................................................507 27-3. Stato di una connessione ................................................................................................................509 29-1. Uno script errato.............................................................................................................................515 29-2. Parola chiave mancante ..................................................................................................................515 29-3. test24, un altro script errato............................................................................................................516 29-5. Trap di exit......................................................................................................................................519 29-6. Pulizia dopo un Control-C..............................................................................................................520 29-8. Esecuzione di processi multipli (su una postazione SMP).............................................................522 31-1. I confronti numerici e quelli di stringhe non si equivalgono..........................................................533 31-2. I trabocchetti di una subshell..........................................................................................................536 31-3. Concatenare con una pipe loutput di echo a read.........................................................................537 33-1. Shell wrapper ................................................................................................................................547 33-2. Uno shell wrapper leggermente pi complesso ............................................................................547 33-3. Uno shell wrapper generico che effettua una registrazione in un le di log ................................548 33-4. Uno shell wrapper per uno script awk ..........................................................................................549 33-5. Uno shell wrapper per un altro script awk....................................................................................549 33-6. Perl inserito in uno script Bash ......................................................................................................551 33-7. Script Bash e Perl combinati ..........................................................................................................551 33-8. Un (inutile) script che richiama s stesso ricorsivamente ..............................................................552 33-9. Un (utile) script che richiama s stesso ricorsivamente .................................................................553 33-10. Un altro (utile) script che richiama s stesso ricorsivamente .......................................................554 33-11. Una rubrica di indirizzi a colori................................................................................................555 33-12. Disegnare un rettangolo................................................................................................................557 33-13. Visualizzare testo colorato............................................................................................................561 33-14. Una gara ippica .........................................................................................................................563 34-1. Espansione di stringa......................................................................................................................587 34-2. Referenziazioni indirette a variabili - una forma nuova .................................................................587 34-3. Applicazione di un semplice database, con lutilizzo della referenziazione indiretta....................588 34-4. Utilizzo degli array e di vari altri espedienti per simulare la distribuzione casuale di un mazzo di carte a 4 giocatori ...........................................................................................................................589 A-1. mailformat: impaginare un messaggio e-mail................................................................................611 A-2. rn: una semplice utility per rinominare un le................................................................................612 A-3. blank-rename: rinomina le i cui nomi contengono spazi ............................................................613 A-4. encryptedpw: upload a un sito ftp utilizzando una password criptata localmente.........................614 A-5. copy-cd: copiare un CD di dati .......................................................................................................614
ix
A-6. Serie di Collatz ................................................................................................................................616 A-7. days-between: calcolo del numero di giorni intercorrenti tra due date ..........................................617 A-8. Creare un dizionario.....................................................................................................................620 A-9. Codica soundex .............................................................................................................................621 A-10. Game of Life ..............................................................................................................................624 A-11. File dati per Game of Life..........................................................................................................631 A-12. behead: togliere le intestazioni dai messaggi di e-mail e di news ................................................632 A-13. ftpget: scaricare le via ftp ...........................................................................................................632 A-14. password: generare password casuali di 8 caratteri......................................................................634 A-15. fo: eseguire backup giornalieri utilizzando le named pipe..........................................................636 A-16. Generare numeri primi utilizzando loperatore modulo................................................................636 A-17. tree: visualizzare lalbero di una directory....................................................................................638 A-18. string functions: funzioni per stringhe simili a quelle del C........................................................639 A-19. Informazioni sulle directory ..........................................................................................................644 A-20. Database object-oriented ...............................................................................................................655 A-21. Libreria di funzioni hash ...............................................................................................................656 A-22. Colorare del testo con le funzioni di hash .....................................................................................659 A-23. Montare le chiavi di memoria USB...............................................................................................661 A-24. Preservare i weblog .......................................................................................................................663 A-25. Proteggere le stringhe letterali.......................................................................................................665 A-26. Stringhe letterali non protette ........................................................................................................668 A-27. Identicare uno spammer ..............................................................................................................670 A-28. Caccia allo spammer......................................................................................................................711 A-29. Rendere wget pi semplice da usare .............................................................................................716 A-30. Uno script per il podcasting .......................................................................................................725 A-31. Fondamenti rivisitati......................................................................................................................727 A-32. Il comando cd esteso .....................................................................................................................747 C-1. Conteggio delle occorrenze di lettere ..............................................................................................772 K-1. Esempio di le .bashrc .................................................................................................................793 L-1. VIEWDATA.BAT: le batch DOS...................................................................................................808 L-2. viewdata.sh: Script di shell risultante dalla conversione di VIEWDATA.BAT ...............................809
Part 1. Introduzione
La shell un interprete di comandi. Molto pi che una semplice interfaccia tra il kernel del sistema operativo e lutilizzatore, anche un vero e proprio potente linguaggio di programmazione. Un programma di shell, chiamato script, uno strumento semplice da usare per creare applicazioni incollando insieme chiamate di sistema, strumenti, utility e le binari (eseguibili). Uno script di shell pu utilizzare virtualmente lintero repertorio di comandi, utility e strumenti UNIX. Se ci non fosse abbastanza, i comandi interni della shell, come i costrutti di verica ed i cicli, forniscono ulteriore potenza e essibilit agli script. Questi si prestano eccezionalmente bene a compiti di amministrazione di sistema e a lavori ripetitivi e di routine, senza lenfasi di un complesso, e fortemente strutturato, linguaggio di programmazione.
In compiti che richiedono un utilizzo intenso di risorse, specialmente quando la velocit un fattore determinante (ordinamenti, hashing, ecc.) In procedure che comprendono operazioni matematiche complesse, specialmente aritmetica in virgola mobile, calcoli in precisione arbitraria o numeri complessi (si usi C++ o FORTRAN) necessaria la portabilit (si usi, invece, il C o Java)
In applicazioni complesse dove necessaria la programmazione strutturata (necessit di tipizzazione delle variabili, prototipi di funzione, ecc.) In applicazioni particolari su cui si sta rischiando il tutto per tutto, o il futuro della propria azienda In situazioni in cui la sicurezza importante, dove occorre garantire lintegrit del sistema e proteggerlo contro intrusioni, cracking e vandalismi In progetti costituiti da sotto-componenti con dipendenze interconnesse Sono richieste operazioni su le di grandi dimensioni (Bash si limita ad un accesso sequenziale ai le, eseguito riga per riga e in un modo particolarmente goffo ed inefciente) necessario il supporto nativo per gli array multidimensionali Sono necessarie strutture di dati quali le liste collegate o gli alberi necessario generare o manipolare graci o GUI necessario un accesso diretto allhardware del sistema necessaria una porta o un socket I/O necessario lutilizzo di librerie o interfacce per lesecuzione di vecchio codice In applicazioni proprietarie a codice chiuso (il codice sorgente degli script di shell aperto e tutti lo possono esaminare)
Nel caso ci si trovi di fronte ad una o pi delle eventualit appena descritte, occorre prendere in considerazione un linguaggio di scripting pi potente -- che potrebbe essere Perl, Tcl, Python, Ruby -- o possibilmente un linguaggio compilato di alto livello, quale il C, C++ o Java. Anche in questo caso, per, eseguire dei prototipi di unapplicazione come script di shell potrebbe costituire unutile base di sviluppo. Sar utilizzata Bash, acronimo di Bourne-Again shell, e un po un gioco di parole sullormai classica shell Bourne di Stephen Bourne. Bash diventata uno standard de facto dello scripting di shell su ogni variante di sistema UNIX. La maggior parte dei principi spiegati in questo libro pu essere applicata altrettanto bene allo scripting con altre shell, quale la Shell Korn, da cui Bash ha derivato alcune delle sue funzionalit 2 e la Shell C e le sue varianti (si faccia attenzione che programmare con la shell C non raccomandabile a causa di alcuni problemi ad essa inerenti, come evidenziato da Tom Christiansen in un post su Usenet (http://www.etext.org/Quartz/computer/unix/csh.harmful.gz) nellOttobre 1993). Quello che segue un manuale sullo scripting di shell che sfrutta i numerosi esempi per illustrare le varie funzionalit della shell. Gli script di esempio funzionano correttamente -- sono stati vericati, per quanto sia stato possibile -- e alcuni di essi possono persino essere impiegati per scopi pratici. Il lettore pu divertirsi con il codice degli esempi presenti nellarchivio dei sorgenti (nomescript.sh oppure nomescript.bash), 3 attribuirgli i permessi di esecuzione (con chmod u+rx nomescript), quindi eseguirli e vedere cosa succede. Se larchivio dei sorgenti non dovesse essere disponibile, allora si ricorra ad un taglia-incolla dalle versioni HTML (http://www.tldp.org/LDP/abs/abs-guide.html.tar.gz), pdf (http://www.tldp.org/LDP/abs/abs-guide.pdf) o testo (http://www.ibiblio.org/pub/Linux/docs/linux-doc-project/abs-guide/abs-guide.txt.gz). Si faccia attenzione che alcuni degli script qui riportati anticipano alcune funzionalit che non sono state ancora spiegate e questo richiede, per la loro comprensione, che il lettore dia uno sguardo ai capitoli successivi.
Capitolo 1. Perch programmare la shell? Se non altrimenti specicato, gli script di esempio che seguono sono stati scritti dallautore (mailto:[email protected]).
Note
1. Ad esse ci si riferisce come builtin, funzionalit interne alla shell. 2. Molti degli elementi di ksh88 ed anche alcuni della pi aggiornata ksh93 sono stati riuniti in Bash. 3. Convenzionalmente, agli script creati da un utente che sono compatibili con la shell Bourne generalmente viene dato un nome con estensione .sh. Gli script di sistema, come quelli che si trovano nel le /etc/rc.d, non seguono questa regola.
Come si pu vedere, non c niente di insolito, solo una serie di comandi che potrebbero essere eseguiti uno ad uno dalla riga di comando di una console o di un xterm. I vantaggi di collocare dei comandi in uno script vanno, per, ben al di l del non doverli reimmettere ogni volta. Lo script, infatti, pu essere modicato, personalizzato o generalizzato per unapplicazione particolare. Esempio 2-2. cleanup: Lo script clean-up migliorato
#!/bin/bash # Corretta intestazione di uno script Bash. # Cleanup, versione 2 # Da eseguire come root, naturalmente. # Qui va inserito il codice che visualizza un messaggio derrore e luscita #+ dallo script nel caso lesecutore non sia root. DIR_LOG=/var/log # Meglio usare le variabili che codificare dei valori. cd $DIR_LOG cat /dev/null > messages cat /dev/null > wtmp
echo "Log cancellati." exit # Metodo corretto per "uscire" da uno script.
Capitolo 2. Iniziare con #! Esempio 2-3. cleanup: Una versione avanzata e generalizzata degli script precedenti.
#!/bin/bash # Cleanup, versione 3 # # # #+ # #+ Attenzione: ----------In questo script sono presenti alcune funzionalit che verranno spiegate pi avanti. Quando avrete ultimato la prima met del libro, forse non vi apparir pi cos misterioso.
Solo gli utenti con $UID 0 hanno i privilegi di root. Numero prestabilito di righe salvate. Riesco a cambiare directory? Codice di exit non-root.
# Da eseguire come root, naturalmente. if [ "$UID" -ne "$ROOT_UID" ] then echo "Devi essere root per eseguire questo script." exit $E_NONROOT fi if [ -n "$1" ] # Verifica se presente unopzione da riga di comando (non-vuota). then linee=$1 else linee=$LINEE # Valore preimpostato, se non specificato da riga di comando. fi
# Stephane Chazelas suggerisce il codice seguente, #+ come metodo migliore per la verifica degli argomenti da riga di comando, #+ ma ancora un po prematuro a questo punto del manuale. # # E_ERR_ARG=65 # Argomento non numerico (formato dellargomento non valido) # # case "$1" in # "" ) linee=50;; # *[!0-9]*) echo "Utilizzo: basename $0 file-da-cancellare"; exit\ # $E_ERR_ARG;; # * ) linee=$1;; # esac # #* Vedere pi avanti al capitolo "Cicli" per la comprensione delle righe #+ precedenti.
cd $DIR_LOG if [ pwd != "$DIR_LOG" ] then echo exit fi # #+ # o if [ "$PWD" != "$DIR_LOG" ] # Non siamo in /var/log?
"Non riesco a cambiare in $DIR_LOG." $E_XCD Doppia verifica per vedere se ci troviamo nella directory corretta, prima di cancellare il file di log.
# ancora pi efficiente: # # cd /var/log || { # echo "Non riesco a spostarmi nella directory stabilita." >&2 # exit $E_XCD; # }
tail -$linee messages > mesg.temp # Salva lultima sezione del file di # log messages. mv mesg.temp messages # Diventa la nuova directory di log.
# cat /dev/null > messages #* Non pi necessario, perch il metodo precedente pi sicuro. cat /dev/null > wtmp # echo "Log cancellati." : > wtmp e > wtmp hanno lo stesso effetto.
exit 0 # Il valore di ritorno zero da uno script #+ indica alla shell la corretta esecuzione dello stesso.
Poich non si voleva cancellare lintero log di sistema, questa versione dello script mantiene inalterata lultima sezione del le di log messages. Si scopriranno continuamente altri modi per rinire gli script precedenti ed aumentarne lefcienza. I caratteri ( #!), allinizio dello script, informano il sistema che il le contiene una serie di comandi che devono essere passati allinterprete indicato. I caratteri #! in realt sono un magic number 1 di due byte, vale a dire un identicatore speciale che designa il tipo di le o, in questo caso, uno script di shell eseguibile (eseguite man magic per ulteriori dettagli su questo affascinante argomento). Immediatamente dopo #! compare un percorso. Si tratta del percorso al programma che deve interpretare i comandi contenuti nello script, sia esso una shell, un linguaggio di programmazione o una utility. Linterprete esegue quindi i comandi dello script, partendo dallinizio (la riga successiva a #!) e ignorando i commenti. 2
Ognuna delle precedenti intestazioni di script richiama un differente interprete di comandi, sia esso /bin/sh, la shell (bash in un sistema Linux) o altri. 3 Lutilizzo di #!/bin/sh, la shell Bourne predenita nella maggior parte delle varie distribuzioni commerciali UNIX, rende lo script portabile su macchine non-Linux, sebbene questo signichi sacricare alcune funzionalit speciche di Bash. Lo script sar, comunque, conforme allo standard POSIX 4 sh. importante notare che il percorso specicato dopo #! deve essere esatto, altrimenti un messaggio derrore -- solitamente Command not found -- sar lunico risultato dellesecuzione dello script. #! pu essere omesso se lo script formato solamente da una serie di comandi specici di sistema e non utilizza direttive interne della shell. Il secondo esempio ha richiesto #! perch la riga di assegnamento di variabile, linee=50, utilizza un costrutto specico della shell. da notare ancora che #!/bin/sh invoca linterprete di shell predenito, che corrisponde a /bin/bash su una macchina Linux.
Suggerimento: Questo manuale incoraggia lapproccio modulare nella realizzazione di uno script. Si annotino e si raccolgano come ritagli i frammenti di codice che potrebbero rivelarsi utili per degli script futuri. Addirittura si potrebbe costruire una libreria piuttosto ampia di routine. Come, ad esempio, la seguente parte introduttiva di uno script che verica se lo stesso stato eseguito con il numero corretto di parametri.
E_ERR_ARGS=65 parametri_dello_script="-a -h -m -z" # -a = all, -h = help, ecc. if [ $# -ne $Numero_di_argomenti_attesi ] then echo "Utilizzo: basename $0 $parametri_dello_script" exit $E_ERR_ARG fi
Spesso scriverete uno script che svolge un compito specico. Il primo script di questo capitolo ne rappresenta un esempio. Successivamente potrebbe sorgere la necessit di generalizzare quello script, in modo che possa svolgere altri compiti simili. Sostituire le costanti letterali (codicate) con delle variabili rappresenta un passo in tale direzione, cos come sostituire blocchi di codice che si ripetono con delle funzioni.
Una volta reso eseguibile, se ne pu vericare la funzionalit con ./nomescript. 7 Se la prima riga inizia con i caratteri #! , allavvio lo script chiamer, per la propria esecuzione, linterprete dei comandi specicato. Come ultimo passo, dopo la verica e il debugging, probabilmente si vorr spostare lo script nella directory /usr/local/bin (operazione da eseguire come root) per renderlo disponibile, oltre che per se stessi, anche agli altri utenti, quindi come eseguibile di sistema. In questo modo lo script potr essere messo in esecuzione semplicemente digitando nomescript [INVIO] da riga di comando.
Note
1. Alcune versioni UNIX (quelle basate su 4.2BSD) utilizzano un magic number a quattro byte, che richiede uno spazio dopo il ! -- #! /bin/sh. 2. La riga con #! dovr essere la prima cosa che linterprete dei comandi (sh o bash) incontra. In caso contrario, dal momento che questa riga inizia con #, verrebbe correttamente interpretata come un commento.
Provate anche a far iniziare un le README con #!/bin/more e rendetelo eseguibile. Il risultato sar la visualizzazione automatica del le di documentazione. (Un here document con luso di cat sarebbe probabilmente unalternativa migliore -- vedi Esempio 17-3).
4. Portable Operating System Interface, un tentativo di standardizzare i SO di tipo UNIX. Le speciche POSIX sono elencate sul sito del Open Group (http://www.opengroup.org/onlinepubs/007904975/toc.htm). 5. Attenzione: richiamando uno script Bash con sh nomescript si annullano le estensioni speciche di Bash e, di conseguenza, se ne potrebbe compromettere lesecuzione. 6. Uno script, per essere eseguito, ha bisogno, oltre che del permesso di esecuzione, anche di quello di lettura perch la shell deve essere in grado di leggerlo. 7. Perch non invocare semplicemente uno script con nomescript? Se la directory in cui ci si trova ($PWD) anche quella dove nomescript collocato, perch il comando non funziona? Il motivo che, per ragioni di sicurezza, la directory corrente, di default, non viene inclusa nella variabile $PATH dellutente. quindi necessario invocare esplicitamente lo script che si trova nella directory corrente con ./nomescript.
Part 2. I fondamenti
Sono considerati commenti anche quelli che seguono uno o pi spazi posti allinizio di una riga.
Cautela
Non possibile inserire, sulla stessa riga, un comando dopo un commento. Non esiste alcun metodo per terminare un commento in modo che si possa inserire del codice eseguibile sulla stessa riga. indispensabile porre il comando in una nuova riga.
Nota: Naturalmente, un # preceduto da un carattere di escape in un enunciato echo non verr considerato come un commento. Inoltre, il # compare in alcuni costrutti di sostituzione di parametro e nelle espressioni con costanti numeriche.
echo echo echo echo "Il presente # non inizia un commento." Il presente # non inizia un commento. Il presente \# non inizia un commento. Il presente # inizia un commento. # una sostituzione di parametro, non un commento. # una conversione di base, non un commento.
11
; Separatore di comandi [punto e virgola]. Permette di inserire due o pi comandi sulla stessa riga.
if [ -x "$nomefile" ]; then
# Notate che "if" e "then" hanno bisogno del #+ punto e virgola. Perch? echo "Il file $nomefile esiste."; cp $nomefile $nomefile.bak else echo "$nomefile non trovato."; touch $nomefile fi; echo "Verifica di file completata."
case "$variabile" in abc) echo "\$variabile = abc" ;; xyz) echo "\$variabile = xyz" ;; esac
Comando punto [punto]. Equivale a source (vedi Esempio 11-20). un builtin bash.
. punto, componente dei nomi dei le. Quando si ha a che fare con i nomi dei le si deve sapere che il punto il presso dei le nascosti, le che un normale comando ls non visualizza.
bash$ touch .file_nascosto bash$ ls -l total 10 -rw-r--r-1 bozo 4034 Jul 18 22:04 data1.addressbook
12
bash$ ls -al total 14 drwxrwxr-x 2 bozo drwx-----52 bozo -rw-r--r-1 bozo -rw-r--r-1 bozo -rw-r--r-1 bozo -rw-rw-r-1 bozo
29 29 18 25 17 29
Se si considerano i nomi delle directory, un punto singolo rappresenta la directory di lavoro corrente, mentre due punti indicano la directory superiore.
bash$ pwd /home/bozo/projects bash$ cd . bash$ pwd /home/bozo/projects bash$ cd .. bash$ pwd /home/bozo/
Il punto appare spesso come destinazione (directory) nei comandi di spostamento di le.
bash$ cp /home/bozo/current_work/junk/* .
. punto corrispondenza di carattere. Nella ricerca di caratteri, come parte di una espressione regolare, il punto verica un singolo carattere. " quoting parziale [doppio apice]. "STRINGA" preserva (dallinterpretazione della shell) la maggior parte dei caratteri speciali che dovessero trovarsi allinterno di STRINGA. Vedi anche Capitolo 5.
13
Capitolo 3. Caratteri speciali quoting totale [apice singolo]. STRINGA preserva (dallinterpretazione della shell) tutti i caratteri speciali che dovessero trovarsi allinterno di STRINGA. Questa una forma di quoting pi forte di ". Vedi anche Capitolo 5. , operatore virgola. Loperatore virgola concatena una serie di operazioni aritmetiche. Vengono valutate tutte, ma viene restituita solo lultima.
let "t2 = ((a = 9, 15 / 3))" # Imposta "a" e "t2 = 15 / 3".
\ escape [barra inversa]. Strumento per il quoting di caratteri singoli. \X preserva il carattere X . Equivale ad effettuare il quoting di X , vale a dire X. La \ si utilizza per il quoting di " e , afnch siano interpretati letteralmente. Vedi Capitolo 5 per una spiegazione approfondita dei caratteri di escape.
/ Separatore nel percorso dei le [barra]. Separa i componenti del nome del le (come in /home/bozo/projects/Makefile). anche loperatore aritmetico di divisione.
sostituzione di comando. Il costrutto comando rende disponibile loutput di comando per lassegnamento ad una variabile. conosciuto anche come apice inverso o apostrofo inverso. :
comando null [due punti]. lequivalente shell di NOP (no op, operazione non-far-niente). Pu essere considerato un sinonimo del builtin di shell true. Il comando : esso stesso un builtin Bash, ed il suo exit status true (0).
: echo $?
# 0
Ciclo innito:
14
if condizione then : # Non fa niente e salta alla prossima istruzione else fa-qualcosa fi
Fornisce un segnaposto dove attesa unoperazione binaria, vedi Esempio 8-2 e parametri predeniti.
: ${nomeutente=whoami} # ${nomeutente=whoami} #
Fornisce un segnaposto dove atteso un comando in un here document. Vedi Esempio 17-10. Valuta una stringa di variabili utilizzando la sostituzione di parametro (come in Esempio 9-14).
: ${HOSTNAME?} ${USER?} ${MAIL?} # Visualizza un messaggio derrore se una, o pi, delle variabili #+ fondamentali dambiente non impostata.
15
Capitolo 3. Caratteri speciali In combinazione con >, loperatore di redirezione, azzera il contenuto di un le, senza cambiarne i permessi. Se il le non esiste, viene creato.
: > data.xxx # Ora il file "data.xxx" vuoto.
# Ha lo stesso effetto di cat /dev/null > data.xxx # Tuttavia non viene generato un nuovo processo poich ":" un builtin.
Vedi anche Esempio 12-14. In combinazione con loperatore di redirezione >> non ha alcun effetto su un preesistente le di riferimento (: >> file_di_riferimento). Se il le non esiste, viene creato.
Nota: Si utilizza solo con i le regolari, non con con le pipe, i link simbolici ed alcuni le particolari.
Pu essere utilizzato per iniziare una riga di commento, sebbene non sia consigliabile. Utilizzando # si disabilita la verica derrore sulla parte restante di quella riga, cos nulla verr visualizzato dopo il commento. Questo non succede con :.
: Questo un commento che genera un errore, (if [ $x -eq 3] ).
I : servono anche come separatore di campo nel le /etc/passwd e nella variabile $PATH.
bash$ echo $PATH /usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/sbin:/usr/sbin:/usr/games
inverte (o nega) il senso di una verica o di un exit status [punto esclamativo]. Loperatore ! inverte lexit status di un comando a cui stato anteposto (vedi Esempio 6-2). Cambia anche il signicato di un operatore di verica. Pu, per esempio, cambiare il senso di uguale ( = ) in non uguale ( != ). Loperatore ! una parola chiave Bash. In un contesto differente, il ! appare anche nelle referenziazioni indirette di variabili. Ancora, da riga di comando il ! invoca il meccanismo della cronologia di Bash (vedi Appendice J). da notare che, allinterno di uno script, il meccanismo della cronologia disabilitato.
16
Capitolo 3. Caratteri speciali * carattere jolly [asterisco]. Il carattere * serve da carattere jolly per lespansione dei nomi dei le nel globbing. Da solo, ricerca tutti i le di una data directory.
* operatore aritmetico. Nellambito delle operazioni aritmetiche, l * indica loperatore di moltiplicazione. Il doppio asterisco, **, loperatore di elevamento a potenza.
? operatore di verica. In certe espressioni, il ? indica la verica di una condizione. In un costrutto parentesi doppie, il ? viene utilizzato come operatore ternario in stile C. Vedi Esempio 9-30. Nella sostituzione di parametro, il ? verica se una variabile stata impostata.
? carattere jolly. Il carattere ? serve da carattere jolly per un singolo carattere, nellespansione dei nomi dei le nel globbing, cos come rappresenta un singolo carattere in una espressione regolare estesa. $ Sostituzione di variabile (contenuto di una variabile).
var1=5 var2=23skidoo echo $var1 echo $var2 # 5 # 23skidoo
Il $ davanti al nome di una variabile rimanda al valore contenuto nella variabile stessa.
17
$ ne-riga. In una espressione regolare, il $ rinvia alla ne della riga di testo. ${} Sostituzione di parametro. $* $@ Parametri posizionali. $? variabile exit status. La variabile $? contiene lexit status di un comando, di una funzione o dello stesso script. $$ variabile ID di processo. La variabile $$ contiene lID di processo dello script in cui appare. () gruppo di comandi.
(a=ciao; echo $a)
Importante: Un elenco di comandi racchiuso da parentesi d luogo ad una subshell. Le variabili allinterno delle parentesi, appartenenti quindi alla subshell, non sono visibili dallo script. Il processo genitore, lo script, non pu leggere le variabili create nel processo glio, la subshell.
a=123 ( a=321; ) echo "a = $a" # a = 123 # "a" tra parentesi si comporta come una variabile locale.
inizializzazione di array.
Array=(elemento1 elemento2 elemento3)
18
Il comando agisce sullelenco dei le, separati da virgole, specicati tra le parentesi graffe. Lespansione dei nomi dei le (il globbing) viene applicata a quelli elencati tra le parentesi.
Cautela
Non consentito alcuno spazio dentro le parentesi, tranne il caso in cui si utilizzi il "quoting" o se preceduto da un carattere di escape.
{}
Blocco di codice [parentesi graffe]. Conosciuto anche come gruppo inline, questo costrutto crea una funzione anonima. Tuttavia, a differenza di una funzione, le variabili presenti nel blocco rimangono visibili alla parte restante dello script.
bash$ { local a;
a=123; }
bash: local: can only be used in a function
# a = 321
La porzione di codice racchiusa tra le parentesi graffe pu avere lI/O rediretto da e verso se stessa. Esempio 3-1. Blocchi di codice e redirezione I/O
#!/bin/bash # Legge le righe del file /etc/fstab.
19
File=/etc/fstab { read riga1 read riga2 } < $File echo echo echo echo echo "La prima riga di $File :" "$riga1" "La seconda riga di $File :" "$riga2"
exit 0 # Ora, come sarebbe possibile verificare i diversi campi di ciascuna riga? # Suggerimento: usate awk.
20
Nota: A differenza di un gruppo di comandi racchiuso da (parentesi), visto in precedenza, una porzione di codice allinterno delle {parentesi graffe} solitamente non d vita ad una subshell. 2
{} \; percorso del le. Per lo pi utilizzata nei costrutti nd. Non un builtin di shell.
Nota: Il ; termina la sintassi dellopzione -exec del comando nd. Deve essere preceduto dal carattere di escape per impedirne la reinterpretazione da parte della shell.
[] verica. Verica lespressione tra [ ]. da notare che [ parte del builtin di shell test (ed anche suo sinonimo), non un link al comando esterno /usr/bin/test.
[[ ]] verica. Verica lespressione tra [[ ]] (parola chiave di shell). Vedi la disamina sul costrutto [[ ... ]].
[] elemento di un array.
21
Capitolo 3. Caratteri speciali Nellambito degli array, le parentesi quadre vengono impiegate nellimpostazione dei singoli elementi di quellarray.
Array[1]=slot_1 echo ${Array[1]}
[] intervallo di caratteri. Come parte di unespressione regolare, le parentesi quadre indicano un intervallo di caratteri da ricercare.
(( )) espansione di espressioni intere. Espande e valuta lespressione intera tra (( )). Vedi la disamina sul costrutto (( ... )).
> &> >&>> < redirezione. nome_script >nome_file redirige loutput di nome_script nel le nome_file. Sovrascrive nome_file nel caso fosse gi esistente. comando &>nome_file redirige sia lo stdout che lo stderr di comando in nome_file. comando >&2 redirige lo stdout di comando nello stderr. nome_script >>nome_file accoda loutput di nome_script in nome_file. Se nome_file non esiste, viene creato. sostituzione di processo. (comando)>
<(comando) In un altro ambito, i caratteri < e > vengono utilizzati come operatori di confronto tra stringhe.
22
Capitolo 3. Caratteri speciali In un altro ambito ancora, i caratteri < e > vengono utilizzati come operatori di confronto tra interi. Vedi anche Esempio 12-9.
<< redirezione utilizzata in un here document. <<< redirezione utilizzata in una here string. < > Confronto ASCII.
veg1=carote veg2=pomodori if [[ "$veg1" < "$veg2" ]] then echo "Sebbene nel dizionario $veg1 preceda $veg2," echo "questo non intacca le mie preferenze culinarie." else echo "Che razza di dizionario stai usando?" fi
pipe. Passa loutput del comando che la precede come input del comando che la segue, o alla shell. il metodo per concatenare comandi.
echo ls -l | sh # Passa loutput di "echo ls -l" alla shell, #+ con lo stesso risultato di "ls -l".
cat *.lst | sort | uniq # Unisce ed ordina tutti i file ".lst", dopo di che cancella le righe doppie.
23
Una pipe, metodo classico della comunicazione tra processi, invia lo stdout di un processo allo stdin di un altro. Nel caso tipico di un comando, come cat o echo, collega un usso di dati da elaborare ad un ltro (un comando che trasforma il suo input). cat $nome_file1 $nome_file2 | grep $parola_da_cercare
Loutput di uno o pi comandi pu essere collegato con una pipe ad uno script.
#!/bin/bash # uppercase.sh : Cambia linput in caratteri maiuscoli. tr # #+ #+ a-z A-Z Per lintervallo delle lettere deve essere utilizzato il "quoting" per impedire di creare file aventi per nome le singole lettere dei nomi dei file.
exit 0
Nota: In una pipe, lo stdout di ogni processo deve essere letto come stdin del successivo. Se questo non avviene, il usso di dati si blocca. La pipe non si comporter come ci si poteva aspettare.
cat file1 file2 | ls -l | sort # Loutput proveniente da "cat file1 file2" scompare.
Una pipe viene eseguita come processo glio e quindi non pu modicare le variabili dello script.
variabile="valore_iniziale" echo "nuovo_valore" | read variabile echo "variabile = $variabile" # variabile = valore_iniziale
Se uno dei comandi della pipe abortisce, questo ne determina linterruzione prematura. Chiamata pipe interrotta, questa condizione invia un segnale SIGPIPE .
24
Capitolo 3. Caratteri speciali >| forza la redirezione (anche se stata impostata lopzione noclobber) . Ci provoca la sovrascrittura forzata di un le esistente. || operatore logico OR. In un costrutto condizionale, loperatore || restituir 0 (successo) se almeno una delle condizioni di verica valutate vera. & Esegue un lavoro in background. Un comando seguito da una & verr eseguito in background (sullo sfondo).
sleep 10
In uno script possono essere eseguiti in background sia i comandi che i cicli . Esempio 3-3. Eseguire un ciclo in background
#!/bin/bash # background-loop.sh for i in 1 2 3 4 5 6 7 8 9 10 # Primo ciclo. do echo -n "$i " done & # Esegue questo ciclo in background. # Talvolta verr eseguito, invece, il secondo ciclo. echo # Questo echo alcune volte non verr eseguito. # Secondo ciclo.
25
exit 0 # Per divertirsi veramente, #+ Nasimuddin Ansari suggerisce laggiunta di sleep 1 #+ dopo i comandi echo -n "$i" delle righe 6 e 14.
Cautela
Un comando eseguito in background allinterno di uno script pu provocarne linterruzione, in attesa che venga premuto un tasto. Fortunatamente, per questa eventualit esiste un rimedio.
&& operatore logico AND . In un costrutto condizionale, loperatore && restituir 0 (successo) solo se tutte le condizioni vericate sono vere. opzione, presso. Presso di opzione di un comando o di un ltro. Presso di un operatore. COMANDO -[Opzione1][Opzione2][...]
ls -al
set -- $variabile
if [ $file1 -ot $file2 ] then echo "Il file $file1 pi vecchio di $file2." fi if [ "$a" -eq "$b" ]
26
(cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xpvf -) # Sposta lintero contenuto di una directory in unaltra # [cortesia di Alan Cox <[email protected]>, con una piccola modifica] # # # # # # # # # # # # # # # # # # # 1) cd /source/directory 2) && 3) tar cf - . Directory sorgente, dove sono contenuti i file che devono essere spostati. "lista And": se loperazione cd ha successo, allora viene eseguito il comando successivo. Lopzione c del comando di archiviazione tar crea un nuovo archivio, lopzione f (file), seguita da - designa come file di destinazione lo sdtout, e lo fa nella directory corrente (.). Collegato a... subshell Cambia alla directory di destinazione. "lista And", come sopra Scompatta larchivio (x), mantiene i permessi e le propriet dei file (p), invia messaggi dettagliati allo stdout (v), leggendo i dati dallo stdin (f seguito da -) Attenzione: x un comando, mentre p, v ed f sono opzioni.
4) 5) 6) 7) 8)
Whew!
# Pi elegante, ma equivalente a: # cd source-directory # tar cf - . | (cd ../dest/directory; tar xpvf -) # # Ottengono lo stesso rirultato anche: # cp -a /source/directory/* /dest/directory # Oppure: # cp -a /source/directory/* /source/directory/.[^.]* /dest/directory # Nel caso ci siano file nascosti in /source/directory.
27
Va notato che, in questo contesto, il - non , di per s un operatore Bash, ma piuttosto unopzione riconosciuta da alcune utility UNIX che scrivono allo stdout, come tar, cat, ecc.
Dove atteso un nome di le, il - redirige loutput allo stdout (talvolta con tar cf), o accetta linput dallo stdin, invece che da un le. un metodo per utilizzare lutility come ltro in una pipe.
bash$ file Usage: file [-bciknvzL] [-f filename] [-m magicfiles] file...
Eseguito da solo, da riga di comando, le genera un messaggio derrore. Occorre aggiungere il - per un migliore risultato. Lesempio seguente fa s che la shell attenda linput dallutente.
bash$ file -
abc
standard input: ASCII text
bash$ file -
#!/bin/bash
standard input: Bourne-Again shell script text executable
Ora il comando accetta linput dallo stdin e lo analizza. Il - pu essere utilizzato per collegare lo stdout ad altri comandi. Ci permette alcune acrobazie, come aggiungere righe allinizio di un le. Utilizzare diff per confrontare un le con la sezione di un altro: grep Linux file1 | diff file2 -
28
Capitolo 3. Caratteri speciali Inne, un esempio concreto di come usare il - con tar. Esempio 3-4. Backup di tutti i le modicati il giorno precedente
#!/bin/bash # Salvataggio di tutti i file della directory corrente che sono stati #+ modificati nelle ultime 24 ore in un archivio "tarball" (file trattato #+ con tar e gzip). FILEBACKUP=backup-$(date +%d-%m-%Y) # Inserisce la data nel nome del file di salvataggio. # Grazie a Joshua Tschida per lidea. archivio=${1:-$FILEBACKUP} # Se non viene specificato un nome di file darchivio da riga di comando, #+ questo verr impostato a "backup-GG-MM-AAAA.tar.gz." tar cvf - find . -mtime -1 -type f -print > $archivio.tar gzip $archivio.tar echo "Directory $PWD salvata nel file \"$archivio.tar.gz\"."
# Stephane Chazelas evidenzia che il precedente codice fallisce lesecuzione #+ se incontra troppi file o se un qualsiasi nome di file contiene caratteri #+ di spaziatura. # Suggerisce, quindi, le seguenti alternative: # ------------------------------------------------------------------# find . -mtime -1 -type f -print0 | xargs -0 tar rvf "$archivio.tar" # utilizzando la versione GNU di "find".
# find . -mtime -1 -type f -exec tar rvf "$archivio.tar" {} \; # portabile su altre versioni UNIX, ma molto pi lento. # -------------------------------------------------------------------
exit 0
29
Cautela
Nomi di le che iniziano con - possono provocare problemi quando vengono utilizzati con il - come operatore di redirezione. Uno script potrebbe vericare questa possibilit ed aggiungere un presso adeguato a tali nomi, per esempio ./-NOMEFILE, $PWD/-NOMEFILE o $PATHNAME/-NOMEFILE.
Anche il valore di una variabile che inizia con un - potrebbe creare problemi.
var="-n" echo $var # Ha leffetto di un
directory di lavoro precedente. Il comando cd - cambia alla directory di lavoro precedente. Viene utilizzata la variabile dambiente $OLDPWD.
Cautela
Non bisogna confondere il - utilizzato in questo senso con loperatore di redirezione - appena discusso. Linterpretazione del - dipende dal contesto in cui appare.
+ Pi. Operatore aritmetico di addizione. In un contesto differente, il simbolo + un operatore di Espressione Regolare.
30
+ Opzione. Opzione per un comando o un ltro. Alcuni comandi e builtins utilizzano il segno + per abilitare certe opzioni ed il segno - per disabilitarle.
% modulo. Modulo (resto di una divisione) , operatore aritmetico. In un contesto differente, il simbolo % loperatore di ricerca di corrispondenza.
~ directory home [tilde]. Corrisponde alla variabile interna $HOME. ~bozo la directory home di bozo, e ls ~bozo elenca il suo contenuto. ~/ la directory home dellutente corrente e ls ~/ elenca il suo contenuto.
bash$ echo ~bozo /home/bozo bash$ echo ~ /home/bozo bash$ echo ~/ /home/bozo/ bash$ echo ~: /home/bozo: bash$ echo ~utente-inesistente ~utente-inesistente
~+ directory di lavoro corrente. Corrisponde alla variabile interna $PWD. ~directory di lavoro precedente. Corrisponde alla variabile interna $OLDPWD. =~ verica di espressione regolare. Questo operatore stato introdotto con la versione 3 di Bash. ^ inizio-riga. In una espressione regolare, un ^ rinvia allinizio di una riga di testo.
31
modicano il comportamento di un terminale o la visualizzazione di un testo. Un carattere di controllo la combinazione di CONTROL + tasto. Normalmente, i caratteri di controllo, inseriti in uno script, non sono utili.
Ctl-D Uscita dalla shell (simile a exit). EOF (end of le). Anchesso termina linput dallo stdin. Durante la digitazione di un testo in una console o in una nestra xterm, Ctl-D cancella il carattere che si trova sotto al cursore. Quando non ci sono pi caratteri, Ctl-D determina la prevista uscita dalla sessione. In una nestra xterm, questo ha come effetto la chiusura della nestra stessa.
Ctl-G SEGNALE ACUSTICO (beep). Su alcune vecchie telescriventi faceva suonare veramente una campanella
Ctl-H Backspace (ritorno distruttivo). Cancella i caratteri che si trovano sotto al cursore nel suo spostamento a ritroso.
32
a="^H^H" # Due Ctl-H (backspace). echo "abcdef" # abcdef echo -n "abcdef$a " # abcd f # Spazio finale ^ ^ Doppio backspace echo -n "abcdef$a" # abcdef # Nessuno spazio alla fine Non viene seguito il backspace (perch?) # I risultati possono essere piuttosto diversi da #+ ci che ci si aspetta. echo; echo
Ctl-K Tabulazione verticale. Durante la digitazione di un testo in una console o in una nestra xterm, Ctl-K cancella i caratteri a partire da quello che si trova sotto il cursore (compreso) no alla ne della riga.
Ctl-L Formfeed (pulisce lo schermo del terminale). Ha lo stesso effetto del comando clear.
Ctl-M A capo.
#!/bin/bash # Grazie a Lee Maschmeyer per lesempio.
read -n 1 -s -p $Control-M sposta il cursore allinizio della riga. Premi Invio. \x0d # Naturalmente, 0d lequivalente esadecimale di Control-M echo >&2 # -s non visualizza quello che viene digitato, #+ quindi necessario andare a capo esplicitamente. read -n 1 -s -p $Control-J sposta il cursore alla riga successiva. \x0a
33
Ctl-S Sospensione (XOFF). Congela lo stdin di un terminale. (Si usi Ctl-Q per ripristinarlo.)
Ctl-U
34
Capitolo 3. Caratteri speciali Cancella una riga di input, a partire dal cursore in senso inverso no allinizio della riga. In alcune impostazioni, Ctl-U cancella lintera riga di input, indipendentemente dalla posizione del cursore.
Ctl-V Durante la digitazione di un testo, Ctl-V consente linserimento di caratteri di controllo. Ad esempio, le due righe seguenti si equivalgono:
echo -e \x0a echo <Ctl-V><Ctl-J>
Ctl-W Durante la digitazione di un testo in una console o in una nestra xterm, Ctl-W cancella a partire dal carattere che si trova sotto al cursore allindietro no al primo spazio incontrato. In alcune impostazioni, Ctl-W cancella allindietro no al primo carattere non alfanumerico.
Spaziatura
serve come divisore, separando comandi o variabili. La spaziatura formata da spazi, tabulazioni, righe vuote, o una loro qualsiasi combinazione. 3 In alcuni contesti, quale lassegnamento di variabile, la spaziatura non consentita e produce un errore di sintassi. Le righe vuote non hanno alcun affetto sullazione dello script, sono quindi molto utili per separare visivamente le diverse sezioni funzionali. $IFS, la speciale variabile dei separatori dei campi di input per determinati comandi. Il carattere preimpostato lo spazio. Per preservare gli spazi presenti in una stringa o in una variabile, si usi il quoting.
35
Note
1. La shell esegue lespansione delle parentesi graffe. Il comando agisce sul risultato dellespansione. 2. Eccezione: una porzione di codice tra parentesi graffe come parte di una pipe potrebbe essere eseguita come subshell.
ls | { read primariga; read secondariga; } # Errore. Il blocco di codice tra le parentesi graffe esegue una subshell, #+ cos loutput di "ls" non pu essere passato alle variabili interne #+ al blocco. echo "La prima riga $primariga; la seconda riga $secondariga" # Non funziona. # Grazie, S.C.
3. Anche il linefeed (a_capo) un carattere di spaziatura. Questo spiega perch una riga vuota, essendo generata semplicemente da un a_capo, viene considerata una spaziatura.
36
bash$ variabile=23
Lunica volta in cui una variabile compare nuda -- senza il presso $ -- quando viene dichiarata o al momento dellassegnamento, quando viene annullata, quando viene esportata, o nel caso particolare di una variabile che rappresenta un segnale (vedi Esempio 29-5). Lassegnamento pu essere fatto con l= (come in var1=27 ), con un enunciato read ed allinizio di un ciclo (for var2 in 1 2 3). Racchiudere il nome della variabile tra doppi apici (" ") non interferisce con la sostituzione di variabile. Questo viene chiamato quoting parziale, o anche quoting debole. Al contrario, lutilizzo degli apici singoli ( ) fa s che il nome della variabile venga interpretato letteralmente, per cui la sostituzione non avverr. In questo caso si ha il quoting pieno, chiamato anche quoting forte. Vedi Capitolo 5 per una trattazione dettagliata.
37
Capitolo 4. Introduzione alle variabili ed ai parametri da notare che $variabile in realt una forma semplicata ed alternativa di ${variabile}. In contesti in cui la sintassi $variabile pu provocare un errore, la forma estesa potrebbe funzionare (vedi la Sezione 9.3, pi oltre). Esempio 4-1. Assegnamento e sostituzione di variabile
#!/bin/bash # Variabili: assegnamento e sostituzione a=375 ciao=$a #---------------------------------------------------------------------------# Quando si inizializzano le variabili, non sono consentiti spazi prima #+ e dopo il segno =. # Cosa succederebbe se ce ne fosse uno? # Nel caso "VARIABILE =valore", # ^ #+ lo script cerca di eseguire il comando "VARIABILE" #+ "=valore".
con largomento
# Nel caso "VARIABILE= valore", # ^ #+ lo script cerca di eseguire il comando "valore" con la variabile #+ dambiente "VARIABILE" impostata a "". #----------------------------------------------------------------------------
# Come sopra.
echo "$ciao" echo "${ciao}" echo ciao="A B C D" echo $ciao # A B C D echo "$ciao" # A B C D # Come si pu vedere, echo $ciao e echo "$ciao" producono # ^ ^ #+ risultati differenti. Il quoting di una variabile conserva gli spazi. echo echo $ciao # $ciao # ^ ^ # Gli apici singoli disabilitano la referenziazione alla variabile, #+ perch il simbolo "$" viene interpretato letteralmente.
38
ciao= # Imposta la variabile al valore nullo. echo "\$ciao (valore nullo) = $ciao" # Attenzione, impostare una variabile al valore nullo non la stessa #+ cosa di annullarla, sebbene il risultato finale sia lo stesso (vedi oltre). # # -------------------------------------------------------------# # consentito impostare pi variabili sulla stessa riga, #+ separandole con uno spazio. # Attenzione, questa forma pu diminuire la leggibilit #+ e potrebbe non essere portabile. var1=21 var2=22 echo echo "var1=$var1 var3=$V3 var2=$var2 var3=$var3"
# Potrebbe causare problemi con le versioni pi vecchie di "sh". # -------------------------------------------------------------echo; echo numeri="uno due tre" # ^ ^ altri_numeri="1 2 3" # ^ ^ # Se ci sono degli spazi allinterno di una variabile, #+ allora necessario il quoting. echo "numeri = $numeri" echo "altri_numeri = $altri_numeri" # altri_numeri = 1 2 3 echo echo "variabile_non_inizializzata = $variabile_non_inizializzata" # Una variabile non inizializzata ha valore nullo (nessun valore). variabile_non_inizializzata= # Viene dichiarata, ma non inizializzata #+ come impostarla al valore nullo, #+ vedi sopra. echo "variabile_non_inizializzata = $variabile_non_inizializzata" # Ha ancora valore nullo. variabile_non_inizializzata=23 # impostata. unset variabile_non_inizializzata # Viene annullata. echo "variabile_non_inizializzata = $variabile_non_inizializzata" # Ha ancora valore nullo. echo exit 0
39
Cautela
Una variabile non inizializzata ha valore nullo: cio proprio nessun valore (non zero!). Utilizzare una variabile prima di averle assegnato un valore, solitamente provoca dei problemi.
Conclusione: Una variabile non inizializzata non ha alcun valore, tuttavia si c nelle operazioni aritmetiche, come se il suo valore fosse 0 (zero) Questo un comportamento non documentato (e probabilmente non por
Cautela
Da non confondere con = e -eq, che servono per le veriche!
da notare che l= pu essere sia loperatore di assegnamento che quello di verica. Dipende dal contesto in cui si trova.
40
# Ora in un modo un po pi raffinato (sostituzione di comando). a=echo Ciao echo $a # #+ #+ #+ #+ # Assegna il risultato del comando echo ad a
Nota: linserimento del punto esclamativo (!) allinterno del costrutto di sostituzione di comando non funziona da riga di comando, perch il ! attiva il "meccanismo di cronologia" della shell Bash. Allinterno di uno script, per, le funzioni di cronologia sono disabilitate. # Assegna il risultato del comando ls -l ad a # Senza lutilizzo del quoting vengono eliminate #+ le tabulazioni ed i ritorni a capo. # Lutilizzo del quoting preserva gli spazi.
41
Assegnamento di variabile utilizzando $(...) (metodo pi recente rispetto agli apici inversi). In realt si tratta di una forma particolare di sostituzionedi comando.
A differenza di molti altri linguaggi di programmazione, Bash non differenzia le sue variabili per tipo. Essenzialmente le variabili Bash sono stringhe di caratteri, ma, in base al contesto, la shell consente le operazioni con interi e i confronti di variabili. Il fattore determinante se il valore di una variabile sia formato, o meno, solo da cifre. Esempio 4-4. Intero o stringa?
#!/bin/bash # int-or-string.sh: Intero o stringa? a=2334 let "a += 1" echo "a = $a " echo # Intero. # a = 2335 # Intero, ancora.
b=${a/23/BB} echo "b = $b" declare -i b echo "b = $b" let "b += 1" echo "b = $b" echo c=BB34 echo "c = $c" d=${c/BB/23} echo "d = $d"
# # # # #
Sostituisce "23" con "BB". Questo trasforma $b in una stringa. b = BB35 Dichiararla come intero non aiuta. b = BB35
# BB35 + 1 = # b = 1
# # # #
42
# Che dire a proposito delle variabili nulle? e="" echo "e = $e" # e = let "e += 1" # Sono consentite le operazioni aritmetiche sulle #+ variabili nulle? echo "e = $e" # e = 1 echo # Variabile nulla trasformata in un intero. # E sulle echo "f = let "f += echo "f = echo variabili non dichiarate? $f" # f = 1" # Sono consentite le operazioni aritmetiche? $f" # f = 1 # Variabile non dichiarata trasformata in un intero.
Le variabili non tipizzate sono sia una benedizione che una calamit. Permettono maggiore essibilit nello scripting (abbastanza corda per impiccarvici!) e rendono pi semplice sfornare righe di codice. Per contro, consentono errori subdoli e incoraggiano stili di programmazione disordinati. compito del programmatore tenere traccia dei tipi di variabili contenute nello script. Bash non lo far per lui.
43
Cautela
Lo spazio assegnato allambiente limitato. Creare troppe variabili dambiente, o se alcune occupano eccessivo spazio, potrebbe causare problemi.
bash$ eval "seq 10000 | sed -e s/.*/export var&=ZZZZZZZZZZZZZZ/" bash$ du bash: /usr/bin/du: Argument list too long
Se uno script imposta delle variabili dambiente, necessario che vengano esportate, cio trasferite allambiente dei programmi che verranno eseguiti. Questo il compito del comando export.
Nota: Uno script pu esportare le variabili solo verso i processi gli, vale a dire solo nei confronti dei comandi o dei processi che vengono iniziati da quel particolare script. Uno script eseguito da riga di comando non pu esportare le variabili allindietro, verso lambiente precedente. Allo stesso modo, i processi gli non possono esportare le variabili allindietro verso i processi genitori che li hanno generati.
---
parametri posizionali rappresentano gli argomenti passati allo script da riga di comando: $0, $1, $2, $3 . . .
$0 contiene il nome dello script, $1 il primo argomento, $2 il secondo, $3 il terzo, ecc.. 1 Dopo $9 il numero degli argomenti deve essere racchiuso tra parentesi graffe, per esempio, ${10}, ${11}, ${12}.
Le variabili speciali $* e $@ forniscono il numero di tutti i parametri posizionali passati. Esempio 4-5. Parametri posizionali
#!/bin/bash
44
# Eseguite lo script con almeno 10 parametri, per esempio # ./nomescript 1 2 3 4 5 6 7 8 9 10 MINPARAM=10 echo echo "Il nome dello script # Aggiungete ./ per indicare echo "Il nome dello script # Visualizza il percorso del echo if [ -n "$1" ] then echo "Il parametro #1 $1" fi if [ -n "$2" ] then echo "Il parametro #2 $2" fi if [ -n "$3" ] then echo "Il parametro #3 $3" fi # ... # Utilizzate il quoting per la variabile #+ da verificare. # necessario il quoting #+ per visualizzare il # \"$0\"." la directory corrente \"basename $0\"." nome (vedi basename)
if [ -n "${10}" ]
then echo "Il parametro #10 ${10}" fi echo "-----------------------------------" echo "In totale i parametri passati : "$*"" if [ $# -lt "$MINPARAM" ] then echo echo "Lo script ha bisogno di almeno $MINPARAM argomenti da riga di comando!" fi echo exit 0
45
Capitolo 4. Introduzione alle variabili ed ai parametri La notazione parentesi graffe, applicata ai parametri posizionali, pu essere facilmente impiegata per la referenziazion allultimo argomento passato allo script da riga di comando. Questo richiede anche la referenziazione indiretta.
arg=$# # Numero di argomenti passati. ultimo_argomento=${!arg} # Oppure: ultimo_argomento=${!#} # (Grazie, Chris Monson.) # Notate che ultimo_argomento=${!$#} non funziona.
Alcuni script possono eseguire compiti diversi in base al nome con cui vengono invocati. Afnch questo possa avvenire, lo script ha bisogno di vericare $0, cio il nome con cui stato invocato. Naturalmente devono esserci dei link simbolici ai nomi alternativi dello script. Vedi Esempio 12-2.
Suggerimento: Se uno script si aspetta un parametro passato da riga di comando, ma stato invocato senza, ci pu causare un assegnamento del valore nullo alla variabile che deve essere inizializzata da quel parametro. Di solito, questo non un risultato desiderabile. Un modo per evitare questa possibilit aggiungere un carattere supplementare ad entrambi i lati dellenunciato di assegnamento che utilizza il parametro posizionale.
variabile1_=$1_ # Invece di variabile1=$1 # Questo evita qualsiasi errore, anche se non presente #+ il parametro posizionale. argomento_critico01=$variabile1_ # Il carattere aggiunto pu essere tolto pi tardi in questo modo: variabile1=${variabile1_/_/} # Si hanno effetti collaterali solo se $variabile1_ inizia con #+ trattino di sottolineatura (underscore). # stato utilizzato uno dei modelli di sostituzione di parametro che verr #+ trattata successivamente. # (Se si omette il sostituto si ottiene una cancellazione.) # Un modo pi diretto per gestire la situazione una #+ semplice verifica della presenza dei parametri posizionali attesi. if [ -z $1 ] then exit $MANCA_PARAM_POSIZIONALE fi # #+ # # # #+ Tuttavia, come ha evidenziato Fabian Kreutz, il metodo precedente pu generare degli effetti collaterali inattesi. Un sistema migliore rappresentato dalla sostituzione di parametro: ${1:-$ValDefault} Vedi la sezione "Sostituzione di parametro" del capitolo "Variabili riviste".
46
Capitolo 4. Introduzione alle variabili ed ai parametri --Esempio 4-6. verica del nome di dominio: wh, whois
#!/bin/bash # ex18.sh # Esegue una verifica whois nome-dominio su uno dei 3 server: # ripe.net, cw.net, radb.net # Inserite questo script - con nome wh - nel file /usr/local/bin # # # # Sono richiesti i seguenti link simbolici: ln -s /usr/local/bin/wh /usr/local/bin/wh-ripe ln -s /usr/local/bin/wh /usr/local/bin/wh-cw ln -s /usr/local/bin/wh /usr/local/bin/wh-radb
E_NOARG=65 if [ -z "$1" ] then echo "Utilizzo: basename $0 [nome-dominio]" exit $E_NOARG fi # Verifica il nome dello script e interroga il server adeguato. case basename $0 in # Oppure: case ${0##*/} in "wh" ) whois [email protected];; "wh-ripe") whois [email protected];; "wh-radb") whois [email protected];; "wh-cw" ) whois [email protected];; * ) echo "Utilizzo: basename $0 [nome-dominio]";; esac exit $?
---
Il comando shift riassegna i parametri posizionali, spostandoli di una posizione verso sinistra.
$1 <--- $2, $2 <--- $3, $3 <--- $4, ecc.
Il vecchio $1 viene sostituito, ma $0 (il nome dello script) non cambia. Se si utilizza un numero elevato di parametri posizionali, shift permette di accedere a quelli dopo il 10, sebbene questo sia possibile anche con la notazione {parentesi graffe}.
47
# Linea extra.
Nota: Il comando shift agisce allo stesso modo sui parametri passati ad una funzione. Vedi Esempio 33-15.
Note
1. il processo che chiama lo script che imposta il parametro $0. Per convenzione, questo parametro il nome dello script. Vedi la pagina di manuale di execv.
48
Capitolo 5. Quoting
Con il termine quoting si intende semplicemente linseririmento di una stringa tra apici. Viene impiegato per proteggere i caratteri speciali contenuti nella stringa dalla reinterpretazione, o espansione, da parte della shell o di uno script. (Un carattere si denisce speciale se viene interpretato diversamente dal suo signicato letterale, come il carattere jolly *.)
324 Apr 2 15:05 VIEWDATA.BAT 507 May 4 14:25 vartrace.sh 539 Apr 14 17:11 viewdata.sh
Nella scrittura, si mette una frase fra virgolette quando la si vuole evidenziare e attribuirle un signicato speciale. In uno script Bash, quando si applica il quoting (virgolette) a una stringa per evidenziarla e conservare il suo signicato letterale.
Tuttavia, alcuni programmi ed utility possono ancora reinterpretare o espandere i caratteri speciali contenuti in una stringa a cui stato applicato il quoting. Un utilizzo importante del quoting quello di proteggere un parametro passato da riga di comando dalla reinterpretazione da parte della shell, ma permettere ancora al programma chiamante di espanderlo.
bash$ grep [Pp]rima *.txt file1.txt:Questa la prima riga di file1.txt. file2.txt:Questa la prima riga di file2.txt.
da notare che listruzione grep [Pp]rima *.txt, senza quoting, funziona con la shell Bash. 1 Il quoting anche in grado di eliminare la fame di a_capo tipica diecho.
bash$ echo $(ls -l) total 8 -rw-rw-r-- 1 bozo bozo 130 Aug 21 12:57 t222.sh -rw-rw-r-- 1 bozo bozo 78 Aug 21 12:57 t71.sh
49
Capitolo 5. Quoting
total 8 -rw-rw-r--rw-rw-r--
1 bozo bozo 130 Aug 21 12:57 t222.sh 1 bozo bozo 78 Aug 21 12:57 t71.sh
variabile2=""
# Vuota.
COMANDO $variabile2 $variabile2 $variabile2 # Esegue COMANDO con nessun argomento. COMANDO "$variabile2" "$variabile2" "$variabile2" # Esegue COMANDO con 3 argomenti vuoti. COMANDO "$variabile2 $variabile2 $variabile2" # Esegue COMANDO con 1 argomento (2 spazi). # Grazie, Stphane Chazelas.
Suggerimento: necessario porre gli argomenti tra doppi apici in un enunciato echo solo quando si ha come scopo la suddivisione delle parole o preservare gli spazi.
50
Capitolo 5. Quoting
var="(]\\{}\$\"" echo $var # (]\{}$" echo "$var" # (]\{}$" echo IFS=\ echo $var echo "$var"
Nessuna differenza.
# (] {}$" # (]\{}$"
Gli apici singoli ( ) agiscono in modo simile a quelli doppi, ma non consentono la referenziazione alle variabili, perch non pi consentita la reinterpretazione di $. Allinterno degli apici singoli, tutti i caratteri speciali, tranne , vengono interpretati letteralmente. Gli apici singoli (quoting pieno) rappresentano un metodo di quoting pi restrittivo di quello con apici doppi (quoting parziale).
Nota: Dal momento che anche il carattere di escape (\) viene interpretato letteralmente, effettuare il quoting di apici singoli mediante apici singoli non produce il risultato atteso.
echo "Why cant I write s between single quotes" # Perch non riesco a scrivere s tra apici singoli echo # Metodo indiretto. echo Why can\t I write ""s between single quotes # |-------| |----------| |-----------------------| # Tre stringhe tra apici singoli a cui sono frapposti il carattere di #+ escape e lapice singolo. # Esempio cortesemente fornito da Stphane Chazelas.
5.2. Escaping
Lescaping un metodo per effettuare il quoting di un singolo carattere. Il carattere di escape (\), posto davanti ad un altro carattere, informa la shell che questultimo deve essere interpretato letteralmente.
51
Capitolo 5. Quoting
Cautela
Con alcuni comandi e utility, come echo e sed, lescaping di un carattere potrebbe avere un effetto particolare - quello di attribuire un signicato specico a quel carattere (le c.d. sequenze di escape [N.d.T.]).
\n signica a capo \r signica invio \t signica tabulazione \v signica tabulazione verticale \b signica ritorno (backspace) \a signica allerta (segnale acustico o accensione di un led) \0xx trasforma in carattere ASCII il valore ottale 0xx Esempio 5-2. Sequenze di escape
#!/bin/bash # escaped.sh: sequenze di escape echo; echo echo "\v\v\v\v" # Visualizza letteralmente: \v\v\v\v . # Utilizzate lopzione -e con echo per un corretto impiego delle #+ sequenze di escape. echo "=============" echo "TABULAZIONE VERTICALE" echo -e "\v\v\v\v" # Esegue 4 tabulazioni verticali. echo "=============="
52
Capitolo 5. Quoting
# Il costrutto $\X rende lopzione -e superflua. echo; echo "A_CAPO E BEEP" echo $\n # A capo. echo $\a # Allerta (beep). echo "===============" echo "VIRGOLETTE" # La versione 2 e successive di Bash consente lutilizzo del costrutto $\nnn. # Notate che in questo caso, \nnn un valore ottale. echo $\t \042 \t # Virgolette (") tra due tabulazioni. # Pu essere utilizzato anche con valori esadecimali nella forma $\xhhh. echo $\t \x22 \t # Virgolette (") tra due tabulazioni. # Grazie a Greg Keraunen per la precisazione. # Versioni precedenti di Bash consentivano \x022. echo "===============" echo
# Assegnare caratteri ASCII ad una variabile. # -----------------------------------------virgolette=$\042 # " assegnate alla variabile. echo "$virgolette Questa una stringa tra virgolette $virgolette, \ mentre questa parte al di fuori delle virgolette." echo # Concatenare caratteri ASCII in una variabile. tripla_sottolineatura=$\137\137\137 # 137 il valore ottale del carattere ASCII _. echo "$tripla_sottolineatura SOTTOLINEA $tripla_sottolineatura" echo ABC=$\101\102\103\010 # 101, 102, 103 sono i valori ottali di A, B, C. echo $ABC echo; echo escape=$\033 # 033 il valore ottale del carattere di escape. echo "\"escape\" visualizzato come $escape" # nessun output visibile.
53
Capitolo 5. Quoting
Vedi Esempio 34-1 per unaltra dimostrazione di $ come costrutto di espansione di stringa.
\$ mantiene il signicato letterale del segno del dollaro (la variabile che segue \$ non verr referenziata)
echo "\$variabile01"
# visualizza $variabile01
echo "\\"
# visualizza \
# Mentre . . . echo "\" # Invoca il prompt secondario da riga di comando. # In uno script provoca un messaggio derrore.
Nota: Il comportamento della \ dipende dal contesto: se le stato applicato lescaping o il quoting, se appare allinterno di una sostituzione di comando o in un here document.
# # # # # # Escaping e quoting semplice z \z \z \\z \z
54
Capitolo 5. Quoting
echo "\\z" # \z # # # # # # # # # Sostituzione di comando z z \z \z \z \\z \z \z
# \z
# \z
Lescaping pu essere applicato anche ai caratteri di una stringa assegnata ad una variabile, ma non si pu assegnare ad una variabile il solo carattere di escape.
variabile=\ echo "$variabile" # Non funziona - d un messaggio derrore: # test.sh: : command not found # Un escape "nudo" non pu essere assegnato in modo sicuro ad una variabile. # # Quello che avviene effettivamente qui che la "\" esegue lescape #+ del a capo e leffetto variabile=echo "$variabile" #+ assegnamento di variabile non valido variabile=\ 23skidoo echo "$variabile"
# 23skidoo # Funziona, perch nella seconda riga #+ presente un assegnamento di variabile valido.
variabile=\ # \^ escape seguito da uno spazio echo "$variabile" # spazio variabile=\\ echo "$variabile"
# \
variabile=\\\ echo "$variabile" # Non funziona - d un messaggio derrore: # test.sh: \: command not found # # Il primo carattere di escape esegue lescaping del secondo, mentre il terzo #+ viene lasciato "nudo", con lidentico risultato del primo esempio visto #+ sopra.
55
Capitolo 5. Quoting
# # #+ #
\\ Il secondo ed il quarto sono stati preservati dal primo e dal terzo. Questo va bene.
Lescaping dello spazio evita la suddivisione delle parole di un argomento contenente un elenco di comandi.
elenco_file="/bin/cat /bin/gzip /bin/more /usr/bin/less /usr/bin/emacs-20.7" # Elenco di file come argomento(i) di un comando. # Aggiunge due file allelenco, quindi visualizza tutto. ls -l /usr/X11R6/bin/xsetroot /sbin/dump $elenco_file echo "-------------------------------------------------------------------------" # Cosa succede se si effettua lescaping dei due spazi? ls -l /usr/X11R6/bin/xsetroot\ /sbin/dump\ $elenco_file # Errore: i primi tre file vengono concatenati e considerati come un unico #+ argomento per ls -l perch lescaping dei due spazi impedisce la #+ divisione degli argomenti (parole).
Il carattere di escape rappresenta anche un mezzo per scrivere comandi su pi righe. Di solito, ogni riga rappresenta un comando differente, ma il carattere di escape posto in ne di riga effettua lescaping del carattere a capo, in questo modo la sequenza dei comandi continua alla riga successiva.
((cd /source/directory && tar cf - . ) | \ (cd /dest/directory && tar xpvf -) # Ripetizione del comando copia di un albero di directory di Alan Cox, # ma suddiviso su due righe per aumentarne la leggibilit. # Come alternativa: tar cf - -C /source/directory . | tar xpvf - -C /dest/directory # Vedi la nota pi sotto. #(Grazie, Stphane Chazelas.)
Nota: Se una riga dello script termina con | (pipe) allora la \ (l escape), non obbligatorio. , tuttavia, buona pratica di programmazione utilizzare sempre lescape alla ne di una riga di codice che continua nella riga successiva.
56
Capitolo 5. Quoting
echo "foo bar" #foo #bar echo echo foo bar # Ancora nessuna differenza. #foo #bar echo echo foo\ bar # Eseguito lescaping del carattere a capo. #foobar echo echo "foo\ bar" # Stesso risultato, perch \ viene ancora interpretato come escape #+ quando posto tra apici doppi. #foobar echo echo foo\ bar # Il carattere di escape \ viene interpretato letteralmente a causa #+ del quoting forte. #foo\ #bar # Esempi suggeriti da Stphane Chazelas.
Note
1. A meno che non ci sia, nella directory di lavoro corrente, un le con nome prima. Una ragione in pi per usare il quoting. (Grazie a Harald Koenig per averlo sottolineato. 2. Si hanno effetti collaterali sul valore della variabile (vedi oltre) 3. Racchiudere il ! tra doppi apici provoca un errore se usato da riga di comando. Viene interpretato come un comando di cronologia. In uno script, tuttavia, questo problema non si presenta, dal momento che la cronologia dei comandi di Bash didabilitata.
Pi interessante il comportamento incoerente della \ quando si trova tra i doppi apici.
57
Capitolo 5. Quoting
bash$ echo ciao\! ciao!
4. La divisione delle parole, in questo contesto, signica suddividere una stringa di caratteri in un certo numero di argomenti separati e distinti.
58
59
$? legge lexit status dellultimo comando eseguito. Dopo lesecuzione di una funzione, $? fornisce lexit status dellultimo comando eseguito nella funzione. Questo il modo che Bash ha per consentire alle funzioni di restituire un valore di ritorno. Al termine di uno script, digitando $? da riga di comando, si ottiene lexit status dello script, cio, dellultimo comando eseguito che, per convenzione, 0 in caso di successo o un intero compreso tra 1 e 255 in caso di errore.
# Exit status 0 perch il comando stato eseguito con successo. # Comando sconosciuto. # Exit status diverso da zero perch il comando non ha #+ avuto successo.
echo exit 113 # Restituir 113 alla shell. # Per verificarlo, digitate "echo $?" dopo lesecuzione dello script.
# Convenzionalmente, un exit 0 indica successo, #+ mentre un valore diverso significa errore o condizione anomala.
$? particolarmente utile per la verica del risultato di un comando in uno script (vedi Esempio 12-32 e Esempio 12-17).
Nota: Il !, loperatore logico not, inverte il risultato di una verica o di un comando e questo si ripercuote sul relativo exit status. Esempio 6-2. Negare una condizione utilizzando !
true # il builtin "true". echo "exit status di \"true\" = $?" ! true echo "exit status di \"! true\" = $?"
# 0
# 1
60
Cautela
Alcuni codici di exit status hanno signicati riservati e non dovrebbero quindi essere usati dallutente in uno script.
61
Capitolo 7. Veriche
Qualsiasi linguaggio di programmazione, che a ragione possa denirsi completo, deve consentire la verica di una condizione e quindi comportarsi in base al suo risultato. Bash possiede il comando test, vari operatori parentesi quadre, parentesi rotonde e il costrutto if/then.
Il costrutto if/then verica se lexit status di un elenco di comandi 0 (perch 0 signica successo per convenzione UNIX) e se questo il caso, esegue uno o pi comandi. Esiste il comando specico [ (parentesi quadra aperta). sinonimo di test ed stato progettato come builtin per ragioni di efcienza. Questo comando considera i suoi argomenti come espressioni di confronto, o di verica di le, e restituisce un exit status corrispondente al risultato del confronto (0 per vero, 1 per falso). Con la versione 2.02, Bash ha introdotto [[ ... ]], comando di verica estesa, che esegue confronti in un modo pi familiare ai programmatori in altri linguaggi. Va notato che [[ una parola chiave, non un comando. Bash vede [[ $a -lt $b ]] come un unico elemento che restituisce un exit status. Anche i costrutti (( ... )) e let ... restituiscono exit status 0 se le espressioni aritmetiche valutate sono espanse ad un valore diverso da zero. Questi costrutti di espansione aritmetica possono, quindi, essere usati per effettuare confronti aritmetici.
let "1<2" restituisce 0 (poich "1<2" espande a "1") (( 0 && 1 )) restituisce 1 (poich "0 && 1" espande a "0")
Un costrutto if pu vericare qualsiasi comando, non solamente le condizioni comprese tra le parentesi quadre.
if cmp a b &> /dev/null # Sopprime loutput. then echo "I file a e b sono identici." else echo "I file a e b sono diversi." fi # Lutilissimo costrutto "if-grep": # -------------------------------if grep -q Bash file then echo "Il file contiene almeno unoccorrenza di Bash."
62
Capitolo 7. Veriche
fi parola=Linux sequenza_lettere=inu if echo "$parola" | grep -q "$sequenza_lettere" # Lopzione "-q" di grep elimina loutput. then echo "$sequenza_lettere trovata in $parola" else echo "$sequenza_lettere non trovata in $parola" fi
63
Capitolo 7. Veriche
echo "0 falso." fi # 0 vero. echo echo "Verifica \"1\"" if [ 1 ] # uno then echo "1 vero." else echo "1 falso." fi # 1 vero. echo echo "Verifica \"-1\"" if [ -1 ] # meno uno then echo "-1 vero." else echo "-1 falso." fi # -1 vero. echo echo "Verifica \"NULL\"" if [ ] # NULL (condizione vuota) then echo "NULL vero." else echo "NULL falso." fi # NULL falso. echo echo "Verifica \"xyz\"" if [ xyz ] # stringa then echo "La stringa casuale vero." else echo "La stringa casuale falso." fi # La stringa casuale vero. echo echo "Verifica \"\$xyz\"" if [ $xyz ] # Verifica se $xyz nulla, ma... # solo una variabile non inizializzata. then echo "La variabile non inizializzata vero." else echo "La variabile non inizializzata falso." fi # La variabile non inizializzata falso.
64
Capitolo 7. Veriche
echo echo "Verifica \"-n \$xyz\"" if [ -n "$xyz" ] # Pi corretto, ma pedante. then echo "La variabile non inizializzata vero." else echo "La variabile non inizializzata falso." fi # La variabile non inizializzata falso. echo
xyz=
echo "Verifica \"-n \$xyz\"" if [ -n "$xyz" ] then echo "La variabile nulla vero." else echo "La variabile nulla falso." fi # La variabile nulla falso.
echo
# Quando "falso" vero? echo "Verifica \"falso\"" if [ "falso" ] # Sembra che "falso" sia solo una stringa. then echo "\"falso\" vero." # e verifica se vero. else echo "\"falso\" falso." fi # "falso" vero. echo echo "Verifica \"\$falso\"" # Ancora variabile non inizializzata. if [ "$falso" ] then echo "\"\$falso\" vero." else echo "\"\$falso\" falso." fi # "$falso" falso. # Ora abbiamo ottenuto il risultato atteso. # Cosa sarebbe accaduto se avessimo verificato #+ la variabile non inizializzata "$vero"? echo
65
Capitolo 7. Veriche
exit 0
if [ condizione-vera ] then comando 1 comando 2 ... else # Opzionale (pu anche essere omesso). # Aggiunge un determinato blocco di codice che verr eseguito se la #+ condizione di verifica falsa. comando 3 comando 4 ... fi
Nota: Quando if e then sono sulla stessa riga occorre mettere un punto e virgola dopo lenunciato if per indicarne il termine. Sia if che then sono parole chiave. Le parole chiave (o i comandi) iniziano gli enunciati e prima che un nuovo enunciato possa incominciare, sulla stessa riga, necessario che il precedente venga terminato.
if [ -x "$nome_file" ]; then
Else if ed elif
elif elif la contrazione di else if. Lo scopo quello di annidare un costrutto if/then in un altro.
if [ condizione1 ] then comando1 comando2 comando3 elif [ condizione2 ] # Uguale a else if then comando4 comando5 else comando-predefinito
66
Capitolo 7. Veriche
fi
Il costrutto if test condizione-vera lesatto equivalente di if [ condizione-vera ]. In questultimo costrutto, la parentesi quadra sinistra [, un simbolo che invoca il comando test. La parentesi quadra destra di chiusura, ], non dovrebbe essere necessaria. Ci nonostante, le pi recenti versioni di Bash la richiedono.
Nota: Il comando test un builtin Bash che verica i tipi di le e confronta le stringhe. Di conseguenza, in uno script Bash, test non richiama leseguibile esterno /usr/bin/test, che fa parte del pacchetto sh-utils. In modo analogo, [ non chiama /usr/bin/[, che un link a /usr/bin/test.
bash$ type test test is a shell builtin bash$ type [ [ is a shell builtin bash$ type [[ [[ is a shell keyword bash$ type ]] ]] is a shell keyword bash$ type ] bash: type: ]: not found
67
Capitolo 7. Veriche
echo if [ -z "$1" ] # Funzionalit identica al precedente blocco #+ di codice. if [ -z "$1" dovrebbe funzionare, ma... Bash risponde con il messaggio derrore di missing close-bracket.
# #+ then echo "Nessun argomento da riga di comando." else echo "Il primo argomento da riga di comando $1." fi echo
if /usr/bin/[ -z "$1" ] # Ancora, funzionalit identica alla precedente. # if /usr/bin/[ -z "$1" # Funziona, ma d un messaggio derrore. # # Nota: # Il problema stato risolto # + nella versione Bash 3.x then echo "Nessun argomento da riga di comando." else echo "Il primo argomento da riga di comando $1." fi echo exit 0
Suggerimento: Lutilizzo del costrutto di verica [[ ... ]] al posto di [ ... ] pu evitare molti errori logici negli script. Per esempio, gli operatori &&, ||, < e > funzionano correttamente in una verica [[ ]], mentre potrebbero dare degli errori con il costrutto [ ] .
68
Capitolo 7. Veriche
Nota: Dopo un if non sono strettamente necessari n il comando test n i costrutti parentesi quadre ( [ ] o [[ ]] ).
dir=/home/bozo if cd "$dir" 2>/dev/null; then # "2>/dev/null" sopprime il messaggio derrore. echo "Ora sei in $dir." else echo "Non riesco a cambiare in $dir." fi
Il costrutto "if COMANDO" restituisce lexit status di COMANDO. Per questo motivo, una condizione tra parentesi quadre pu essere utilizzata da sola, senza if, se abbinata ad un costrutto lista.
var1=20 var2=22 [ "$var1" -ne "$var2" ] && echo "$var1 diversa da $var2" home=/home/bozo [ -d "$home" ] || echo "La directory $home non esiste."
Il costrutto (( )) espande e valuta unespressione aritmetica. Se il risultato della valutazione dellespressione zero, viene restituito come exit status 1, ovvero falso. Una valutazione diversa da zero restituisce come exit status 0, ovvero vero. Questo in contrasto marcato con lutilizzo di test e dei costrutti [ ] precedentemente discussi. Esempio 7-3. Veriche aritmetiche utilizzando (( ))
#!/bin/bash # Verifiche aritmetiche. # Il costrutto (( ... )) valuta e verifica le espressioni aritmetiche. # Exit status opposto a quello fornito dal costrutto [ ... ]! (( 0 )) echo "lexit status di \"(( 0 ))\" $?." (( 1 )) echo "Lexit status di \"(( 1 ))\" $?."
# 1
# 0
(( 5 > 4 )) # vero echo "Lexit status di \"(( 5 > 4 ))\" $?." # 0 (( 5 > 9 )) # falso echo "Lexit status di \"(( 5 > 9 ))\" $?." # 1 (( 5 - 5 )) # 0 echo "Lexit status di \"(( 5 - 5 ))\" $?." # 1
69
Capitolo 7. Veriche
(( 5 / 4 )) # Divisione o.k. echo "Lexit status di \"(( 5 / 4 ))\" $?." # 0 (( 1 / 2 )) # Risultato della divisione <1. echo "Lexit status di \"(( 1 / 2 ))\" $?." # Arrotondato a 0. # 1 (( 1 / 0 )) 2>/dev/null # Divisione per 0 non consentita. # ^^^^^^^^^^^ echo "Lexit status di \"(( 1 / 0 ))\" $?." # 1 # Che funzione ha "2>/dev/null"? # Cosa succederebbe se fosse tolto? # Toglietelo, quindi rieseguite lo script. exit 0
-f il le un le regolare (non una directory o un le di dispositivo) -s il le ha dimensione superiore a zero -d il le una directory -b il le un dispositivo a blocchi (oppy, cdrom, ecc.) -c il le un dispositivo a caratteri (tastiera, modem, scheda audio, ecc.)
70
Capitolo 7. Veriche -p il le una pipe -h il le un link simbolico -L il le un link simbolico -S il le un socket -t il le (descrittore) associato ad un terminale Questa opzione pu essere utilizzata per vericare se lo stdin ([ -t 0 ]) o lo stdout ([ -t 1 ]) in un dato script un terminale.
-r il le ha il permesso di lettura (per lutente che esegue la verica) -w il le ha il permesso di scrittura (per lutente che esegue la verica) -x il le ha il permesso di esecuzione (per lutente che esegue la verica) -g impostato il bit set-group-id (sgid) su un le o directory Se una directory ha il bit sgid impostato, allora un le creato in quella directory appartiene al gruppo proprietario della directory, non necessariamente al gruppo dellutente che ha creato il le. Pu essere utile per una directory condivisa da un gruppo di lavoro.
-u impostato il bit set-user-id (suid) su un le Un le binario di propriet di root con il bit set-user-id impostato funziona con i privilegi di root anche quando invocato da un utente comune. 1 utile con eseguibili (come pppd e cdrecord) che devono accedere allhardware del sistema. Non impostando il bit suid, questi eseguibili non potrebbero essere invocati da un utente diverso da root.
-rwsr-xr-t 1 root 178236 Oct 2 2000 /usr/sbin/pppd
71
Capitolo 7. Veriche
Un le con il bit suid impostato visualizzato con una s nellelenco dei permessi.
-k impostato lo sticky bit Comunemente conosciuto come sticky bit, il bit save-text-mode un tipo particolare di permesso. Se un le ha il suddetto bit impostato, quel le verr mantenuto nella memoria cache, per consentirne un accesso pi rapido. 2 Se impostato su una directory ne limita il permesso di scrittura. Impostando lo sticky bit viene aggiunta una t allelenco dei permessi di un le o di una directory.
drwxrwxrwt 7 root 1024 May 19 21:26 tmp/
Se lutente non il proprietario della directory con lo sticky bit impostato, ma ha il permesso di scrittura, in quella directory pu soltanto cancellare i le di sua propriet. Questo impedisce agli utenti di sovrascrivere o cancellare inavvertitamente i le di qualcunaltro nelle directory ad accesso pubblico, come, ad esempio, /tmp. (Naturalmente, il proprietario della directory, o root, pu cancellare o rinominare i le.)
-O siete il proprietario del le -G lid di gruppo del le uguale al vostro -N il le stato modicato dallultima volta che stato letto f1 -nt f2 il le f1 pi recente del le f2 f1 -ot f2 il le f1 meno recente del le f2 f1 -ef f2 i le f1 e f2 sono hard link allo stesso le ! not -- inverte il risultato delle precedenti opzioni di verica (restituisce vero se la condizione assente).
72
# Se nessun argomento viene passato allo script, la directory di ricerca #+ directorys viene impostata alla directory corrente. Altrimenti directorys #+ viene impostata allargomento passato. ######################################## [ $# -eq 0 ] && directorys=pwd || directorys=$@ # Implementazione della funzione verlink per cercare, nella directory # passatale, i file che sono link a file inesistenti, quindi visualizzarli #+ tra virgolette. Se uno degli elementi della directory una sottodirectory, #+ allora anche questa viene passata alla funzione verlink. ########## verlink () { for elemento in $1/*; do [ -h "$elemento" -a ! -e "$elemento" ] && echo \"$elemento\" [ -d "$elemento" ] && verlink $elemento # Naturalmente, -h verifica i link simbolici, -d le directory. done } # Invia ogni argomento passato allo script alla funzione verlink, se una #+ directory valida. Altrimenti viene visualizzato un messaggio derrore e le #+ informazioni sullutilizzo. ############################# for directory in $directorys; do if [ -d $directory ] then verlink $directory else echo "$directory non una directory" echo "Utilizzo: $0 dir1 dir2 ..." fi
73
Capitolo 7. Veriche
done exit 0
Vedi anche Esempio 28-1, Esempio 10-7, Esempio 10-3, Esempio 28-3 e Esempio A-1 che illustrano gli utilizzi degli operatori di verica di le.
confronto di interi
-eq
74
confronto di stringhe
=
uguale a
75
!= diverso (non uguale) da if [ "$a" != "$b" ] Allinterno del costrutto [[ ... ]] questo operatore esegue la ricerca di corrispondenza.
< inferiore a, in ordine alfabetico ASCII if [[ "$a" < "$b" ]] if [ "$a" \< "$b" ] Si noti che < necessita dellescaping nel costrutto [ ].
76
Capitolo 7. Veriche > maggiore di, in ordine alfabetico ASCII if [[ "$a" > "$b" ]] if [ "$a" \> "$b" ] Si noti che > necessita dellescaping nel costrutto [ ]. Vedi Esempio 26-11 per unapplicazione di questo operatore di confronto.
Cautela
Loperatore -n richiede assolutamente il quoting della stringa allinterno delle parentesi quadre. Lutilizzo, tra le parentesi quadre, di una stringa senza quoting, sia con ! -z che da sola (vedi Esempio 7-6), normalmente funziona, tuttavia non una pratica sicura. Bisogna sempre utilizzare il quoting su una stringa da vericare. 3
77
Capitolo 7. Veriche
then echo "$a non uguale a $b" echo "(confronto numerico)" fi echo if [ "$a" != "$b" ] then echo "$a non uguale a $b." echo "(confronto di stringhe)" # "4" != "5" # ASCII 52 != ASCII 53 fi # In questo particolare esempio funziona sia "-ne" che "!=". echo exit 0
# Se una stringa non stata inizializzata, non ha un valore definito. # Questo stato si dice "nullo" (non zero!). if [ -n $stringa1 ] # $stringa1 non stata dichiarata o inizializzata. then echo "La stringa \"stringa1\" non nulla." else echo "La stringa \"stringa1\" nulla." fi # Risultato sbagliato. # Viene visualizzato $stringa1 come non nulla, anche se non era inizializzata. echo
# Proviamo ancora. if [ -n "$stringa1" ] # Questa volta stato applicato il quoting a $stringa1. then echo "la stringa \"stringa1\" non nulla." else echo "La stringa \"stringa1\" nulla."
78
Capitolo 7. Veriche
fi # Usate il quoting per le stringhe nel costrutto #+ di verifica parentesi quadre!
echo
if [ $stringa1 ] # Qui, $stringa1 sola. then echo "La stringa \"stringa1\" non nulla." else echo "La stringa \"stringa1\" nulla." fi # Questo funziona bene. # Loperatore di verifica [ ] da solo in grado di rilevare se la stringa #+ nulla. # Tuttavia buona pratica usare il quoting ("$stringa1"). # # Come ha evidenziato Stephane Chazelas, # if [ $stringa1 ] ha un argomento, "]" # if [ "$stringa1" ] ha due argomenti, la stringa vuota "$stringa1" e "]"
echo
stringa1=inizializzata if [ $stringa1 ] # Ancora, $stringa1 da sola. then echo "La stringa \"stringa1\" non nulla." else echo "La stringa \"stringa1\" nulla." fi # Ancora, risultato corretto. # Nondimeno, meglio utilizzare il quoting ("$stringa1"), perch. . . stringa1="a = b" if [ $stringa1 ] # Ancora $stringa1 da sola. then echo "La stringa \"stringa1\" non nulla." else echo "La stringa \"stringa1\" nulla." fi # Senza il quoting di "$stringa1" ora si ottiene un risultato sbagliato! exit 0
79
Capitolo 7. Veriche
# Grazie anche a Florian Wisser per la "citazione iniziale". # (*) Lintestazione di commento originaria recita "Testing null strings #+ and unquoted strings, but not strings and sealing wax, not to # mention cabbages and kings ..." attribuita a Florian Wisser. La # seconda riga non stata tradotta in quanto, la sua traduzione # letterale, non avrebbe avuto alcun senso nel contesto attuale # (N.d.T.).
exit $?
80
Capitolo 7. Veriche
# In questo punto dello script "exit $?" inutile perch lo script, # in ogni caso, restituir lexit status dellultimo comando eseguito.
confronti composti
-a and logico exp1 -a exp2 restituisce vero se entrambe exp1 e exp2 sono vere.
Sono simili agli operatori di confronto Bash && e || utilizzati allinterno delle doppie parentesi quadre.
[[ condizione1 && condizione2 ]]
Gli operatori -o e -a vengono utilizzati con il comando test o allinterno delle parentesi quadre singole.
if [ "$exp1" -a "$exp2" ]
Fate riferimento ad Esempio 8-3, Esempio 26-16 e Esempio A-28 per vedere allopera gli operatori di confronto composto.
if [ condizione1 ] then if [ condizione2 ] then fa-qualcosa # Ma solo se sia "condizione1" che "condizione2" sono vere. fi fi
81
Capitolo 7. Veriche Vedi Esempio 34-4 per una dimostrazione dei costrutti condizionali if/then annidati.
if [ -f $HOME/.Xclients ]; then exec $HOME/.Xclients elif [ -f /etc/X11/xinit/Xclients ]; then exec /etc/X11/xinit/Xclients else # failsafe settings. Although we should never get here # (we provide fallbacks in Xclients as well) it cant hurt. xclock -geometry 100x100-5+5 & xterm -geometry 80x50-50+150 & if [ -f /usr/bin/netscape -a -f /usr/share/doc/HTML/index.html ]; then netscape /usr/share/doc/HTML/index.html & fi fi
Spiegate i costrutti di verica del frammento precedente, quindi esaminate lintero le /etc/X11/xinit/xinitrc ed analizzate i costrutti if/then. necessario consultare i capitoli riguardanti grep, sed e le espressioni regolari pi avanti.
Note
1. Fate attenzione che il bit suid impostato su le binari (eseguibili) pu aprire falle di sicurezza. Il bit suid non ha alcun effetto sugli script di shell. 2. Nei moderni sistemi UNIX, lo sticky bit viene utilizzato solo sulle directory e non pi sui le. 3. Come sottolinea S.C., in una verica composta, il quoting di una variabile stringa pu non essere sufciente. [ -n "$stringa" -o "$a" = "$b" ] potrebbe, con alcune versioni di Bash, provocare un errore se $stringa fosse vuota. Il modo per evitarlo quello di aggiungere un carattere extra alle variabili che potrebbero essere vuote, [ "x$stringa" != x -o "x$a" = "x$b" ] (le x si annullano).
82
var=27 categoria=minerali
Cautela
Non bisogna assolutamente confondere l= operatore di assegnamento con l= operatore di verica.
if [ "$stringa1" = "$stringa2" ] # if [ "X$stringa1" = "X$stringa2" ] pi sicuro, evita un #+ messaggio derrore se una delle variabili dovesse essere vuota. # (Le due "X" anteposte si annullano). then comando fi
operatori aritmetici
+ pi meno * per
83
# z = 125
bash$ expr 5 % 3 2
5/3 = 1 con resto 2 Questo operatore viene utilizzato, tra laltro, per generare numeri in un determinato intervallo (vedi Esempio 9-24, Esempio 9-27) e per impaginare loutput dei programmi (vedi Esempio 26-15 e Esempio A-6). anche utile per generare numeri primi, (vedi Esempio A-16). Modulo si trova sorprendentemente spesso in diverse formule matematiche. Esempio 8-1. Massimo comun divisore
#!/bin/bash # gcd.sh: massimo comun divisore # Uso dellalgoritmo di Euclide # Il "massimo comun divisore" (MCD) di due interi lintero #+ pi grande che divide esattamente entrambi. # # #+ #+ #+ #+ # # # Lalgoritmo di Euclide si basa su divisioni successive. Ad ogni passaggio, dividendo <--- divisore divisore <--- resto finch resto = 0. Nellultimo passaggio MCD = dividendo. Per uneccellente disamina dellalgoritmo di Euclide, vedi al sito di Jim Loy, http://www.jimloy.com/number/euclids.htm.
84
mcd () { # # #+ # Assegnamento arbitrario. Non ha importanza quale dei due maggiore. Perch?
dividendo=$1 divisore=$2
resto=1
# Se la variabile usata in un ciclo non #+ inizializzata, il risultato un errore #+ al primo passaggio nel ciclo.
until [ "$resto" -eq 0 ] do let "resto = $dividendo % $divisore" dividendo=$divisore # Ora viene ripetuto con 2 numeri pi piccoli. divisore=$resto done # Algoritmo di Euclide } # Lultimo $dividendo il MCD.
# Esercizio : # -------# Verificate gli argomenti da riga di comando per essere certi che siano #+ degli interi, se non lo fossero uscite dallo script con un adeguato #+ messaggio derrore. exit 0
+= pi-uguale (incrementa una variabile con una costante let "var += 5" come risultato var stata incrementata di 5.
85
Capitolo 8. Operazioni ed argomenti correlati -= meno-uguale (decrementa una variabile di una costante) *= per-uguale (moltiplica una variabile per una costante) let "var *= 4" come risultato var stata moltiplicata per 4.
/= diviso-uguale (divide una variabile per una costante) %= modulo-uguale (resto della divisione di una variabile per una costante) Gli operatori aritmetici si trovano spesso in espressioni con expr o let. Esempio 8-2. Utilizzo delle operazioni aritmetiche
#!/bin/bash # Contare fino a 11 in 10 modi diversi. n=1; echo -n "$n " let "n = $n + 1" echo -n "$n " # Va bene anche let "n = n + 1".
: $((n = $n + 1)) # I ":" sono necessari perch altrimenti Bash tenta #+ di interpretare "$((n = $n + 1))" come un comando. echo -n "$n " (( n = n + 1 )) # Alternativa pi semplice del metodo precedente. # Grazie a David Lombard per la precisazione. echo -n "$n " n=$(($n + 1)) echo -n "$n " : $[ n = $n + 1 ] # I ":" sono necessari perch altrimenti Bash tenta #+ di interpretare "$[ n = $n + 1 ]" come un comando. # Funziona anche se "n" fosse inizializzata come stringa. echo -n "$n " n=$[ $n + 1 ] # Funziona anche se "n" fosse inizializzata come stringa.
86
# e anche : $[ ++n ]]
Nota: In Bash, attualmente, le variabili intere sono del tipo signed long (32-bit) comprese nellintervallo da -2147483648 a 2147483647. Unoperazione comprendente una variabile con un valore al di fuori di questi limiti d un risultato sbagliato.
a=2147483646 echo "a = $a" let "a+=1" echo "a = $a" let "a+=1" echo "a = $a"
# # # # # #
a = 2147483646 Incrementa "a". a = 2147483647 incrementa ancora "a", viene oltrepassato il limite. a = -2147483648 ERRORE (fuori intervallo)
87
Cautela
Bash non contempla laritmetica in virgola mobile. Considera i numeri che contengono il punto decimale come stringhe.
a=1.5 let "b = $a + 1.3" # Errore. # t2.sh: let: b = 1.5 + 1.3: syntax error in expression #+ (error token is ".5 + 1.3") echo "b = $b" # b=1
Si utilizzi bc negli script in cui sono necessari i calcoli in virgola mobile, oppure le librerie di funzioni matematiche.
Operatori bitwise. Gli operatori bitwise compaiono raramente negli script di shell. Luso principale sembra essere quello di manipolare e vericare i valori letti dalle porte o dai socket. Lo scorrimento di bit pi importante nei linguaggi compilati, come il C e il C++, che sono abbastanza veloci per consentirne un uso procuo.
operatori bitwise
<< scorrimento a sinistra (moltiplicazione per 2 per ogni posizione spostata) <<= scorrimento a sinistra-uguale let "var <<= 2" come risultato i bit di var sono stati spostati di 2 posizioni verso sinistra (moltiplicazione per 4)
>> scorrimento a destra (divisione per 2 per ogni posizione spostata) >>= scorrimento a destra-uguale (inverso di <<=) & AND bitwise &= AND bitwise-uguale
88
Capitolo 8. Operazioni ed argomenti correlati | OR bitwise |= OR bitwise-uguale ~ complemento bitwise ! NOT bitwise ^ XOR bitwise ^= XOR bitwise-uguale
operatori logici
&& and (logico)
if [ $condizione1 ] && [ $condizione2 ] # Uguale a: if [ $condizione1 -a $condizione2 ] # Restituisce vero se entrambe, condizione1 e condizione2, sono vere... if [[ $condizione1 && $condizione2 ]] # Funziona anche cos. # Notate che loperatore && non consentito nel costrutto [ ... ].
Nota: && pu essere utilizzato, secondo il contesto, in una lista and per concatenare dei comandi.
|| or (logico)
if [ $condizione1 ] || [ $condizione2 ] # Uguale a: if [ $condizione1 -o $condizione2 ] # Restituisce vero se vera o condizione1 o condizione2 ... if [[ $condizione1 || $condizione2 ]] # Funziona anche cos.
89
Nota: Bash verica lexit status di ogni enunciato collegato con un operatore logico.
if [ "$a" -eq 98 ] || [ "$b" -eq 47 ] then echo "Verifica nr.2 eseguita con successo." else echo "Verifica nr.2 fallita." fi
# Le opzioni -a e -o offrono #+ una condizione di verifica composta alternativa. # Grazie a Patrick Callahan per la precisazione.
if [ "$a" -eq 24 -a "$b" -eq 47 ] then echo "Verifica nr.3 eseguita con successo." else echo "Verifica nr.3 fallita." fi
90
if [ "$a" -eq 98 -o "$b" -eq 47 ] then echo "Verifica nr.4 eseguita con successo." else echo "Verifica nr.4 fallita." fi
a=rinoceronte b=coccodrillo if [ "$a" = rinoceronte ] && [ "$b" = coccodrillo ] then echo "Verifica nr.5 eseguita con successo." else echo "Verifica nr.5 fallita." fi exit 0
bash$ echo $(( 1 && 2 )) $((3 && 0)) $((4 || 0)) $((0 || 0)) 1 0 1 0
operatori diversi
, operatore virgola Loperatore virgola concatena due o pi operazioni aritmetiche. Vengono valutate tutte le operazioni (con possibili effetti collaterali), ma viene restituita solo lultima.
let "t1 = ((5 + 3, 7 - 1, 15 - 4))" echo "t1 = $t1" # t1 = 11 let "t2 = ((a = 9, 15 / 3))" echo "t2 = $t2 a = $a" # Imposta "a" e calcola "t2" # t2 = 5 a = 9
Loperatore virgola viene impiegato principalmente nei cicli for. Vedi Esempio 10-12.
91
# 32
# Ottale: numeri preceduti da 0 (zero) let "oct = 032" echo "numero ottale = $ott" # Risultato visualizzato come decimale. # --------- ------------ ---- --------
# 26
# Esadecimale: numeri preceduti da 0x o 0X let "esa = 0x32" echo "numero esadecimale = $esa" # 50 # Risultato visualizzato come decimale. # # # #+ Altre basi: BASE#NUMERO BASE tra 2 e 64. NUMERO deve essere formato dai simboli nellintervallo indicato da BASE, vedi di seguito.
let "bin = 2#111100111001101" echo "numero binario = $bin" let "b32 = 32#77" echo "numero in base 32 = $b32"
# 31181
# 231
let "b64 = 64#@_" echo "numero in base 64 = $b64" # 4031 # # Questa notazione funziona solo per un intervallo limitato (2 - 64) #+ di caratteri ASCII # 10 cifre + 26 caratteri minuscoli + 26 caratteri maiuscoli + @ + _
92
# # # #+
Nota importante: --------------Utilizzare un simbolo al di fuori dellintervallo della base specificata provoca un messaggio derrore.
let "ott_errato = 081" # Messaggio (parziale) derrore visualizzato: # ott_errato = 081: value too great for base #+ (error token is "081") # I numeri ottali utilizzano solo cifre nellintervallo 0 - 7. exit 0 # Grazie, Rich Bartell e Stephane Chazelas, per il chiarimento.
93
$BASH_ENV
variabile dambiente che punta al le di avvio di Bash, che deve essere letto quando si invoca uno script
$BASH_SUBSHELL
variabile che indica il livello della subshell. Si tratta di una nuova variabile aggiunta in Bash, versione 3. Per il suo impiego vedi Esempio 20-1.
$BASH_VERSINFO[n]
un array di 6 elementi contenente informazioni sulla versione Bash installata. simile a $BASH_VERSION, vedi oltre, ma pi dettagliata.
# Informazioni sulla versione Bash: for n in 0 1 2 3 4 5 do echo "BASH_VERSINFO[$n] = ${BASH_VERSINFO[$n]}" done # # # # # # BASH_VERSINFO[0] BASH_VERSINFO[1] BASH_VERSINFO[2] BASH_VERSINFO[3] BASH_VERSINFO[4] BASH_VERSINFO[5] = = = = = = 3 00 14 1 release i386-redhat-linux-gnu # # # # # # Nr. della major version. Nr. della minor version. Nr. del patch level. Nr. della build version. Stato della release. Architettura.
95
$BASH_VERSION
Un buon metodo per determinare quale shell in funzione quello di vericare $BASH_VERSION. $SHELL potrebbe non fornire necessariamente una risposta corretta.
$DIRSTACK
il contenuto della locazione pi alta dello stack delle directory (determinato da pushd e popd) Questa variabile corrisponde al comando dirs, senonch dirs mostra lintero contenuto dello stack delle directory.
$EDITOR
numero ID effettivo dellutente Numero identicativo dellutente corrente corrispondente a qualsiasi identit egli abbia assunto, solitamente tramite il comando su.
Cautela
$EUID, di conseguenza, non necessariamente uguale a $UID.
96
# xyz23 in esecuzione.
$GLOBIGNORE
i gruppi a cui appartiene lutente corrente lelenco (array) dei numeri id dei gruppi a cui appartiene lutente corrente, cos come sono registrati nel le /etc/passwd.
$HOME
In fase di boot, il comando hostname, presente in uno script init, assegna il nome del sistema. Tuttavia la funzione gethostname() che imposta la variabile interna Bash $HOSTNAME. Vedi anche Esempio 9-14.
97
$IFS
separatore di campo (internal eld separator) Questa variabile determina il modo in cui Bash riconosce i campi, ovvero le singole parole, nellinterpratazione delle stringhe di caratteri. Il valore preimpostato una spaziatura (spazio, tabulazione e ritorno a capo), ma pu essere modicato, per esempio, per vericare un le dati che usa la virgola come separatore di campo. E da notare che $* utilizza il primo carattere contenuto in $IFS. Vedi Esempio 5-1.
98
Cautela
$IFS non tratta la spaziatura allo stesso modo degli altri caratteri.
echo; echo "IFS=:" echo "-----" IFS=: var=":a::b:c:::" output_arg_uno_per_riga $var # # [] # [a] # [] # [b] # [c] # [] # [] # []
# In awk si ottiene lo stesso risultato con il separatore di campo "F # Grazie, Stephane Chazelas. echo exit 0
99
Capitolo 9. Variabili riviste Vedi anche Esempio 12-37 per unistruttiva dimostrazione sullimpiego di $IFS.
$IGNOREEOF
ignora EOF: quanti end-of-le (control-D) la shell deve ignorare prima del logout
$LC_COLLATE
Spesso impostata nei le .bashrc o /etc/profile, questa variabile controlla lordine di collazione nellespansione del nome del le e nella ricerca di corrispondenza. Se mal gestita, LC_COLLATE pu provocare risultati inattesi nel globbing dei nomi dei le.
Nota: Dalla versione 2.05 di Bash, il globbing dei nomi dei le non fa pi distinzione tra lettere minuscole e maiuscole, in un intervallo di caratteri specicato tra parentesi quadre. Per esempio, ls [A-M]* restituisce sia File1.txt che file1.txt. Per riportare il globbing allabituale comportamento, si imposti LC_COLLATE a C con export LC_COLLATE=C nel le /etc/profile e/o ~/.bashrc.
$LC_CTYPE
Questa variabile interna controlla linterpretazione dei caratteri nel globbing e nella ricerca di corrispondenza.
$LINENO
Variabile contenente il numero della riga dello script di shell in cui essa appare. Ha valore solo nello script in cui si trova. utile in modo particolare nel debugging.
# *** INIZIO BLOCCO DI DEBUGGING *** ultimo_arg_cmd=$_ # Viene salvato. echo "Alla riga numero $LINENO, variabile \"v1\" = $v1" echo "Ultimo argomento eseguito = $ultimo_arg_cmd" # *** FINE BLOCCO DI DEBUGGING ***
$MACHTYPE
100
directory di lavoro precedente (OLD-print-working-directory, la directory in cui vi trovavate prima dellultimo comando cd)
$OSTYPE
$PATH
i percorsi delle directory in cui si trovano i le eseguibili (binari), di solito /usr/bin/, /usr/X11R6/bin/, /usr/local/bin, ecc. Quando viene dato un comando, la shell ricerca automaticamente il percorso delleseguibile. Questo possibile perch tale percorso memorizzato nella variabile dambiente $PATH, che un elenco di percorsi possibili separati da : (due punti). Di solito il sistema conserva la congurazione di $PATH nel le /etc/profile e/o ~/.bashrc (vedi Appendice G).
PATH=${PATH}:/opt/bin aggiunge la directory /opt/bin ai percorsi predeniti. Usato in uno script rappresenta un espediente per aggiungere temporaneamente una directory a $PATH. Quando lo script termina viene ripristinato il valore originale di $PATH (questo perch un processo glio, qual uno script, non pu modicare lambiente del processo genitore, la shell).
Nota: La directory di lavoro corrente, ./, di solito per ragioni di sicurezza, non compresa in
$PATH.
$PIPESTATUS
Array contenente lo/gli exit status dellultima pipe eseguita in foreground (primo piano). piuttosto interessante in quanto non fornisce necessariamente come risultato lexit status dellultimo comando eseguito.
bash$ echo $PIPESTATUS 0 bash$ ls -al | comando_errato bash: comando_errato: command not found
101
Gli elemnti dellarray $PIPESTATUS sono gli exit status dei corrispondenti comandi eseguiti nella pipe. $PIPESTATUS[0] contiene lexit status del primo comando della pipe, $PIPESTATUS[1] lexit status del secondo comando, e cos via.
Cautela
La variabile $PIPESTATUS, in una shell di login, potrebbe contenere un errato valore 0 (nelle versioni Bash precedenti alla 3.0).
tcsh% bash bash$ who | grep nobody | sort bash$ echo ${PIPESTATUS[*]} 0
Grazie a Wayne Pollock per la puntualizzazione e per aver fornito lesempio precedente.
102
Nota: $PIPESTATUS una variabile volatile. Deve essere visualizzata immediatamente dopo la pipe, prima che venga eseguito qualsiasi altro comando.
bash$ $ ls | comando_errato | wc bash: comando_errato: command not found 0 0 0 bash$ echo ${PIPESTATUS[@]} 0 127 0 bash$ echo ${PIPESTATUS[@]} 0
$PPID
Lo $PPID di un processo non che lID di processo (pid) del processo genitore. 1 Lo si confronti con il comando pidof.
$PROMPT_COMMAND
Variabile che contiene un comando che deve essere eseguito immediatamente prima della visualizzazione del prompt primario $PS1 .
$PS1
Prompt secondario. Compare quando atteso un ulteriore input (il comando non ancora terminato). Viene visualizzato come >.
103
Prompt di quarto livello. Viene visualizzato allinizio di ogni riga di output quando lo script stato invocato con lopzione -x. Viene visualizzato come +.
$PWD
#!/bin/bash E_ERRATA_DIRECTORY=73 clear # Pulisce lo schermo. DirectoryDestinazione=/home/bozo/projects/GreatAmericanNovel cd $DirectoryDestinazione echo "Cancellazione dei vecchi file in $DirectoryDestinazione." if [ "$PWD" != "$DirectoryDestinazione" ] then # Evita di cancellare per errore una directory sbagliata. echo "Directory errata!" echo "Sei in $PWD, non in $DirectoryDestinazione!" echo "Salvo!" exit $E_ERRATA_DIRECTORY fi rm -rf * rm .[A-Za-z0-9]* # Cancella i file i cui nomi iniziano con un punto. # rm -f .[^.]* ..?* per cancellare file che iniziano con due o pi punti. # (shopt -s dotglob; rm -f *) anche in questo modo. # Grazie, S.C. per la puntualizzazione. # I nomi dei file possono essere formati da tutti i caratteri nellintervallo #+ 0 - 255, tranne "/". La cancellazione di file che iniziano con caratteri #+ inconsueti lasciata come esercizio. # Altre eventuali operazioni. echo echo "Fatto." echo "Cancellati i vecchi file in $DirectoryDestinazione." echo
104
$REPLY
la variabile preimpostata quando non ne viene fornita alcuna a read. utilizzabile anche con i menu select. In questo caso, per, fornisce solo il numero che indica la variabile scelta, non il valore della variabile.
#!/bin/bash # reply.sh # REPLY la variabile preimpostata per il comando read. echo echo -n "Qual la tua verdura preferita?" read echo "La tua verdura preferita $REPLY." # REPLY contiene il valore dellultimo "read" se e solo se #+ non stata indicata alcuna variabile. echo echo -n "Qual il tuo frutto preferito?" read frutto echo "Il tuo frutto preferito $frutto." echo "ma..." echo "Il valore di \$REPLY ancora $REPLY." # $REPLY ancora impostato al valore precedente perch #+ la variabile $frutto contiene il nuovo valore letto con "read". echo exit 0
$SECONDS
#!/bin/bash TEMPO_LIMITE=10 INTERVALLO=1 echo echo "Premi Control-C per terminare prima di $TEMPO_LIMITE secondi." echo
105
while [ "$SECONDS" -le "$TEMPO_LIMITE" ] do if [ "$SECONDS" -eq 1 ] then unita=secondo else unita=secondi fi echo "Questo script in esecuzione da $SECONDS $unita." # Su una macchina lenta o sovraccarica lo script, talvolta, #+ potrebbe saltare un conteggio. sleep $INTERVALLO done echo -e "\a" exit 0 # Beep!
$SHELLOPTS
$SHLVL
Livello della shell. Profondit di annidamento di Bash. Se, da riga di comando $SHLVL vale 1, in uno script questo valore viene aumentato a 2.
$TMOUT
Se la variabile dambiente $TMOUT impostata ad un valore tempo diverso da zero, il prompt della shell termina dopo tempo secondi. Questo provoca il logout. Dalla versione Bash 2.05b possibile utilizzare $TMOUT negli script in combinazione con read.
# Funziona negli script con Bash versione 2.05b e successive. TMOUT=3 # Imposta il prompt alla durata di tre secondi.
echo "Qual la tua canzone preferita?" echo "Svelto, hai solo $TMOUT secondi per rispondere!" read canzone if [ -z "$canzone" ] then
106
Esistono altri metodi, pi complessi, per implementare un input temporizzato in uno script. Una possibile alternativa quella di impostare un ciclo di temporizzazione per segnalare allo script quando il tempo scaduto. Ma anche cos necessaria una routine per la gestione di un segnale per catturare (trap) (vedi Esempio 29-5) linterrupt generato dal ciclo di temporizzazione (u!). Esempio 9-2. Input temporizzato
#!/bin/bash # timed-input.sh # TMOUT=3 # TEMPOLIMITE=3 Funziona anche questo, a partire dalle pi recenti versioni di Bash. # In questo caso tre secondi. Pu essere impostato #+ ad un valore diverso.
VisualizzaRisposta() { if [ "$risposta" = TIMEOUT ] then echo $risposta else # Ho voluto tenere separati i due esempi. echo "La tua verdura preferita $risposta" kill $! # Uccide la funzione AvvioTimer in esecuzione in #+ background perch non pi necessaria. $! il PID #+ dellultimo job in esecuzione in background. fi }
AvvioTimer() { sleep $TEMPOLIMITE && kill -s 14 $$ & # Attende 3 secondi, quindi invia il segnale SIGALARM allo script. } Int14Vettore() { risposta="TIMEOUT" VisualizzaRisposta exit 14
107
echo "Qual la tua verdura preferita? " AvvioTimer read risposta VisualizzaRisposta # Ammettiamolo, questa unimplementazione tortuosa per temporizzare #+ linput, comunque lopzione "-t" di "read" semplifica il compito. # Vedi "t-out.sh" pi sotto. # Se desiderate qualcosa di pi elegante... prendete in considerazione #+ la possibilit di scrivere lapplicazione in C o C++, #+ utilizzando le funzioni di libreria appropriate, come alarm e setitimer. exit 0
leggi_temporizzazione() { timeout=$1 nomevar=$2 precedenti_impostazioni_tty=stty -g stty -icanon min 0 time ${timeout}0 eval read $nomevar # o semplicemente stty "$precedenti_impostazioni_tty" # Vedi la pagina di manuale di "stty". } echo; echo -n "Come ti chiami? Presto! " leggi_temporizzazione $INTERVALLO nome
read $nomevar
# Questo potrebbe non funzionare su tutti i tipi di terminale. # Il timeout massimo, infatti, dipende dallo specifico terminale. #+ (spesso di 25.5 secondi). echo if [ ! -z "$nome" ] then # se il nome stato immesso prima del timeout...
108
Forse, il metodo pi semplice quello di usare read con lopzione -t. Esempio 9-4. read temporizzato
#!/bin/bash # t-out.sh # Ispirato da un suggerimento di "syngin seven" (grazie).
TEMPOLIMITE=4
# 4 secondi
read -t $TEMPOLIMITE variabile <&1 # ^^^ # In questo esempio, "<&1" necessario per Bash 1.x e 2.x, # ma inutile per Bash 3.x. echo if [ -z "$variabile" ] # nulla? then echo "Tempo scaduto, la variabile non stata impostata." else echo "variabile = $variabile" fi exit 0
$UID
numero ID dellutente il numero identicativo dellutente corrente com registrato nel le /etc/passwd. Rappresenta lid reale dellutente, anche nel caso abbia assunto temporaneamente unaltra identit per mezzo di su. $UID una variabile in sola lettura e non pu essere modicata n da riga di comando n in uno script. il sostituto del builtin id.
109
then echo "Sei root." else echo "Sei un utente normale (ma la mamma ti vuol bene lo stesso)." fi exit 0
# ===================================================================== # # Il codice seguente non viene eseguito perch lo script gi terminato. # Un metodo alternativo per andare al fondo della questione: NOME_ROOT=root nomeutente=id -nu # Oppure... if [ "$nomeutente" = "$NOME_ROOT" ] then echo "Rooty, toot, toot. Sei root." else echo "Sei solo un semplice utente." fi nomeutente=whoami
110
Parametri Posizionali
$0, $1, $2, ecc.
rappresentano i diversi parametri che vengono passati da riga di comando ad uno script, ad una funzione, o per impostare una variabile (vedi Esempio 4-5 e Esempio 11-15)
$#
numero degli argomenti passati da riga di comando, 2 ovvero numero dei parametri posizionali (vedi Esempio 33-2)
$*
$@
Simile a $*, ma ogni parametro una stringa tra apici (quoting), vale a dire, i parametri vengono passati intatti, senza interpretazione o espansione. Questo signica, tra laltro, che ogni parametro dellelenco viene considerato come una singola parola.
Nota: Naturalmente, $@ va usata con il quoting.
111
echo "Elenco degli argomenti con \"\$*\":" for arg in "$*" # Non funziona correttamente se "$*" non tra apici. do echo "Argomento nr.$indice = $arg" let "indice+=1" done # $* vede tutti gli argomenti come ununica parola. echo "Tutto lelenco come parola singola." echo indice=1 # Reimposta il contatore. # Cosa succede se vi dimenticate di farlo?
echo "Elenco degli argomenti con \"\$@\":" for arg in "$@" do echo "Argomento nr.$indice = $arg" let "indice+=1" done # $@ vede gli argomenti come parole separate. echo "Elenco composto da diverse parole." echo indice=1 # Reimposta il contatore.
echo "Eleco degli argomenti con \$* (senza quoting):" for arg in $* do echo "Argomento nr.$indice = $arg" let "indice+=1" done # $* senza quoting vede gli argomenti come parole separate. echo "Elenco composto da diverse parole." exit 0
Dopo uno shift, venendo a mancare il precedente $1, che viene perso, $@ contiene i restanti parametri posizionali.
#!/bin/bash # Da eseguire con echo "$@" shift echo "$@" shift echo "$@"
./nomescript 1 2 3 4 5
# 1 2 3 4 5 # 2 3 4 5 # 3 4 5
# Ogni "shift" perde il precedente $1. # Come conseguenza "$@" contiene i parametri rimanenti.
112
Capitolo 9. Variabili riviste Allinterno degli script di shell, la variabile speciale $@ viene utilizzata come strumento per ltrare un dato input. Il costrutto cat "$@" permette di gestire un input da uno script, dallo stdin o da le forniti come parametri. Vedi Esempio 12-21 e Esempio 12-22.
Cautela
I parametri $* e $@ talvolta si comportano in modo incoerente e sorprendente. Questo dipende dallimpostazione di $IFS.
113
IFS=: echo IFS=":", utilizzando "$*" c=0 for i in "$*" do echo "$((c+=1)): [$i]" done echo --echo IFS=":", utilizzando $* c=0 for i in $* do echo "$((c+=1)): [$i]" done echo --var=$* echo IFS=":", utilizzando "$var" (var=$*) c=0 for i in "$var" do echo "$((c+=1)): [$i]" done echo --echo IFS=":", utilizzando $var (var=$*) c=0 for i in $var do echo "$((c+=1)): [$i]" done echo --var="$*" echo IFS=":", utilizzando $var (var="$*") c=0 for i in $var do echo "$((c+=1)): [$i]" done echo --echo IFS=":", utilizzando "$var" (var="$*") c=0 for i in "$var" do echo "$((c+=1)): [$i]" done echo --echo IFS=":", utilizzando "$@" c=0 for i in "$@" do echo "$((c+=1)): [$i]" done echo ---
114
Nota: I parametri $@ e $* differiscono solo quando vengono posti tra doppi apici.
115
# Il comportamento di $* e $@ quando $IFS vuota dipende da quale #+ versione Bash o sh in esecuzione. quindi sconsigliabile fare #+ affidamento su questa "funzionalit" in uno script.
Cautela
In origine era un costrutto ksh che stato adottato da Bash, ma, sfortunatamente, non sembra funzionare in modo attendibile negli script Bash. Un suo possibile uso quello di eseguire unautoverica di interattivit.
116
LOG=$0.log COMANDO1="sleep 100" echo "Registra i PID dei comandi in background dello script: $0" >> "$LOG" # Possono essere cos controllati e, se necessario, "uccisi". echo >> "$LOG" # Registrazione dei comandi. echo -n "PID di \"$COMANDO1\": ${COMANDO1} & echo $! >> "$LOG" # PID di "sleep 100": 1506 " >> "$LOG"
possibile_job_bloccante & { sleep ${TIMEOUT}; eval kill -9 $! &> /dev/null; } # Forza il completamento di un programma mal funzionante. # Utile, ad esempio, negli script init.
# Grazie a Sylvain Fourmanoit per aver segnalato questuso creativo della variabile "!".
$_
Variabile speciale impostata allultimo argomento del precedente comando eseguito. Esempio 9-9. Variabile underscore
#!/bin/bash echo $_ # /bin/bash # digitate solo /bin/bash per eseguire lo script. # Non viene visualizzato alcun output del comando. # du # Non viene visualizzato alcun output del comando. # -al (ultimo argomento)
# :
117
Exit status di un comando, funzione, o dello stesso script (vedi Esempio 23-7)
$$
ID di processo dello script. La variabile $$ viene spesso usata negli script per creare un nome di le temporaneo univoco (vedi Esempio A-13, Esempio 29-6, Esempio 12-28 e Esempio 11-25). Di solito pi semplice che invocare mktemp.
Bash supporta un numero sorprendentemente elevato di operazioni per la manipolazione delle stringhe. Purtroppo, questi strumenti mancano di organizzazione e razionalizzazione. Alcuni sono un sotto insieme della sostituzione di parametro, altri appartengono alle funzionalit del comando UNIX expr. Tutto questo si traduce in una sintassi dei comandi incoerente ed in una sovrapposizione di funzionalit, per non parlare della confusione.
expr "$stringa" : .*
stringaZ=abcABC123ABCabc echo ${#stringaZ} echo expr length $stringaZ echo expr "$stringaZ" : .* # 15 # 15 # 15
118
len=${#riga} if [ "$len" -lt "$LUNMIN" ] then echo # Aggiunge la riga bianca. fi done exit 0
stringaZ=abcABC123ABCabc # |------| echo expr match "$stringaZ" abc[A-Z]*.2 echo expr "$stringaZ" : abc[A-Z]*.2 # 8 # 8
Indice
expr index $stringa $sottostringa Numero di posizione in $stringa del primo carattere presente in $sottostringa che stato vericato.
# 6 # Posizione di C.
echo expr index "$stringaZ" 1c # 3 # c (in terza posizione) viene verificato prima di 1.
119
Estrazione di sottostringa
${stringa:posizione} Estrae la sottostringa da $stringa iniziando da $posizione. Se il parametro $stringa * o @, allora vengono estratti i parametri posizionali, 3 iniziando da $posizione.
stringaZ=abcABC123ABCabc # 0123456789..... # Lindicizzazione inizia da 0. echo ${stringaZ:0} echo ${stringaZ:1} echo ${stringaZ:7} echo ${stringaZ:7:3} # abcABC123ABCabc # bcABC123ABCabc # 23ABCabc # 23A # Sottostringa di tre caratteri.
# possibile indicizzare partendo dalla fine della stringa? echo ${stringaZ:-4} # abcABC123ABCabc # Restituisce lintera stringa, come con ${parametro:-default}. # Tuttavia . . . echo ${stringaZ:(-4)} # Cabc echo ${stringaZ: -4} # Cabc # Ora funziona. # Le parentesi, o laggiunta di uno spazio, "preservano" il parametro negativo. # Grazie, Dan Jacobson, per averlo evidenziato.
Se il parametro $stringa * o @, vengono estratti un massimo di $lunghezza parametri posizionali, iniziando da $posizione.
# #
120
expr substr $stringa $posizione $lunghezza Estrae $lunghezza caratteri da $stringa iniziando da $posizione.
stringaZ=abcABC123ABCabc # 123456789...... # Lindicizzazione inizia da 1. echo expr substr $stringaZ 1 2 echo expr substr $stringaZ 4 3 # ab # ABC
expr match "$stringa" \($sottostringa\) Estrae $sottostringa dalla parte iniziale di $stringa, dove $sottostringa una espressione regolare. expr "$stringa" : \($sottostringa\) Estrae $sottostringa dalla parte iniziale di $stringa, dove $sottostringa unespressione regolare.
stringaZ=abcABC123ABCabc # ======= echo expr echo expr echo expr # Tutte le match "$stringaZ" \(.[b-c]*[A-Z]..[0-9]\) "$stringaZ" : \(.[b-c]*[A-Z]..[0-9]\) "$stringaZ" : \(.......\) forme precedenti danno lo stesso risultato. # abcABC1 # abcABC1 # abcABC1
expr match "$stringa" .*\($sottostringa\) Estrae $sottostringa dalla parte nale di $stringa, dove $sottostringa unespressione regolare. expr "$stringa" : .*\($sottostringa\) Estrae $sottostringa dalla parte nale di $stringa, dove $sottostringa unespressione regolare.
stringaZ=abcABC123ABCabc # ======
121
# ABCabc # ABCabc
Rimozione di sottostringa
${stringa#sottostringa} Toglie loccorrenza pi breve di $sottostringa dalla parte iniziale di $stringa. ${stringa##sottostringa} Toglie loccorrenza pi lunga di $sottostringa dalla parte iniziale di $stringa.
stringaZ=abcABC123ABCabc # |----| # |----------| echo ${stringaZ#a*C} # 123ABCabc # stata tolta loccorrenza pi breve compresa tra a e C. echo ${stringaZ##a*C} # abc # stata tolta loccorrenza pi lunga compresa tra a e C.
${stringa%sottostringa} Toglie loccorrenza pi breve di $sottostringa dalla parte nale di $stringa. ${stringa%%sottostringa} Toglie loccorrenza pi lunga di $sottostringa dalla parte nale di $stringa.
stringaZ=abcABC123ABCabc # || # |------------| echo ${stringaZ%b*c} # abcABC123ABCa # stata tolta loccorrenza pi breve compresa #+ tra b e c, dalla fine di $stringaZ. echo ${stringaZ%%b*c} # a # stata tolta loccorrenza pi lunga compresa #+ tra b e c, dalla fine di $stringaZ.
122
Capitolo 9. Variabili riviste Esempio 9-11. Conversione di formato di le graci e modica del nome dei le
#!/bin/bash # cvt.sh: # Converte tutti i file immagine MacPaint, in una directory data, #+ nel formato "pbm". # #+ # #+ Viene utilizzato leseguibile "macptopbm" del pacchetto "netpbm", mantenuto da Brian Henderson ([email protected]). Netpbm di solito compreso nellinstallazione standard della maggior parte delle distribuzioni Linux.
# Se viene fornito il nome di una directory come #+ argomento dello script... # Altrimenti viene utilizzata la directory corrente.
# Si assume che tutti i file immagine nella directory siano dei MacPaint, #+ con nomi aventi estensione ".mac" for file in $directory/* do nomefile=${file%.*c} # Globbing dei nomi dei file.
# Toglie lestensione ".mac" dal nome del file #+ (.*c verifica tutto tra . e c, compresi). $OPERAZIONE $file > "$nomefile.$ESTENSIONE" # Converte e redirige il file con una nuova #+ estensione. rm -f $file # Cancella i file originali dopo la conversione. echo "$nomefile.$ESTENSIONE" # Visualizza quello che avviene allo stdout. done exit 0 # Esercizio: # ---------# Cos com, lo script converte "tutti" i file presenti nella #+ directory di lavoro corrente. # Modificatelo in modo che agisca "solo" sui file con estensione ".mac".
123
semplice_getopt() { echo "semplice_getopt()" echo "I parametri sono $*" until [ -z "$1" ] do echo "Elaborazione parametro di: $1" if [ ${1:0:1} = / ] then tmp=${1:1} # Elinina le / iniziali . . . parametro=${tmp%%=*} # Estrae il nome. valore=${tmp##*=} # Estrae il valore. echo "Parametro: $parametro, valore: $valore" eval $parametro=$valore fi shift done } # Passiamo tutte le opzioni a semplice_getopt(). semplice_getopt $* echo "verifica $verifica" echo "verifica2 $verifica2" exit 0 --sh getopt_example.sh /verifica=valore1 /verifica2=valore2 I parametri sono /verifica=valore1 /verifica2=valore2 Elaborazione parametro di: /verifica=valore1 Parametro: verifica, valore: valore1 Elaborazione parametro di: /verifica2=valore2 Parametro: verifica2, valore: valore2 verifica valore1 verifica2 valore2
124
Sostituzione di sottostringa
${stringa/sottostringa/sostituto} Sostituisce la prima occorrenza di $sottostringa con $sostituto. ${stringa//sottostringa/sostituto} Sostituisce tutte le occorrenze di $sottostringa con $sostituto.
stringaZ=abcABC123ABCabc echo ${stringaZ/abc/xyz} # xyzABC123ABCabc # Sostituisce la prima occorrenza di abc con xyz. # xyzABC123ABCxyz # Sostituisce tutte le occorrenze di abc con xyz.
echo ${stringaZ//abc/xyz}
${stringa/#sottostringa/sostituto} Se $sottostringa viene vericata allinizio di $stringa, allora $sostituto rimpiazza $sottostringa. ${stringa/%sottostringa/sostituto} Se $sottostringa viene vericata alla ne di $stringa, allora $sostituto rimpiazza $sottostringa.
stringaZ=abcABC123ABCabc echo ${stringaZ/#abc/XYZ} # XYZABC123ABCabc # Sostituisce loccorrenza iniziale abcconXYZ. # abcABC123ABCXYZ # Sostituisce loccorrenza finale abc con XYZ.
echo ${stringaZ/%abc/XYZ}
125
126
tuo_id=${USER}-su-${HOSTNAME} echo "$tuo_id" # echo "Vecchio \$PATH = $PATH" PATH=${PATH}:/opt/bin # Aggiunge /opt/bin a $PATH per la durata dello script. echo "Nuovo \$PATH = $PATH"
${parametro-default} ${parametro:-default} Se parametro non impostato, viene impostato al valore fornito da default.
echo ${nomeutente-whoami} # Visualizza il risultato del comando whoami, se la variabile #+ $nomeutente non ancora impostata.
Nota: ${parametro-default} e ${parametro:-default} sono quasi uguali. Laggiunta dei : serve solo quando parametro stato dichiarato, ma non impostato.
#!/bin/bash # param-sub.sh # Il fatto che una vairabile sia stata dichiarata #+ influenza luso dellopzione preimpostata, #+ anche se la variabile nulla. nomeutente0= echo "nomeutente0 stata dichiarata, ma contiene un valore nullo." echo "nomeutente0 = ${nomeutente0-whoami}" # Non visualizza niente. echo echo nomeutente1 non stata dichiarata.
127
# # Ancora una volta: variabile= # variabile stata dichiarata, ma contiene un valore nullo." echo "${variabile-0}" echo "${variabile:-1}" # ^ unset variabile echo "${variabile-2}" echo "${variabile:-3}" exit 0 # 2 # 3 # (nessun output) # 1
Il costrutto parametro-default viene utilizzato per fornire agli script gli argomenti dimenticati da riga di comando.
DEFAULT_NOMEFILE=generico.dat nomefile=${1:-$DEFAULT_NOMEFILE} # Se non diversamente specificato, il successivo blocco di #+ comandi agisce sul file "generico.dat". # # Seguono comandi.
Vedi anche Esempio 3-4, Esempio 28-2 e Esempio A-6. Si confronti questo metodo con luso di una lista and per fornire un argomento di default.
128
Se parametro non impostato, viene impostato al valore fornito da default. Le due forme sono quasi equivalenti. I : servono solo quando $parametro stato dichiarato, ma non impostato, 4 come visto in precedenza.
${parametro+altro_valore} ${parametro:+altro_valore} Se parametro impostato, assume altro_valore, altrimenti viene impostato come stringa nulla. Le due forme sono quasi equivalenti. I : servono solo quando parametro stato dichiarato, ma non impostato. Vedi sopra.
echo "###### \${parametro+altro_valore} ########" echo a=${param1+xyz} echo "a = $a" param2= a=${param2+xyz} echo "a = $a" param3=123 a=${param3+xyz} echo "a = $a"
# a =
# a = xyz
# a = xyz
echo echo "###### \${parametro:+altro_valore} ########" echo a=${param4:+xyz} echo "a = $a"
# a =
129
# a = xyz
${parametro?msg_err} ${parametro:?msg_err} Se parametro impostato viene usato, altrimenti visualizza un messaggio derrore (msg_err). Le due forme sono quasi equivalenti. I : servono solo quando parametro stato dichiarato, ma non impostato. Come sopra.
: ${HOSTNAME?} ${USER?} ${HOME?} ${MAIL?} echo echo "Il nome della macchina $HOSTNAME." echo "Tu sei $USER." echo "La directory home $HOME." echo "La cartella di posta INBOX si trova in $MAIL." echo echo "Se leggete questo messaggio, vuol dire che" echo "le variabili dambiente pi importanti sono impostate." echo echo # -----------------------------------------------------# Il costrutto ${nomevariabile?} pu verificare anche #+ le variabili impostate in uno script. QuestaVariabile=Valore-di-Questa-Variabile # da notare, en passant, che le variabili stringa possono contenere #+ caratteri che non sono consentiti se usati nei loro nomi . : ${QuestaVariabile?} echo "Il valore di QuestaVariabile $QuestaVariabile". echo echo
130
# Stesso risultato con: #+ finta_variabile=${ZZXy23AB?} #+ finta_variabile=${ZZXy23AB?"ZXy23AB non stata impostata."} # # echo ${ZZXy23AB?} >/dev/null # Confrontate questi metodi per la verifica dellimpostazione di una variabile #+ con "set -u" . . .
echo "Questo messaggio non viene visualizzato perch lo script gi terminato." QUI=0 exit $QUI
# Verificate lexit status dello script eseguito, sia con che senza argomento. # Se il parametro stato fornito, allora "$?" 0. # Altrimenti "$?" 1.
Sostituzione e/o espansione di parametro. Le espressioni che seguono sono il complemento delle operazioni sulle stringhe del costrutto match con expr (vedi Esempio 12-9). Vengono per lo pi usate per la verica dei nomi dei le.
131
${#*} e ${#@} forniscono il numero dei parametri posizionali . Per gli array, ${#array[*]} e ${#array[@]} forniscono il numero degli elementi che compongono larray.
${var#Modello} ${var##Modello} Toglie da $var la parte pi breve/lunga di $Modello vericata allinizio di $var. Una dimostrazione del suo impiego tratta dallEsempio A-7:
# Funzione dallesempio "days-between.sh". # Toglie lo/gli zeri iniziali dallargomento fornito.
132
toglie_zero_iniziale () # Toglie possibili zeri iniziali { #+ dagli argomenti passati. return=${1#0} # "1" st per $1 -- largomento passato. } # "0" indica ci che va tolto da "$1" -- gli zeri.
${var%Modello} ${var%%Modello} Toglie da $var la parte pi breve/lunga di $Modello vericata alla fine di $var. La versione 2 di Bash ha introdotto delle opzioni aggiuntive. Esempio 9-17. Ricerca di corrispondenza nella sostituzione di parametro
#!/bin/bash # patt-matching.sh # Ricerca di corrispondenza utilizzando gli operatori si sostituzione #+ di parametro # ## % %%.
133
# abcd12345abc6789 # abcd12345abc6789 # (forma alternativa) echo "Numero di caratteri in ${var1} = ${#var1}" echo echo "modello1 = $modello1" echo "--------------" echo ${var1#$modello1} = # Alleventuale occorrenza #+ abcd12345abc6789 # |-| echo ${var1##$modello1} = # Alleventuale occorrenza #+ abcd92345abc6789 #+ |----------| echo; echo; echo modello2=b*9 # tutto quello che si trova tra b e 9 echo "var1 = $var1" # Ancora abcd12345abc6789 echo echo "modello2 = $modello2" echo "--------------" echo ${var1%modello2} = "${var1%$modello2}" # Alleventuale occorrenza pi corta, toglie gli #+ abcd12345abc6789 #+ |----| echo ${var1%%modello2} = "${var1%%$modello2}" # Alleventuale occorrenza pi lunga, toglie gli #+ abcd12345abc6789 #+ |-------------| # abcd12345a ultimi 6 caratteri ^^^^^^ # a ultimi 15 caratteri ^^^^^^ # a*c (tutto ci che compreso tra a e c)
"${var1#$modello1}" # d12345abc6789 pi corta, toglie i primi 3 caratteri ^^^^^ "${var1##$modello1}" # 6789 pi lunga, toglie i primi 12 caratteri ^^^^^
# Ricordate, # e ## agiscono sulla parte iniziale della stringa #+ (da sinistra verso destra), % e %% agiscono sulla parte #+ finale della stringa (da destra verso sinistra). echo exit 0
134
E_ERR_ARG=65 case $# in 0|1) # La barra verticale, in questo contesto, significa "or". echo "Utilizzo: basename $0 vecchia_estensione nuova_estensione" exit $E_ERR_ARG # Se gli argomenti sono 0 o 1, interrompe lo script. ;; esac for nomefile in *.$1 # Passa in rassegna lelenco dei file che terminano con il 1mo argomento. do mv $nomefile ${nomefile%$1}$2 # Toglie la parte di nomefile che verifica il 1mo argomento, #+ quindi aggiunge il 2do argomento. done exit 0
135
Capitolo 9. Variabili riviste ${var//Modello/Sostituto} Sostituzione globale. Tutte le occorrenze di Modello presenti in var vengono rimpiazzate da Sostituto. Come prima, se si omette Sostituto allora tutte le occorrenze di Modello vengono rimpiazzate con niente, vale a dire, cancellate. Esempio 9-19. Utilizzare la verica di occorrenza per controllare stringhe arbitrarie
#!/bin/bash var1=abcd-1234-defg echo "var1 = $var1" t=${var1#*-*} echo "var1 (viene tolto tutto ci che si trova prima del primo" echo "trattino, compreso) = $t" # t=${var1#*-} D lo stesso risultato, #+ perch # verifica la stringa pi corta, #+ e * verifica tutto quello che sta prima, compresa una stringa vuota. # (Grazie a Stephane Chazelas per la puntualizzazione.) t=${var1##*-*} echo "Se var1 contiene un \"-\", viene restituita una stringa vuota..." echo "var1 = $t"
t=${var1%*-*} echo "var1 (viene tolto tutto ci che si trova dopo lultimo" echo "trattino, compreso) = $t" echo # ------------------------------------------percorso=/home/bozo/idee/pensieri.di.oggi # ------------------------------------------echo "percorso = $percorso" t=${percorso##/*/} echo "percorso senza tutti i prefissi = $t" # Stesso risultato con t=basename $percorso , in questo caso particolare. # t=${percorso%/}; t=${t##*/} una soluzione pi generica, #+ ma talvolta potrebbe non funzionare. # Se $percorso termina con un carattere di ritorno a capo, allora #+ basename $percorso fallisce, al contrario dellespressione precedente. # (Grazie, S.C.) t=${percorso%/*.*} # Stesso risultato di t=dirname $percorso echo "percorso a cui stato tolto il suffisso (/pensieri.di.oggi) = $t" # Questi operatori possono non funzionare, come nei casi"../", #+ "/foo////", # "foo/", "/". Togliere i suffissi, specialmente quando
136
${var/#Modello/Sostituto} Se il presso di var vericato da Modello, allora Sostituto rimpiazza Modello. ${var/%Modello/Sostituto} Se il sufsso di var vericato da Modello, allora Sostituto rimpiazza Modello. Esempio 9-20. Verica di occorrenza di pressi o sufssi di stringa
#!/bin/bash # var-match.sh: # Dimostrazione di sostituzione di occorrenza di prefisso/suffisso di stringa. v0=abc1234zip1234abc echo "v0 = $v0" echo # Variabile originale. # abc1234zip1234abc
# Verifica del prefisso (inizio) della stringa. v1=${v0/#abc/ABCDEF} # abc1234zip1234abc # |-| echo "v1 = $v1" # ABCDEF1234zip1234abc # |----| # Verifica del suffisso (fine) della stringa. v2=${v0/%abc/ABCDEF} # abc1234zip123abc # |-| echo "v2 = $v2" # abc1234zip1234ABCDEF
137
${!prefissovar*} ${!prefissovar@} Verica tutte le variabili precedentemente dichiarate i cui nomi iniziano con pressovar.
xyz23=qualsiasi_cosa xyz24= a=${!xyz*} echo "a = $a" a=${!xyz@} echo "a = $a" # Espande i nomi delle variabili dichiarate che iniziano #+ con "xyz". # a = xyz23 xyz24 # Come prima. # a = xyz23 xyz24
opzioni declare/typeset
-r readonly (sola lettura)
declare -r var1
138
Capitolo 9. Variabili riviste (declare -r var1 uguale a readonly var1) approssimativamente equivalente al qualicatore di tipo const del C. Il tentativo di modicare il valore di una variabile in sola lettura fallisce generando un messaggio derrore.
-i intero
declare -i numero # Lo script tratter le successive occorrenze di "numero" come un intero. numero=3 echo "numero = $numero"
# Numero = 3
numero=tre echo "Numero = $numero" # numero = 0 # Cerca di valutare la stringa "tre" come se fosse un intero.
Sono consentire alcune operazioni aritmetiche sulle variabili dichiarate interi senza la necessit di usare expr o let.
# n = 6/3
# n = 2
-a array
declare -a indici
-f funzioni
declare -f
In uno script, una riga con declare -f senza alcun argomento, elenca tutte le funzioni precedentemente denite in quello script.
139
-x export
declare -x var3
-x var=$valore
declare -x var3=373
Il comando declare consente di assegnare un valore alla variabile mentre viene dichiarata, impostando cos anche le sue propriet.
140
declare -r var2=13.36
# declare consente di impostare la propriet #+ della variabile e contemporaneamente #+ assegnarle un valore. echo "var2 dichiarata come $var2" # Tentativo di modificare una variabile in sola #+ lettura. var2=13.37 # Provoca un messaggio derrore e luscita dallo #+ script. echo "var2 ancora $var2" exit 0 # Questa riga non verr eseguita. # Lo script non esce in questo punto.
Cautela
Luso del builtin declare restringere lambito di una variabile.
foo () { FOO="bar" } bar () { foo echo $FOO } bar # Visualizza bar.
Tuttavia . . .
foo (){ declare FOO="bar" } bar () { foo echo $FOO } bar # Non visualizza niente.
141
Ipotizziamo che il valore di una variabile sia il nome di una seconda variabile. in qualche modo possibile recuperare il valore di questa seconda variabile dalla prima? Per esempio, se a=lettera_alfabeto e lettera_alfabeto=z, pu una referenziazione ad a restituire z? In effetti questo possibile e prende il nome di referenziazione indiretta. Viene utilizzata linsolita notazione eval var1=\$$var2. Esempio 9-22. Referenziazioni indirette
#!/bin/bash # ind-ref.sh: Referenziazione indiretta a variabile. # Accedere al contenuto del contenuto di una variabile. a=lettera_alfabeto lettera_alfabeto=z echo # Referenziazione diretta. echo "a = $a" # a = lettera_alfabeto # Referenziazione indiretta. eval a=\$$a echo "Ora a = $a" # Ora a = z echo # La variabile "a" contiene il nome di unaltra variabile.
# Proviamo a modificare la referenziazione di secondo-ordine. t=tabella_cella_3 tabella_cella_3=24 echo "\"tabella_cella_3\" = $tabella_cella_3" # "tabella_cella_3" = 24 echo -n "\"t\" dereferenziata = "; eval echo \$$t # "t" dereferenziata = 24 # In questo semplice caso, funziona anche quello che segue (perch?). # eval t=\$$t; echo "\"t\" = $t" echo t=tabella_cella_3 NUOVO_VAL=387 tabella_cella_3=$NUOVO_VAL echo "Valore di \"tabella_cella_3\" modificato in $NUOVO_VAL." echo "\"tabella_cella_3\" ora $tabella_cella_3" echo -n "\"t\" dereferenziata "; eval echo \$$t # "eval" ha due argomenti "echo" e "\$$t" (impostata a $tabella_cella_3) echo
142
# Un altro metodo quello della notazione ${!t}, trattato nella #+ sezione "Bash, versione 2". Vedi anche ex78.sh. exit 0
Qual lutilit pratica della referenziazione indiretta delle variabili? Fornire a Bash un po delle funzionalit dei puntatori del C, ad esempio, nella ricerca nelle tabelle. Nonch avere qualche altra inreressantissima applicazione. . . . Nils Radtke mostra come realizzare nomi di variabili dinamici e valutarne il contenuto. Questo pu risultare utile quando occorre includere dei le di congurazione con source.
#!/bin/bash
# --------------------------------------------------------------------# Questo file pu essere "caricato" da un altro file tramite "source". isdnMioProviderReteRemota=172.16.0.100 isdnTuoProviderReteRemota=10.0.0.10 isdnServizioOnline="MioProvider" # ---------------------------------------------------------------------
# 172.16.0.100
# Considerate il frammento seguente dove viene inizializzata una #+ variabile di nome getSparc, ma manca getIa64: verMirrorArch () { arch="$1"; if [ "$(eval "echo \${$(echo get$(echo -ne $arch | sed s/^\(.\).*/\1/g | tr a-z A-Z; echo $arch | sed s/^.\(.*\)/\1/g)):-falso}")" = vero ] then return 0; else return 1; fi; }
143
getSparc="vero" unset getIa64 verMirrorArch sparc echo $? # 0 # Vero verMirrorArch Ia64 echo $? # 1 # Falso # Note: # ---# Anche la parte del nome della variabile da-sostituire viene costruita #+ esplicitamente. # I parametri passati a verMirrorArch sono in lettere minuscole. # Il nome della variabile formato da due parti: "get" e "Sparc" . . .
awk .....
144
Cautela
Questo metodo un po complicato. Se la seconda variabile modica il proprio valore, allora la prima deve essere correttamente dereferenziata (come nellesempio precedente). Fortunatamente, la notazione ${!variabile}, introdotta con la versione 2 di Bash (vedi Esempio 34-2), rende la referenziazione indiretta pi intuitiva.
Bash non supporta laritmetica dei puntatori e ci limita drasticamente lutilit della referenziazione indiretta. Questa, infatti, in un linguaggio di scripting, solo un brutto espediente.
nellintervallo 0 - 32767. Non dovrebbe essere utilizzata per generare una chiave di cifratura. Esempio 9-24. Generare numeri casuali
#!/bin/bash # $RANDOM restituisce un intero casuale diverso ad ogni chiamata. # Intervallo nominale: 0 - 32767 (intero con segno di 16-bit). NUM_MASSIMO=10 contatore=1 echo echo "$NUM_MASSIMO numeri casuali:" echo "-----------------" while [ "$contatore" -le $NUM_MASSIMO ]
Genera 10 ($NUM_MASSIMO)
145
---
$numero"
# Se necessario un intero casuale non inferiore a un certo limite, #+ occorre impostare una verifica per eliminare tutti i numeri al di #+ sotto di tale limite. LIMITE_INFERIORE=200 numero=0 # inizializzazione while [ "$numero" -le $LIMITE_INFERIORE ] do numero=$RANDOM done echo "Numero casuale maggiore di $LIMITE_INFERIORE --echo # Prendiamo in considerazione una semplice alternativa al ciclo precedente, #+ in particolare # let "numero = $RANDOM + $LIMITE_INFERIORE" # Elimineremmo il ciclo while e lesecuzione sarebbe pi veloce. # Ma ci sarebbe un problema. Quale?
$numero"
# Combiniamo le due tecniche precedenti per ottenere un #+ numero compreso tra due limiti. numero=0 # inizializzazione while [ "$numero" -le $LIMITE_INFERIORE ] do
146
# Genera una scelta binaria, vale a dire, il valore "vero" o "falso". BINARIO=2 T=1 numero=$RANDOM let "numero %= $BINARIO" # Da notare che let "numero >>= 14" d una migliore distribuzione casuale #+ (lo scorrimento a destra elimina tutto tranne lultima cifra binaria). if [ "$numero" -eq $T ] then echo "VERO" else echo "FALSO" fi echo
# Si pu simulare il lancio dei dadi. MODULO=6 # Modulo 6 per un intervallo 0 - 5. # Aumentandolo di 1 si ottiene il desiderato intervallo 1 - 6. # Grazie a Paulo Marcel Coelho Aragao per la semplificazione. dado1=0 dado2=0 # Sarebbe stato meglio impostare semplicemente MODULO=7 e non aggiungere 1? # Perch o perch no? # Si lancia ciascun dado separatamente in modo da ottenere la corretta #+ probabilit. let "dado1 = $RANDOM % $MODULO +1" # Lancio del primo dado. let "dado2 = $RANDOM % $MODULO +1" # Lancio del secondo dado. # Quale, tra le precedenti operazioni aritmetiche, ha la precedenza -#+ modulo (%) o addizione (+)?
let "punteggio = $dado1 + $dado2" echo "Lancio dei dadi = $punteggio" echo
exit 0
147
Capitolo 9. Variabili riviste Esempio 9-25. Scegliere una carta a caso dal mazzo
#!/bin/bash # pick-card.sh # Esempio di scelta a caso di elementi di un array.
# Sceglie una carta, una qualsiasi. Semi="Fiori Quadri Cuori Picche" Denominazioni="2 3 4 5 6 7 8 9 10 Fante Donna Re Asso" # Notate le variabili elencate su pi righe.
seme=($Semi) # Inizializza larray. denominazione=($Denominazioni) num_semi=${#seme[*]} # Conta gli elementi dellarray. num_denominazioni=${#denominazione[*]} echo -n "${denominazione[$((RANDOM%num_denominazioni))]} di " echo ${seme[$((RANDOM%num_semi))]}
Jipe ha evidenziato una serie di tecniche per generare numeri casuali in un intervallo dato.
# Generare un numero casuale compreso tra 6 e 30.
148
Bill Gradwohl ha elaborato una formula pi perfezionata che funziona con i numeri positivi.
numeroc=$(((RANDOM%(max-min+divisibilePer))/divisibilePer*divisibilePer+min))
Qui Bill presenta una versatile funzione che restituisce un numero casuale compreso tra due valori specicati. Esempio 9-26. Numero casuale in un intervallo dato
#!/bin/bash # random-between.sh # Numero casuale compreso tra due valori specificati. # Script di Bill Gradwohl, con modifiche di secondaria importanza fatte #+ dallautore del libro. # Utilizzato con il permesso dellautore.
interCasuale() { # Genera un numero casuale positivo o negativo #+ compreso tra $min e $max #+ e divisibile per $divisibilePer. # Restituisce una distribuzione di valori "ragionevolmente casuale". # # Bill Gradwohl - 1 Ott, 2003 sintassi() # Funzione echo echo echo echo echo echo echo echo echo { allinterno di una funzione. "Sintassi: interCasuale [min] [max] [multiplo]" "Si aspetta che vengano passati fino a 3 parametri," "tutti per opzionali." "min il valore minimo" "max il valore massimo" "multiplo specifica che il numero generato deve essere un" "multiplo di questo valore."
149
"Se si omette qualche valore, vengono usati" "quelli preimpostati: 0 32767 1" "Lesecuzione senza errori restituisce 0, altrimenti viene" "richiamata la funzione sintassi e restituito 1." "Il numero generato viene restituito nella variabile globale" "interCasualeNum" "Valori negativi passati come parametri vengono gestiti" "anchessi correttamente."
150
# E da notare che per ottenere una corretta distribuzione dei valori #+ estremi, si deve agire su un intervallo che va da 0 a #+ abs(max-min)+divisibilePer, non semplicemente abs(max-min)+1. # Il leggero incremento produrr la giusta distribuzione per i #+ valori limite. # #+ #+ #+ #+ # Se si cambia la formula e si usa abs(max-min)+1 si otterranno ancora dei risultati corretti, ma la loro casualit sar falsata dal fatto che il numero di volte in cui verranno restituiti gli estremi ($min e $max) sar considerevolmente inferiore a quella ottenuta usando la formula corretta. -----------------------------------------------------------------------
intervallo=$((max-min)) [ ${intervallo} -lt 0 ] && intervallo=$((0-intervallo)) let intervallo+=divisibilePer interCasualeNum=$(((RANDOM%intervallo)/divisibilePer*divisibilePer+min)) return 0 # #+ #+ # # # } # Verifichiamo la funzione. min=-14 max=20 divisibilePer=3 Tuttavia, Paulo Marcel Coelho Aragao sottolinea che quando $max e $min non sono divisibili per $divisibilePer, la formula sbaglia. Suggerisce invece la seguente: numeroc = $(((RANDOM%(max-min+1)+min)/divisibilePer*divisibilePer))
# Genera un array e controlla che si sia ottenuto almeno uno dei risultati #+ possibili, se si effettua un numero sufficiente di tentativi. declare -a risultati minimo=${min} massimo=${max} if [ $((minimo/divisibilePer*divisibilePer)) -ne ${minimo} ]; then if [ ${minimo} -lt 0 ]; then minimo=$((minimo/divisibilePer*divisibilePer))
151
# Se max non esattamente divisibile per $divisibilePer, #+ viene ricalcolato. if [ $((massimo/divisibilePer*divisibilePer)) -ne ${massimo} ]; then if [ ${massimo} -lt 0 ]; then massimo=$((((massimo/divisibilePer)-1)*divisibilePer)) else massimo=$((massimo/divisibilePer*divisibilePer)) fi fi
# Poich gli indici degli array possono avere solo valori positivi, #+ necessario uno spiazzamento che garantisca il raggiungimento #+ di questo risultato. spiazzamento=$((0-minimo)) for ((i=${minimo}; i<=${massimo}; i+=divisibilePer)); do risultati[i+spiazzamento]=0 done
# Ora si esegue per un elevato numero di volte, per vedere cosa si ottiene. nr_volte=1000 # Lautore dello script suggeriva 100000, #+ ma sarebbe occorso veramente molto tempo. for ((i=0; i<${nr_volte}; ++i)); do # Notate che qui min e max sono specificate in ordine inverso #+ per vedere, in questo caso, il corretto comportamento della funzione. interCasuale ${max} ${min} ${divisibilePer} # Riporta un errore se si verifica un risultato inatteso. [ ${interCasualeNum} -lt ${min} -o ${interCasualeNum} -gt ${max} ] \ && echo errore MIN o MAX - ${interCasualeNum}! [ $((interCasualeNum%${divisibilePer})) -ne 0 ] \ && echo DIVISIBILE PER errore - ${interCasualeNum}! # Registra i risultati statisticamente. risultati[interCasualeNum+spiazzamento]=\ $((risultati[interCasualeNum+spiazzamento]+1)) done
152
for ((i=${minimo}; i<=${massimo}; i+=divisibilePer)); do [ ${risultati[i+spiazzamento]} -eq 0 ] && echo "Nessun risultato per $i." \ || echo "${i} generato ${risultati[i+spiazzamento]} volte." done
exit 0
Ma, quant casuale $RANDOM? Il modo migliore per vericarlo scrivere uno script che mostri la distribuzione dei numeri casuali generati da $RANDOM. Si lancia alcune volte un dado con $RANDOM. . . Esempio 9-27. Lanciare un dado con RANDOM
#!/bin/bash # Quant casuale RANDOM? RANDOM=$$ # Cambia il seme del generatore di numeri #+ casuali usando lID di processo dello script.
FACCE=6 # Un dado ha 6 facce. NUMMAX_LANCI=600 # Aumentatelo se non avete nientaltro di meglio da fare. lanci=0 # Contatore dei lanci. tot_uno=0 tot_due=0 tot_tre=0 tot_quattro=0 tot_cinque=0 tot_sei=0 # I contatori devono essere inizializzati a 0 perch #+ una variabile non inizializzata ha valore nullo, non zero.
visualizza_risultati () { echo echo "totale degli uno = $tot_uno" echo "totale dei due = $tot_due" echo "totale dei tre = $tot_tre" echo "totale dei quattro = $tot_quattro" echo "totale dei cinque = $tot_cinque" echo "totale dei sei = $tot_sei" echo } aggiorna_contatori() { case "$1" in 0) let "tot_uno += 1";; 1) 2) 3) 4) let let let let
153
while [ "$lanci" -lt "$NUMMAX_LANCI" ] do let "dado1 = RANDOM % $FACCE" aggiorna_contatori $dado1 let "lanci += 1" done visualizza_risultati exit 0 # #+ # #+ # # #+ I punteggi dovrebbero essere distribuiti abbastanza equamente, nellipotesi che RANDOM sia veramente casuale. Con $NUMMAX_LANCI impostata a 600, la frequenza di ognuno dei sei numeri dovrebbe aggirarsi attorno a 100, pi o meno 20 circa. Ricordate che RANDOM un generatore pseudocasuale, e neanche particolarmente valido.
# La casualit un argomento esteso e complesso. # Sequenze "casuali" sufficientemente lunghe possono mostrare #+ un andamento caotico e "non-casuale". # # # # Esercizio (facile): -----------------Riscrivete lo script per simulare il lancio di una moneta 1000 volte. Le possibilit sono "TESTA" o "CROCE".
Come si visto nellultimo esempio, meglio ricalcolare il seme del generatore RANDOM ogni volta che viene invocato. Utilizzando lo stesso seme, RANDOM ripete le stesse serie di numeri. 6 (Rispecchiando il comportamento della funzione random() del C.) Esempio 9-28. Cambiare il seme di RANDOM
#!/bin/bash # seeding-random.sh: Cambiare il seme della variabile RANDOM. MAX_NUMERI=25 # Quantit di numeri che devono essere generati.
154
echo; echo RANDOM=2 numeri_casuali echo; echo # RANDOM=$$ imposta il seme di RANDOM allid di processo dello script. # anche possibile usare come seme di RANDOM i comandi time o date. # Ancora pi elegante... SEME=$(head -1 /dev/urandom | od -N 1 | awk { print $2 }) # Output pseudocasuale prelevato da /dev/urandom (file di #+ dispositivo di sistema pseudo-casuale), quindi convertito #+ con "od" in una riga di numeri (ottali) visualizzabili, #+ infine "awk" ne recupera solamente uno per SEME. RANDOM=$SEME numeri_casuali echo; echo exit 0 # Altro tentativo, ma con seme diverso... # viene generata una serie differente.
Nota: Il le di dispositivo /dev/urandom fornisce un metodo per generare numeri pseudocasuali molto pi casuali che non la variabile $RANDOM. dd if=/dev/urandom of=nomefile bs=1 count=XX crea un le di numeri casuali ben distribuiti . Tuttavia, per assegnarli ad una variabile in uno script necessario un espediente, come ltrarli attraverso od (come nellesempio precedente e Esempio 12-13) o utilizzare dd (vedi Esempio 12-55) o anche collegandoli con una pipe a md5sum (vedi Esempio 33-14). Esistono altri modi per generare numeri pseudocasuali in uno script. Awk ne fornisce uno molto comodo.
155
echo -n "Numeri casuali tra 0 e 1 = " echo | awk "$SCRIPTAWK" # Cosa succede se si omette echo? exit 0
# Esercizi: # --------# 1) Usando un ciclo, visualizzare 10 differenti numeri casuali. # (Suggerimento: bisogna ricalcolare un diverso seme per la funzione #+ "srand()" ad ogni passo del ciclo. Cosa succede se non viene fatto?) # 2) Usando come fattore di scala un multiplo intero, generare numeri #+ casuali nellintervallo tra 10 e 100. # 3) Come il precedente esercizio nr.2, ma senza intervallo.
echo (( a = 23 )) # Impostazione, in stile C, con gli spazi da entrambi i lati #+ dell "=". echo "a (valore iniziale) = $a"
156
# Grazie, Jeroen Domburg. ########################################################################### echo (( t = a<45?7:11 )) # Operatore ternario del C. echo "Se a < 45, allora t = 7, altrimenti t = 11." echo "t = $t " # Si! echo
# --------------------# Attenzione, sorpresa! # --------------------# Evidentemente Chet Ramey ha contrabbandato un mucchio di costrutti in #+ stile C, non documentati, in Bash (in realt adattati da ksh, in #+ quantit notevole). # Nella documentazione Bash, Ramey chiama ((...)) matematica di shell, #+ ma ci va ben oltre laritmetica. # Mi spiace, Chet, ora il segreto svelato. # Vedi anche luso del costrutto ((...)) nei cicli "for" e "while". # Questo costrutto funziona solo nella versione exit 0 2.04 e successive, di Bash.
157
Note
1. Naturalmente, il PID dello script in esecuzione $$. 2. I termini argomento e parametro vengono spesso usati per indicare la stessa cosa. In questo libro hanno lo stesso, identico signicato: quello di una variabile passata ad uno script o ad una funzione. 3. Questo vale sia per gli argomenti da riga di comando che per i parametri passati ad una funzione. 4. Se $parametro nullo, in uno script non interattivo, questultimo viene terminato con exit status 127 (il codice di errore Bash di command not found). 5. La reale casualit, per quanto esista veramente, la si pu trovare solo in alcuni fenomeni naturali ancora non completamente compresi come il decadimento radioattivo. I computer possono solamente simularla, di conseguenza ci si riferisce alle sequenze casuali da essi generate col termine di numeri pseudocasuali. 6. Il seme di una serie di numeri pseudocasuali genarata dal computer pu essere considerata unetichetta identicativa. Per esempio, si pensi a una serie pseudocasuale con seme 23 come serie #23.
Una propriet della serie di numeri pseudocasuali rappresentata dallampiezza del ciclo prima che la serie incominci a ripetersi. Un buon generatore di numeri pseudocasuali produce serie con cicli molto grandi.
158
10.1. Cicli
Un ciclo un blocco di codice che itera (ripete) un certo numero di comandi nch la condizione di controllo del ciclo vera.
cicli for
for arg in [lista] il costrutto di ciclo fondamentale. Differisce signicativamente dal suo analogo del linguaggio C.
Nota: Ad ogni passo del ciclo, arg assume il valore di ognuna delle successive variabili elencate in lista.
for arg in "$var1" # Al 1mo passo del # Al 2do passo del # Al 3zo passo del # ... # Al passo Nmo del
"$var2" "$var3" ... "$varN" ciclo, arg = $var1 ciclo, arg = $var2 ciclo, arg = $var3 ciclo, arg = $varN
# Bisogna applicare il "quoting" agli argomenti di [lista] per #+ evitare una possibile suddivisione delle parole.
Gli argomenti elencati in lista possono contenere i caratteri jolly. Se do si trova sulla stessa riga di for, necessario usare il punto e virgola dopo lista.
159
Nota: Ogni elemento in [lista] pu contenere pi parametri. Ci torna utile quando questi devono essere elaborati in gruppi. In tali casi, si deve usare il comando set (vedi Esempio 11-15) per forzare la verica di ciascun elemento in [lista] e per assegnare ad ogni componente i rispettivi parametri posizionali.
Esempio 10-2. Ciclo for con due parametri in ogni elemento [lista]
#!/bin/bash # Pianeti rivisitati. # Associa il nome di ogni pianeta con la sua distanza dal sole. for pianeta in "Mercurio 36" "Venere 67" "Terra 93" "Marte 142" "Giove 483" do set -- $pianeta # Verifica la variabile "pianeta" e imposta i parametri #+ posizionali. # i "--" evitano sgradevoli sorprese nel caso $pianeta sia nulla #+ o inizi con un trattino. # Potrebbe essere necessario salvare i parametri posizionali #+ originari, perch vengono sovrascritti. # Un modo per farlo usare un array, # param_origin=("$@") echo "$1 $2,000,000 miglia dal sole"
160
##-------due done
In un ciclo for, una variabile pu sostituire [lista]. Esempio 10-3. Fileinfo: operare su un elenco di le contenuto in una variabile
#!/bin/bash # fileinfo.sh FILE="/usr/sbin/accept /usr/sbin/pwck /usr/sbin/chroot /usr/bin/fakefile /sbin/badblocks /sbin/ypbind" # Elenco dei file sui quali volete informazioni. # Compreso linesistente file /usr/bin/fakefile. echo for file in $FILE do if [ ! -e "$file" ] # Verifica se il file esiste. then echo "$file non esiste."; echo continue # Verifica il successivo. fi ls -l $file | awk { print $9 " # Visualizza 2 campi. dimensione file: " $5 }
whatis basename $file # Informazioni sul file. # Fate attenzione che, affinch questo script funzioni correttamente, #+ bisogna aver impostato il database whatis. # Per farlo, da root, eseguite /usr/bin/makewhatis. echo done exit 0
In un ciclo for possibile il globbing se in [lista] sono presenti i caratteri jolly (* e ?), che vengono usati per lespansione dei nomi dei le.
161
Capitolo 10. Cicli ed alternative Esempio 10-4. Agire sui le con un ciclo for
#!/bin/bash # list-glob.sh: Generare [lista] in un ciclo for usando il "globbing". echo for file in * # ^ Bash esegue lespansione del nome del file #+ nelle espressioni che riconoscono il globbing. do ls -l "$file" # Elenca tutti i file in $PWD (directory corrente). # Ricordate che il carattere jolly "*" verifica tutti i file, #+ tuttavia, il "globbing" non verifica i file i cui nomi iniziano #+ con un punto. # Se il modello non verifica nessun file, allora si autoespande. # Per evitarlo impostate lopzione nullglob #+ (shopt -s nullglob). # Grazie, S.C. done echo; echo for file in [jx]* do rm -f $file # Cancella solo i file i cui nomi iniziano con #+ "j" o "x" presenti in $PWD. echo "Rimosso il file \"$file\"". done echo exit 0
Omettere in [lista] in un ciclo for fa s che il ciclo agisca su $@ -- lelenco degli argomenti forniti allo script da riga di comando. Una dimostrazione particolarmente intelligente di ci illustrata in Esempio A-16. Esempio 10-5. Tralasciare in [lista] in un ciclo for
#!/bin/bash # Invocate lo script sia con che senza argomenti e osservate cosa succede. for a do echo -n "$a " done # Manca in lista, quindi il ciclo opera su $@ #+ (elenco degli argomenti da riga di comando, compresi gli spazi).
162
echo exit 0
In un possibile impiegare la sostituzione di comando per generare [lista]. Vedi anche Esempio 12-49, Esempio 10-10 ed Esempio 12-43. Esempio 10-6. Generare [lista] in un ciclo for con la sostituzione di comando
#!/bin/bash # for-loopcmd.sh: un ciclo for con [lista] #+ prodotta dalla sostituzione di comando. NUMERI="9 7 3 8 37.53" for numero in echo $NUMERI do echo -n "$numero " done echo exit 0 # for numero in 9 7 3 8 37.53
Ecco un esempio un po pi complesso dellutilizzo della sostituzione di comando per creare [lista]. Esempio 10-7. Unalternativa con grep per i le binari
#!/bin/bash # bin-grep.sh:
# Unalternativa con "grep" per file binari. # Effetto simile a "grep -a" E_ERR_ARG=65 E_NOFILE=66 if [ $# -ne 2 ] then echo "Utilizzo: basename $0 stringa_di_ricerca nomefile" exit $E_ERR_ARG fi if [ ! -f "$2" ] then echo "Il file \"$2\" non esiste." exit $E_NOFILE fi
IFS="\n"
163
per esercitarvi
Sempre sullo stesso tema. Esempio 10-8. Elencare tutti gli utenti del sistema
#!/bin/bash # userlist.sh FILE_PASSWORD=/etc/passwd n=1 # Numero utente for nome in $(awk BEGIN{FS=":"}{print $1} < "$FILE_PASSWORD" ) # Separatore di campo = :^^^^^^ # Visualizza il primo campo ^^^^^^^^ # Ottiene linput dal file delle password ^^^^^^^^^^^^^^^^^ do echo "UTENTE nr.$n = $nome" let "n += 1" done
# # # # #
exit 0 # # # #+ # Esercizio: --------Com che un utente ordinario (o uno script eseguito dallo stesso) riesce a leggere /etc/passwd? Non si tratta di una falla per la sicurezza? Perch o perch no?
164
Capitolo 10. Cicli ed alternative Esempio nale di [lista] risultante dalla sostituzione di comando. Esempio 10-9. Vericare tutti i le binari di una directory in cerca degli autori
#!/bin/bash # findstring.sh: # Cerca una stringa particolare nei binari di una directory specificata. directory=/usr/bin/ stringa="Free Software Foundation"
for file in $( find $directory -type f -name * | sort ) do strings -f $file | grep "$stringa" | sed -e "s%$directory%%" # Nellespressione "sed", necessario sostituire il normale #+ delimitatore "/" perch si d il caso che "/" sia uno dei #+ caratteri che deve essere filtrato. done exit 0 # # # #+ Esercizio (facile): -----------------Modificate lo script in modo tale che accetti come parametri da riga di comando $directory e $stringa.
Loutput di un ciclo for pu essere collegato con una pipe ad un comando o ad una serie di comandi. Esempio 10-10. Elencare i link simbolici presenti in una directory
#!/bin/bash # symlinks.sh: Elenca i link simbolici presenti in una directory.
directory=${1-pwd} # Imposta come predefinita la directory di lavoro corrente, nel caso non ne #+ venga specificata alcuna. # Corrisponde al seguente blocco di codice. # ------------------------------------------------------------------# ARG=1 # Si aspetta un argomento da riga di comando. # # if [ $# -ne "$ARG" ] # Se non c 1 argomento... # then # directory=pwd # directory di lavoro corrente # else # directory=$1 # fi # ------------------------------------------------------------------echo "Link simbolici nella directory \"$directory\"" for file in "$( find $directory -type l )" # -type l = link simbolici
165
exit 0
# Jean Helou propone la seguente alternativa: echo "Link simbolici nella directory \"$directory\"" # Salva lIFS corrente. Non si mai troppo prudenti. VECCHIOIFS=$IFS IFS=: for file in $(find $directory -type l -printf "%p$IFS") do # ^^^^^^^^^^^^^^^^ echo "$file" done|sort
Lo stdout di un ciclo pu essere rediretto in un le, come dimostra la piccola modica apportata allesempio precedente. Esempio 10-11. Link simbolici presenti in una directory salvati in un le
#!/bin/bash # symlinks.sh: Elenca i link simbolici presenti in una directory. OUTFILE=symlinks.list # file di memorizzazione
directory=${1-pwd} # Imposta come predefinita la directory di lavoro corrente, nel caso non #+ ne venga specificata alcuna.
echo "Link simbolici nella directory \"$directory\"" > "$OUTFILE" echo "---------------------------" >> "$OUTFILE" for file in "$( find $directory -type l )" do echo "$file" done | sort >> "$OUTFILE" # ^^^^^^^^^^^^^ # -type l = link simbolici
166
Vi una sintassi alternativa per il ciclo for che risulta molto familiare ai programmatori in linguaggio C. Si basa sulluso del costrutto doppie parentesi. Esempio 10-12. Un ciclo for in stile C
#!/bin/bash # Due modi per contare fino a 10. echo # Sintassi standard. for a in 1 2 3 4 5 6 7 8 9 10 do echo -n "$a " done echo; echo # +===================================================================+ # Ora facciamo la stessa cosa usando la sintassi in stile C. LIMITE=10 for ((a=1; a <= LIMITE; a++)) do echo -n "$a " done echo; echo # +===================================================================+ # Uso dell "operatore virgola" del C per incrementare due variabili #+ contemporaneamente. for ((a=1, b=1; a <= LIMITE; a++, b++)) do echo -n "$a-$b " done echo; echo exit 0 # La virgola concatena le operazioni. # Doppie parentesi, e "LIMITE" senza "$".
167
Capitolo 10. Cicli ed alternative --Adesso un ciclo for impiegato in unapplicazione pratica. Esempio 10-13. Utilizzare efax in modalit batch
#!/bin/bash # Inviare un fax (dovete avere un fax installato) ARG_ATTESI=2 E_ERR_ARG=65 if [ $# -ne $ARG_ATTESI ] # Verifica il corretto numero di argomenti. then echo "Utilizzo: basename $0 nr_telefono file_testo" exit $E_ERR_ARG fi
if [ ! -f "$2" ] then echo "Il file $2 non un file di testo" exit $E_ERR_ARG fi
# Crea file fax formattati dai file di testo. # Concatena i file appena creati. # Usa il carattere jolly in lista.
# Esegue il lavoro.
# Come ha sottolineato S.C. il ciclo for potrebbe essere sostituito con # efax -d /dev/ttyS3 -o1 -t "T$1" $2.0* # ma non sarebbe stato altrettanto istruttivo [sorriso]. exit 0
while Questo costrutto verica una condizione data allinizio del ciclo che viene mantenuto in esecuzione nch quella condizione rimane vera (restituisce exit status 0). A differenza del ciclo for, il ciclo while viene usato in quelle situazioni in cui il numero delle iterazioni non conosciuto in anticipo.
168
Come nel caso dei cicli for, collocare il do sulla stessa riga della condizione di verica rende necessario luso del punto e virgola.
while [condizione] ; do
da notare che alcuni cicli while specializzati, come per esempio il costrutto getopts, si discostano un po dalla struttura standard appena illustrata. Esempio 10-14. Un semplice ciclo while
#!/bin/bash var0=0 LIMITE=10 while [ "$var0" -lt "$LIMITE" ] do echo -n "$var0 " # -n sopprime il ritorno a capo. # ^ Lo spazio serve a separare i numeri visualizzati. var0=expr $var0 + 1 # var0=$(($var0+1)) anche questa forma va bene. # var0=$((var0 + 1)) anche questa forma va bene. # let "var0 += 1" anche questa forma va bene. done # Anche vari altri metodi funzionano. echo exit 0
(fine per terminare) " # Non read $var1 (perch?). # necessario il "quoting"
169
Un ciclo while pu avere diverse condizioni. Ma solamente quella nale che stabilisce quando il ciclo deve terminare. Per questo scopo, per, necessaria una sintassi leggermente differente. Esempio 10-16. Ciclo while con condizioni multiple
#!/bin/bash var1=nonimpostata precedente=$var1 while echo "variabile-precedente = $precedente" echo precedente=$var1 [ "$var1" != fine ] # Tiene traccia del precedente valore di $var1. # "while" con quattro condizioni, ma solo lultima che controlla #+ il ciclo. # l*ultimo* exit status quello che conta. do echo "Immetti la variable nr.1 (fine per terminare) " read var1 echo "variabile nr.1 = $var1" done # Cercate di capire come tutto questo funziona. # un tantino complicato. exit 0
Come per il ciclo for, anche per un ciclo while si pu impiegare una sintassi in stile C usando il costrutto doppie parentesi (vedi anche Esempio 9-30). Esempio 10-17. Sintassi in stile C di un ciclo while
#!/bin/bash # wh-loopc.sh: Contare fino a 10 con un ciclo "while". LIMITE=10 a=1 while [ "$a" -le $LIMITE ] do echo -n "$a "
170
Nota: Un ciclo while pu avere il proprio stdin rediretto da un le tramite il < alla ne del blocco. In un ciclo while il relativo stdin pu essere fornito da una pipe.
until Questo costrutto verica una condizione data allinizio del ciclo che viene mantenuto in esecuzione nch quella condizione rimane falsa (il contrario del ciclo while).
171
Capitolo 10. Cicli ed alternative Notate che un ciclo until verica la condizione allinizio del ciclo, differendo, in questo, da analoghi costrutti di alcuni linguaggi di programmazione. Come nel caso dei cicli for, collocare il do sulla stessa riga della condizione di verica rende necessario luso del punto e virgola.
until [condizione-falsa] ; do
172
Vedi Esempio 26-11 per unillustrazione di cicli while annidati e Esempio 26-13 per un ciclo while annidato in un ciclo until.
echo echo "Visualizza i numeri da 1 fino a 20 (saltando 3 e 11)." a=0 while [ $a -le "$LIMITE" ]
173
Il comando break pu avere un parametro. Il semplice break conclude il ciclo in cui il comando di trova, mentre un break N interrompe il ciclo al livello N . Esempio 10-21. Interrompere un ciclo ad un determinato livello
#!/bin/bash # break-levels.sh: Interruzione di cicli. # "break N" interrompe i cicli al livello N. for cicloesterno in 1 2 3 4 5
174
#--------------------------------for ciclointerno in 1 2 3 4 5 do echo -n "$ciclointerno " if [ "$ciclointerno" -eq 3 ] then break # Provate break 2 per vedere il risultato. # ("Interrompe" entrambi i cicli, interno ed esterno). fi done #--------------------------------echo done echo exit 0
Il comando continue, come break, pu avere un parametro. Un semplice continue interrompe lesecuzione delliterazione corrente del ciclo e d inizio alla successiva. continue N salta tutte le restanti iterazioni del ciclo in cui si trova e continua con literazione successiva del ciclo N di livello superiore. Esempio 10-22. Proseguire ad un livello di ciclo superiore
#!/bin/bash # Il comando "continue N", continua allNsimo livello. for esterno in I II III IV V do echo; echo -n "Gruppo $esterno: " # ciclo esterno
# ---------------------------------------------------for interno in 1 2 3 4 5 6 7 8 9 10 # ciclo interno do if [ "$interno" -eq 7 ] then continue 2 # Continua al ciclo di 2 livello, cio il #+ "ciclo esterno". Modificate la riga precedente #+ con un semplice "continue" per vedere il #+ consueto comportamento del ciclo. fi echo -n "$interno " # 7 8 9 10 non verranno mai visualizzati. done # ----------------------------------------------------
175
done echo; echo # Esercizio: # Trovate un valido uso di "continue N" in uno script. exit 0
while true do for n in .iso.* do [ "$n" = ".iso.opts" ] && continue beta=${n#.iso.} [ -r .Iso.$beta ] && continue [ -r .lock.$beta ] && sleep 10 && continue lockfile -r0 .lock.$beta || continue echo -n "$beta: " date run-isotherm $beta date ls -alF .Iso.$beta [ -r .Iso.$beta ] && rm -f .lock.$beta continue 2 done break done # I dettagli, in particolare sleep N, sono specifici per la mia #+ applicazione, ma la struttura generale : while true do for job in {modello} do {job gi terminati o in esecuzione} && continue {marca il job come in esecuzione, lo esegue, lo marca come eseguito} continue 2 done
176
In questo modo lo script si interromper solo quando non ci saranno pi job da eseguire (compresi i job che sono stati aggiunti durante il runtime). Tramite luso di appropriati lockfile pu essere eseguito su diverse macchine concorrenti senza duplicazione di calcoli [che, nel mio caso, occupano un paio dore, quindi veramente il caso di evitarlo]. Inoltre, poich la ricerca ricomincia sempre dallinizio, possibile codificare le priorit nei nomi dei file. Naturalmente, questo si potrebbe fare senza continue 2, ma allora si dovrebbe verificare effettivamente se alcuni job sono stati eseguiti (in questo caso dovremmo cercare immediatamente il job successivo) o meno (in questaltro dovremmo interrompere o sospendere lesecuzione per molto tempo prima di poter verificare un nuovo job).
Cautela
Il costrutto continue N difcile da capire e complicato da usare, in modo signicativo, in qualsiasi contesto. Sarebbe meglio evitarlo.
177
Capitolo 10. Cicli ed alternative case "$variabile" in "$condizione1" ) comando... ;; "$condizione2" ) comando... ;; esac
Nota:
Il "quoting" delle variabili non obbligatorio, dal momento che la suddivisione delle parole
non ha luogo.
Ogni riga di verica termina con una parentesi tonda chiusa ). Ciascun blocco di istruzioni termina con un doppio punto e virgola ;;. Lintero blocco case termina con esac (case scritto al contrario).
# Esercizio: # --------# Cos com, lo script accetta la pressione di un solo tasto, quindi #+ termina. Modificate lo script in modo che accetti un input continuo,
178
read persona case "$persona" in # Notate luso del "quoting" per la variabile. "E" | "e" ) # Accetta sia una lettera maiuscola che minuscola. echo echo "Roland Evans" echo "4321 Floppy Dr." echo "Hardscrabble, CO 80753" echo "(303) 734-9874" echo "(303) 734-9892 fax" echo "[email protected]" echo "Socio daffari & vecchio amico" ;; # Attenzione al doppio punto e virgola che termina ogni opzione. "J" | "j" ) echo echo "Mildred Jones" echo "249 E. 7th St., Apt. 19" echo "New York, NY 10009" echo "(212) 533-2814" echo "(212) 533-9972 fax" echo "[email protected]" echo "Ex fidanzata" echo "Compleanno: Feb. 11" ;;
179
# Aggiungete in seguito le informazioni per Smith & Zane. * ) # Opzione predefinita. # Un input vuoto (tasto INVIO) o diverso dalle scelte #+ proposte, viene verificato qui. echo echo "Non ancora inserito nel database." ;; esac echo # # # #+ Esercizio: --------Modificate lo script in modo che accetti input multipli, invece di terminare dopo aver visualizzato un solo indirizzo.
exit 0
Un uso particolarmente intelligente di case quello per vericare gli argomenti passati da riga di comando.
#!/bin/bash case "$1" in "") echo "Utilizzo: ${0##*/} <nomefile>"; exit $E_ERR_PARAM;; # Nessun parametro da riga di comando, # o primo parametro vuoto. # Notate che ${0##*/} equivale alla sostituzione di parametro #+ ${var##modello}. Cio $0. -*) NOMEFILE=./$1;; # #+ #+ #+ Se il nome del file passato come argomento ($1) inizia con un trattino, lo sostituisce con ./$1 di modo che i comandi successivi non lo interpretino come unopzione.
* ) NOMEFILE=$1;; esac
# Altrimenti, $1.
Ecco un esempio ancor pi chiaro di gestione dei parametri passati da riga di comando:
#! /bin/bash
180
# Dallo script "Log2Rot" di Stefano Falsetto, #+ parte del suo pacchetto "rottlog". # Usato con il consenso dellautore.
exit 0
Un costrutto case pu ltrare le stringhe in una ricerca che fa uso del globbing. Esempio 10-27. Una semplice ricerca di stringa
#!/bin/bash # match-string.sh: semplice ricerca di stringa verifica_stringa () { UGUALE=0 NONUGUALE=90 PARAM=2 # La funzione richiede 2 argomenti. ERR_PARAM=91 [ $# -eq $PARAM ] || return $ERR_PARAM
181
exit 0
case "$1" in [a-zA-Z]*) return $SUCCESSO;; # Inizia con una lettera? * ) return $FALLIMENTO;; esac } # Confrontatelo con la funzione "isalpha ()" del C.
isalpha2 () {
182
verifica_var () # Front-end per isalpha (). { if isalpha "$@" then echo "\"$*\" inizia con un carattere alfabetico." if isalpha2 "$@" then # Non ha significato se il primo carattere non alfabetico. echo "\"$*\" contiene solo lettere." else echo "\"$*\" contiene almeno un carattere non alfabetico." fi else echo "\"$*\" non inizia con una lettera." # Stessa risposta se non viene passato alcun argomento. fi echo } verifica_cifra ()# Front-end per isdigit (). { if isdigit "$@" then echo "\"$*\" contiene solo cifre [0 - 9]." else echo "\"$*\" contiene almeno un carattere diverso da una cifra." fi echo } a=23skidoo
183
# Sostituzione di comando.
verifica_var $a verifica_var $b verifica_var $c verifica_var $d verifica_var $e verifica_var $f verifica_var # Non viene passato nessun argomento, cosa succede? # verifica_cifra $g verifica_cifra $h verifica_cifra $i
exit 0
# Esercizio: # --------# Scrivete la funzione isfloat () che verifichi i numeri in virgola #+ mobile. Suggerimento: la funzione uguale a isdigit (), ma con #+ laggiunta della verifica del punto decimale.
select Il costrutto select, adottato dalla Shell Korn, anchesso uno strumento per creare menu.
Viene visualizzato un prompt allutente afnch immetta una delle scelte presenti nella variabile lista. Si noti che select usa, in modo predenito, il prompt PS3 (#? ). Questo pu essere modicato.
184
Capitolo 10. Cicli ed alternative Esempio 10-29. Creare menu utilizzando select
#!/bin/bash PS3=Scegli il tuo ortaggio preferito: # Imposta la stringa del prompt. echo select verdura in "fagioli" "carote" "patate" "cipolle" "rape" do echo echo "Il tuo ortaggio preferito sono i/le $verdura." echo "Yuck!" echo break # Cosa succederebbe se non ci fosse il "break"? done exit 0
Se viene omesso in lista allora select usa lelenco degli argomenti passati da riga di comando allo script ($@) o alla funzione in cui il costrutto select inserito. Lo si confronti con il comportamento del costrutto for variabile [in lista] con in lista omesso. Esempio 10-30. Creare menu utilizzando select in una funzione
#!/bin/bash PS3=Scegli il tuo ortaggio preferito: echo scelta_di() { select verdura # [in lista] omesso, quindi select usa gli argomenti passati alla funzione. do echo echo "Il tuo ortaggio preferito: $verdura." echo "Yuck!" echo break done } scelta_di fagioli riso carote ravanelli pomodori spinaci # $1 $2 $3 $4 $5 $6
185
Note
1. Sono builtin di shell, mentre altri comandi di ciclo, come while e case, sono parole chiave.
186
187
Quando un comando, o la stessa shell, svolge un certo compito, d origine (spawn) ad un nuovo sottoprocesso. Questa azione si chiama forking. Il nuovo processo il glio, mentre il processo che lha generato il genitore. Mentre il processo glio sta svolgendo il proprio lavoro, il processo genitore resta ancora in esecuzione. Si noti che mentre un processo genitore ottiene lID di processo del processo glio, riuscendo in questo modo a passargli degli argomenti, non vero linverso. Ci pu creare dei problemi che sono subdoli e difcili da individuare. Esempio 11-1. Uno script che genera istanze multiple di s stesso
#!/bin/bash # spawn.sh
PID=$(pidof sh $0) # ID dei processi delle diverse istanze dello script. P_array=( $PID ) # Inseriti in un array (perch?). echo $PID # Visualizza gli ID dei processi genitore e figlio. let "instanze = ${#P_array[*]} - 1" # Conta gli elementi, meno 1. # Perch viene sottratto 1? echo "$instanze instanza(e) dello script in esecuzione." echo "[Premete Ctl-C per terminare.]"; echo
sleep 1 sh $0 exit 0
# Attesa. # Provaci ancora, Sam. # Inutile: lo script non raggiunger mai questo punto. # Perch?
# Dopo aver terminato lesecuzione con Ctl-C, #+ saranno proprio tutte "morte" le istanze generate dallo script? # In caso affermativo, perch? # # # # Nota: ---Fate attenzione a non tenere in esecuzione lo script troppo a lungo. Potrebbe, alla fine, esaurire troppe risorse di sistema.
# Uno script che genera istanze multiple di s stesso #+ rappresenta una tecnica di scripting consigliabile. # Siete daccordo oppure no?
In genere, un builtin Bash eseguito in uno script non genera un sottoprocesso. Al contrario, un ltro o un comando di sistema esterno, solitamente, avvia (fork) un sottoprocesso.
Un builtin pu avere un nome identico a quello di un comando di sistema. In questo caso Bash lo
188
Capitolo 11. Comandi interni e builtin reimplementa internamente. Per esempio, il comando Bash echo non uguale a /bin/echo, sebbene la loro azione sia quasi identica.
#!/bin/bash echo "Questa riga usa il builtin \"echo\"." /bin/echo "Questa riga usa il comando di sistema
/bin/echo."
Una parola chiave un simbolo, un operatore o una parola riservata. Le parole chiave hanno un signicato particolare per la shell e, infatti, rappresentano le componenti strutturali della sua sintassi . Ad esempio for, while, do e ! sono parole chiave. Come un builtin, una parola chiave una componente interna di Bash, ma a differenza di un builtin, non di per se stessa un comando, ma parte di una struttura di comandi pi ampia. 1
I/O
echo visualizza (allo stdout) unespressione o una variabile (vedi Esempio 4-1).
echo Ciao echo $a
echo richiede lopzione -e per visualizzare le sequenze di escape. Vedi Esempio 5-2. Normalmente, ogni comando echo visualizza una nuova riga. Lopzione -n annulla questo comportamento.
Nota: echo pu essere utilizzato per fornire una sequenza di comandi in una pipe.
if echo "$VAR" | grep -q txt # if [[ $VAR = *txt* ]] then echo "$VAR contiene la sottostringa \"txt\"" fi
Nota: Si pu utilizzare echo, in combinazione con la sostituzione di comando, per impostare una variabile. a=echo "CIAO" | tr A-Z a-z Vedi anche Esempio 12-19, Esempio 12-3, Esempio 12-42 ed Esempio 12-43.
Si faccia attenzione che echo comando cancella tutti i ritorni a capo generati dalloutput di comando.
189
Capitolo 11. Comandi interni e builtin La variabile $IFS (internal eld separator), di norma, comprende \n (ritorno a capo) tra i suoi caratteri di spaziatura. Bash, quindi, scinde loutput di comando in corrispondenza dei ritorni a capo. Le parti vengono passate come argomenti a echo. Di conseguenza echo visualizza questi argomenti separati da spazi.
bash$ ls -l /usr/share/apps/kjezz/sounds -rw-r--r-1 root root 1407 Nov -rw-r--r-1 root root 362 Nov
7 7
bash$ echo ls -l /usr/share/apps/kjezz/sounds total 40 -rw-r--r-- 1 root root 716 Nov 7 2000 reflect.au -rw-r--r-- 1 root root 362 Nov 7 2000 se
Quindi, in che modo si pu inserire un "a capo" in una stringa di caratteri da visualizzare?
# Incorporare un a capo? echo "Perch questa stringa non viene \n suddivisa su due righe?" # Non viene divisa. # Proviamo qualcosaltro. echo echo $"Riga di testo contenente un a capo." # Viene visualizzata su due righe distinte (a capo incorporato). # Ma, il prefisso di variabile "$" proprio necessario? echo echo "Questa stringa divisa su due righe." # No, il "$" non necessario. echo echo "---------------" echo echo -n $"Unaltra riga di testo contenente un a capo." # Viene visualizzata su due righe (a capo incorporato). # In questo caso neanche lopzione -n riesce a sopprimere la capo. echo echo echo "---------------" echo echo
190
# Tuttavia, quello che segue non funziona come potremmo aspettarci. # Perch no? Suggerimento: assegnamento a una variabile. stringa1=$"Ancora unaltra riga di testo contenente un a capo (forse)." echo $stringa1 # Ancora unaltra riga di testo contenente un a_capo (forse). # ^ # La_capo diventato uno spazio. # Grazie a Steve Parker per la precisazione.
Nota: Questo comando un builtin di shell e non uguale a /bin/echo, sebbene la sua azione sia simile.
bash$ type -a echo echo is a shell builtin echo is /bin/echo
printf Il comando printf, visualizzazione formattata, rappresenta un miglioramento di echo. una variante meno potente della funzione di libreria printf() del linguaggio C. Anche la sua sintassi un po differente. printf stringa-di-formato... parametro... la versione builtin Bash del comando /bin/printf o /usr/bin/printf. Per una descrizione dettagliata, si veda la pagina di manuale di printf (comando di sistema).
Cautela
Le versioni pi vecchie di Bash potrebbero non supportare printf.
191
printf "\n"
printf "%s %s \n" $Messaggio1 $Messaggio2 echo # ==================================================# # Simulazione della funzione sprintf del C. # Impostare una variabile con una stringa di formato. echo Pi12=$(printf "%1.12f" $PI) echo "Pi con 12 cifre decimali = $Pi12" Msg=printf "%s %s \n" $Messaggio1 $Messaggio2 echo $Msg; echo $Msg # Ora possiamo disporre della funzione sprintf come modulo #+ caricabile per Bash. Questo, per, non portabile. exit 0 # N.d.T. Nella versione originale veniva usato il punto come separatore #+ decimale. Con le impostazioni locali italiane il punto avrebbe #+ impedito il corretto funzionamento di printf.
E_ERR_DIR=65 var=directory_inesistente errore() { printf "$@" >&2 # Organizza i parametri posizionali passati e li invia allo stderr. echo
192
read Legge il valore di una variabile dallo stdin, vale a dire, preleva in modo interattivo linput dalla tastiera. Lopzione -a permette a read di assegnare le variabili di un array (vedi Esempio 26-6). Esempio 11-3. Assegnamento di variabile utilizzando read
#!/bin/bash # "Leggere" variabili. echo -n "Immetti il valore della variabile var1: " # Lopzione -n di echo sopprime il ritorno a capo. read var1 # Notate che non vi nessun $ davanti a var1, perch la variabile #+ in fase di impostazione. echo "var1 = $var1"
echo # Un singolo enunciato read pu impostare pi variabili. echo -n "Immetti i valori delle variabili var2 e var3 (separati da \ uno spazio o da tab): " read var2 var3 echo "var2 = $var2 var3 = $var3" # Se si immette un solo valore, le rimanenti variabili restano non #+ impostate (nulle). exit 0
Se a read non associata una variabile, linput viene assegnato alla variabile dedicata $REPLY. Esempio 11-4. Cosa succede quando read non associato ad una variabile
#!/bin/bash # read-novar.sh echo # -------------------------- # echo -n "Immetti un valore: "
193
Normalmente, immettendo una \ nellinput di read si disabilita il ritorno a capo. Lopzione -r consente di interpretare la \ letteralmente. Esempio 11-5. Input su pi righe per read
#!/bin/bash echo echo "Immettete una stringa che termina con \\, quindi premete <INVIO>." echo "Dopo di che, immettete una seconda stringa e premete ancora <INVIO>." read var1 # La "\" sopprime il ritorno a capo durante la lettura di $var1. # prima riga \ # seconda riga echo "var1 = $var1" # var1 = prima riga seconda riga # Per ciascuna riga che termina con "\", si ottiene un prompt alla riga #+ successiva per continuare ad inserire caratteri in var1. echo; echo echo "Immettete unaltra stringa che termina con \\ , quindi premete <INVIO>." read -r var2 # Lopzione -r fa s che "\" venga interpretata letteralmente. # prima riga \ echo "var2 = $var2" # var2 = prima riga \ # Lintroduzione dei dati termina con il primo <INVIO>.
194
echo exit 0
Il comando read possiede alcune interessanti opzioni che consentono di visualizzare un prompt e persino di leggere i tasti premuti senza il bisogno di premere INVIO.
# Rilevare la pressione di un tasto senza dover premere INVIO. read -s -n1 -p "Premi un tasto " tasto echo; echo "Hai premuto il tasto "\"$tasto\""." # # # #+ Lopzione -s serve a non visualizzare linput. Lopzione -n N indica che devono essere accettati solo N caratteri di input. Lopzione -p permette di visualizzare il messaggio del prompt immediatamente successivo, prima di leggere linput.
# Usare queste opzioni un po complicato, perch #+ devono essere poste nellordine esatto.
Lopzione -n di read consente anche il rilevamento dei tasti freccia ed alcuni altri tasti inusuali. Esempio 11-6. Rilevare i tasti freccia
#!/bin/bash # arrow-detect.sh: Rileva i tasti freccia, e qualcosaltro. # Grazie a Sandro Magi per avermelo mostrato. # -------------------------------------------------------# Codice dei caratteri generati dalla pressione dei tasti. frecciasu=\[A frecciagi=\[B frecciadestra=\[C frecciasinistra=\[D ins=\[2 canc=\[3 # -------------------------------------------------------SUCCESSO=0 ALTRO=65 echo -n "Premi un tasto... " # Potrebbe essere necessario premere anche INVIO se viene premuto un #+ tasto non tra quelli elencati. read -n3 tasto # Legge 3 caratteri. echo -n "$tasto" | grep "$frecciasu" # Verifica il codice del #+ tasto premuto.
195
echo " stato premuto un altro tasto." exit $ALTRO # # # # # # Esercizi: -------1) Semplificate lo script trasformando le verifiche multiple "if" in un costrutto case. 2) Aggiungete il rilevamento dei tasti "Home", "Fine", "PgUp" e "PgDn". N.d.T. Attenzione! I codici dei tasti indicati allinizio potrebbero non
196
Nota: Lopzione -n di read evita il rilevamento del tasto INVIO (nuova riga).
Lopzione -t di read consente un input temporizzato (vedi Esempio 9-4). Il comando read pu anche leggere il valore da assegnare alla variabile da un le rediretto allo stdin. Se il le contiene pi di una riga, solo la prima viene assegnata alla variabile. Se read ha pi di un parametro, allora ad ognuna di queste variabili vengono assegnate le stringhe successive delimitate da spazi. Attenzione! Esempio 11-7. Utilizzare read con la redirezione di le
#!/bin/bash read var1 <file-dati echo "var1 = $var1" # var1 viene impostata con lintera prima riga del file di input "file-dati" read var2 var3 <file-dati echo "var2 = $var2 var3 = $var3" # Notate qui il comportamento poco intuitivo di "read". # 1) Ritorna allinizio del file di input. # 2) Ciascuna variabile viene impostata alla stringa corrispondente, # separata da spazi, piuttosto che allintera riga di testo. # 3) La variabile finale viene impostata alla parte rimanente della riga. # 4) Se ci sono pi variabili da impostare di quante siano le # stringhe separate da spazi nella prima riga del file, allora le # variabili in eccesso restano vuote. echo "------------------------------------------------" # Come risolvere il problema precedente con un ciclo: while read riga do echo "$riga" done <file-dati # Grazie a Heiner Steven per la puntualizzazione. echo "------------------------------------------------" # Uso della variabile $IFS (Internal Field Separator) per suddividere #+ una riga di input per "read", #+ se non si vuole che il delimitatore preimpostato sia la spaziatura. echo "Elenco di tutti gli utenti:" OIFS=$IFS; IFS=: # /etc/passwd usa ":" come separatore di campo.
197
# Impostando la variabile $IFS allinterno dello stesso ciclo, #+ viene eliminata la necessit di salvare il valore originario #+ di $IFS in una variabile temporanea. # Grazie, Dim Segebart per la precisazione. echo "------------------------------------------------" echo "Elenco di tutti gli utenti:" while IFS=: read name passwd uid gid fullname ignore do echo "$name ($fullname)" done <etc/passwd # Redirezione I/O. echo echo "\$IFS ancora $IFS" exit 0
Nota: Il tentativo di impostare delle variabili collegando con una pipe loutput del comando echo a read, fallisce. Tuttavia, collegare con una pipe loutput di cat sembra funzionare.
cat file1 file2 | while read riga do echo $riga done
Comunque, come mostra Bjn Eriksson: Esempio 11-8. Problemi leggendo da una pipe
#!/bin/sh # readpipe.sh # Esempio fornito da Bjon Eriksson. ultimo="(null)" cat $0 | while read riga do echo "{$riga}" ultimo=$riga done printf "\nFatto, ultimo:$ultimo\n"
198
######################################################## ./readpipe.sh {#!/bin/sh} {ultimo="(null)"} {cat $0 |} {while read riga} {do} {echo "{$riga}"} {ultimo=$riga} {done} {printf "nFatto, ultimo:$ultimon"}
Fatto, ultimo:(null) La variabile (ultimo) stata impostata allinterno di una subshell, al di fuori di essa, quindi, rimane non impostata.
Lo script gendiff, che di solito si trova in /usr/bin in molte distribuzioni Linux, usa una pipe per collegare loutput di nd ad un costrutto while read .
find $1 \( -name "*$2" -o -name ".*$2" \) -print | while read f; do . . .
Filesystem
cd Il familiare comando di cambio di directory cd viene usato negli script in cui, per eseguire un certo comando, necessario trovarsi in una directory specica.
[dal gi citato esempio di Alan Cox] Lopzione -P (physical) di cd permette di ignorare i link simbolici. cd - cambia a $OLDPWD, la directory di lavoro precedente.
199
Cautela
Il comando cd non funziona come ci si potrebbe aspettare quando seguito da una doppia barra.
bash$ cd // bash$ pwd //
Loutput, naturalmente, dovrebbe essere /. Questo rappresenta un problema sia da riga di comando che in uno script.
pwd Print Working Directory. Fornisce la directory corrente dellutente (o dello script) (vedi Esempio 11-9). Ha lo stesso effetto della lettura del valore della variabile builtin $PWD. pushd popd dirs Questa serie di comandi forma un sistema per tenere nota delle directory di lavoro; un mezzo per spostarsi avanti e indietro tra le directory in modo ordinato. Viene usato uno stack (del tipo LIFO) per tenere traccia dei nomi delle directory. Diverse opzioni consentono varie manipolazioni dello stack delle directory. pushd nome-dir immette il percorso di nome-dir nello stack delle directory e simultaneamente passa dalla directory di lavoro corrente a nome-dir popd preleva (pop) il nome ed il percorso della directory che si trova nella locazione pi alta dello stack delle directory e contemporaneamente passa dalla directory di lavoro corrente a quella prelevata dallo stack. dirs elenca il contenuto dello stack delle directory (lo si confronti con la variabile $DIRSTACK). Un comando pushd o popd, che ha avuto successo, invoca in modo automatico dirs. Gli script che necessitano di ricorrenti cambiamenti delle directory di lavoro possono trarre giovamento dalluso di questi comandi, evitando di dover codicare ogni modica allinterno dello script. da notare che nellarray implicito $DIRSTACK, accessibile da uno script, memorizzato il contenuto dello stack delle directory. Esempio 11-9. Cambiare la directory di lavoro corrente
#!/bin/bash dir1=/usr/local
200
Variabili
let Il comando let permette di eseguire le operazioni aritmetiche sulle variabili. In molti casi, opera come una versione meno complessa di expr. Esempio 11-10. Facciamo fare a let qualche calcolo aritmetico.
#!/bin/bash echo let a=11 let a=a+5 echo "11 + 5 = $a" # # # # Uguale a a=11 Equivale a let "a = a + 5" (i doppi apici e gli spazi la rendono pi leggibile.) 16
let "a <<= 3" # Equivale a let "a = a << 3" echo "\"\$a\" (=16) scorrimento a sinistra di 3 bit = $a" # 128 let "a /= 4" # Equivale a echo "128 / 4 = $a" # 32 let "a = a / 4"
201
let "a %= 8" # Equivale a let "a = a % 8" echo "270 modulo 8 = $a (270 / 8 = 33, resto $a)" # 6 echo exit 0
eval eval arg1 [arg2] ... [argN] Combina gli argomenti presenti in unespressione, o in una lista di espressioni, e li valuta. Espande qualsiasi variabile presente nellespressione. Il resultato viene tradotto in un comando. Pu essere utile per generare del codice da riga di comando o da uno script.
bash$ processo=xterm bash$ mostra_processo="eval ps ax | grep $processo" bash$ $mostra_processo 1867 tty1 S 0:02 xterm 2779 tty1 S 0:00 xterm 2886 pts/1 S 0:00 grep xterm
# Se non si preservano i ritorni a capo, la verifica delloutput #+ con utility come "awk" risulta pi facile.
202
echo echo "=======================================================================" echo # Ora vediamo come "espandere" una variabile usando "eval" . . . for i in 1 2 3 4 5; do eval valore=$i # valore=$i ha lo stesso effetto. "eval", in questo caso, non necessario. # Una variabile senza meta-significato valuta se stessa -#+ non pu espandersi a nientaltro che al proprio contenuto letterale. echo $valore done echo echo "---" echo for i in ls df; do valore=eval $i # valore=$i in questo caso avrebbe un effetto completamente diverso. # "eval" valuta i comandi "ls" e "df" . . . # I termini "ls" e "df" hanno un meta-significato, #+ dal momento che sono interpretati come comandi #+ e non come stringhe di caratteri. echo $valore done
exit 0
# Le operazioni seguenti devono essere eseguite da root. chmod 666 /dev/ttyS3 # Ripristino dei permessi di lettura+scrittura, #+ altrimenti? # Quando si invia un SIGKILL a ppp i permessi della porta seriale vengono #+ modificati, quindi vanno ripristinati allo stato precedente il SIGKILL.
203
impvar_rot_13 var "foobar" echo $var impvar_rot_13 var "$var" echo $var
# Codifica "foobar" con rot13. # sbbone # Codifica "sbbone" con rot13. # Ritorno al valore originario della variabile. # foobar
Rory Winston ha fornito il seguente esempio che dimostra quanto possa essere utile eval. Esempio 11-14. Utilizzare eval per forzare una sostituzione di variabile in uno script Perl
Nello script Perl "test.pl": ... my $WEBROOT = <WEBROOT_PATH>; ... Per forzare la sostituzione di variabile provate: $export WEBROOT_PATH=/usr/local/webroot $sed s/<WEBROOT_PATH>/$WEBROOT_PATH/ < test.pl > out Ma questo d solamente: my $WEBROOT = $WEBROOT_PATH;
204
Tuttavia: $export WEBROOT_PATH=/usr/local/webroot $eval sed s%\<WEBROOT_PATH\>%$WEBROOT_PATH% < test.pl > out # ==== Che funziona bene, eseguendo lattesa sostituzione: my $WEBROOT = /usr/local/webroot;
Cautela
Il comando eval pu essere rischioso e normalmente, quando esistono alternative ragionevoli, dovrebbe essere evitato. Un eval $COMANDI esegue tutto il contenuto di COMANDI, che potrebbe riservare spiacevoli sorprese come un rm -rf *. Eseguire del codice non molto familiare contenente un eval, e magari scritto da persone sconosciute, signica vivere pericolosamente.
set Il comando set modica il valore delle variabili interne di uno script. Un possibile uso quello di attivare/disattivare le modalit (opzioni), legate al funzionamento della shell, che determinano il comportamento dello script. Unaltra applicazione quella di reimpostare i parametri posizionali passati ad uno script con il risultato dellistruzione (set comando). Lo script assume i campi delloutput di comando come parametri posizionali. Esempio 11-15. Utilizzare set con i parametri posizionali
#!/bin/bash # script "set-test" # Invocate lo script con tre argomenti da riga di comando, # per esempio, "./set-test uno due tre". echo echo echo echo echo
posizionali prima di set nr.1 da riga di comando = nr.2 da riga di comando = nr.3 da riga di comando =
set uname -a
echo $_
205
Invocando set senza alcuna opzione, o argomento, viene visualizzato semplicemente lelenco di tutte le variabili dambiente, e non solo, che sono state inizializzate.
bash$ set AUTHORCOPY=/home/bozo/posts BASH=/bin/bash BASH_VERSION=$2.05.8(1)-release ... XAUTHORITY=/home/bozo/.Xauthority _=/etc/bashrc variabile22=abc variabile23=xzy
set con --$variabile assegna in modo esplicito il contenuto della variabile ai parametri posizionali. Se non viene specicata nessuna variabile dopo --, i parametri posizionali vengono annullati. Esempio 11-16. Riassegnare i parametri posizionali
#!/bin/bash variabile="uno due tre quattro cinque" set -- $variabile # Imposta i parametri posizionali al contenuto di "$variabile". primo_param=$1 secondo_param=$2 shift; shift # Salta i primi due parametri posizionali. restanti_param="$*" echo echo "primo parametro = $primo_param" echo "secondo parametro = $secondo_param" echo "rimanenti parametri = $restanti_param"
206
# uno # due
# ====================================================== set -# Annulla i parametri posizionali quando non viene specificata #+ nessuna variabile. primo_param=$1 secondo_param=$2 echo "primo parametro = $primo_param" echo "secondo parametro = $secondo_param" exit 0
unset il comando unset annulla una variabile di shell, vale a dire, la imposta al valore nullo. Fate attenzione che questo comando non applicabile ai parametri posizionali.
bash$
207
export Il comando export rende disponibili le variabili a tutti i processi gli generati dallo script in esecuzione o dalla shell. Purtroppo, non vi alcun modo per esportare le variabili in senso contrario verso il processo genitore, ovvero nei confronti del processo che ha chiamato o invocato lo script o la shell. Un uso importante del comando export si trova nei le di avvio (startup) per inizializzare e rendere accessibili le variabili dambiente ai susseguenti processi utente. Esempio 11-18. Utilizzare export per passare una variabile ad uno script awk incorporato
#!/bin/bash # #+ #+ #+ #+ Ancora unaltra versione dello script "column totaler" (col-totaler.sh) che aggiunge una specifica colonna (di numeri) nel file di destinazione. Qui viene usato lambiente per passare una variabile dello script ad awk... e inserire lo script awk in una variabile.
ARG=2 E_ERR_ARG=65 if [ $# -ne "$ARG" ] # Verifica il corretto numero di argomenti da #+ riga di comando. then echo "Utilizzo: basename $0 nomefile colonna-numero" exit $E_ERR_ARG fi nomefile=$1 colonna_numero=$2 #===== Fino a questo punto uguale allo script originale =====# export colonna_numero # Esporta il numero di colonna allambiente, in modo che sia disponibile #+ allutilizzo.
# -----------------------------------------------scriptawk={ totale += $ENVIRON["colonna_numero"]} END { print totale } # S, una variabile pu contenere uno script awk. # -----------------------------------------------# Ora viene eseguito lo script awk. awk $scriptawk $nomefile # Grazie, Stephane Chazelas. exit 0
208
declare typeset I comandi declare e typeset specicano e/o limitano le propriet delle variabili. readonly Come declare -r, imposta una variabile in sola lettura ovvero, in realt, come una costante. I tentativi per modicare la variabile falliscono generando un messaggio derrore. lanalogo shell del qualicatore di tipo const del linguaggio C. getopts Questo potente strumento verica gli argomenti passati da riga di comando allo script. lanalogo Bash del comando esterno getopt e della funzione di libreria getopt familiare ai programmatori in C. Permette di passare e concatenare pi opzioni 2 e argomenti associati allo script (per esempio nomescript -abc -e /usr/local). Il costrutto getopts utilizza due variabili implicite. $OPTIND, che il puntatore allargomento, (OPTion INDex) e $OPTARG (OPTion ARGument) largomento (eventuale) associato ad unopzione. Nella dichiarazione, i due punti che seguono il nome dellopzione servono ad indicare che quellopzione ha associato un argomento. Il costrutto getopts di solito si trova allinterno di un ciclo while che elabora le opzioni e gli argomenti uno alla volta e quindi incrementa la variabile implicita $OPTIND per il passo successivo.
Nota: 1. Gli argomenti passati allo script da riga di comando devono essere preceduti da un meno (-). il presso - che consente a getopts di riconoscere gli argomenti da riga di comando come opzioni . Infatti, getopts non elabora argomenti che non siano preceduti da - e termina la sua azione appena incontra unopzione che ne priva.
209
while getopts ":abcde:fg" Opzione # Dichiarazione iniziale. # a, b, c, d, e, f, g sono le opzioni attese. # I : dopo lopzione e indicano che c un argomento associato. do case $Opzione in a ) # Fa qualcosa con la variabile a. b ) # Fa qualcosa con la variabile b. ... e) # Fa qualcosa con e, e anche con $OPTARG, # che largomento associato allopzione e. ... g ) # Fa qualcosa con la variabile g. esac done shift $(($OPTIND - 1)) # Sposta il puntatore allargomento successivo. # Tutto questo non affatto complicato come sembra <sorriso>.
Esempio 11-19. Utilizzare getopts per leggere le opzioni/argomenti passati ad uno script
#!/bin/bash # Prove con getopts e OPTIND # Script modificato il 9/10/03 su suggerimento di Bill Gradwohl. # #+ # #+ # # # # # # #+ # # # # Osserviamo come getopts elabora gli argomenti passati allo script da riga di comando. Gli argomenti vengono verificati come "opzioni" (flag) ed argomenti associati. Provate ad invocare lo script con nomescript -mn nomescript -oq qOpzione (qOpzione pu essere una stringa qualsiasi.) nomescript -qXXX -r nomescript -qr - Risultato inaspettato, considera "r" come largomento dellopzione "q" nomescript -q -r - Risultato inaspettato, come prima. nomescript -mnop -mnop - Risultato inaspettato (OPTIND non attendibile nello stabilire da dove proviene unopzione).
210
NO_ARG=0 E_ERR_OPZ=65 if [ $# -eq "$NO_ARG" ] # Lo script stato invocato senza #+ alcun argomento?
then echo "Utilizzo: basename $0 opzioni (-mnopqrs)" exit $E_ERR_OPZ # Se non ci sono argomenti, esce e #+ spiega come usare lo script. fi # Utilizzo: nomescript -opzioni # Nota: necessario il trattino (-)
while getopts ":mnopq:rs" Opzione do case $Opzione in m ) echo "Scenario nr.1: opzione -m- [OPTIND=${OPTIND}]";; n | o ) echo "Scenario nr.2: opzione -$Opzione- [OPTIND=${OPTIND}]";; p ) echo "Scenario nr.3: opzione -p- [OPTIND=${OPTIND}]";; q ) echo "Scenario nr.4: opzione -q-\ con argomento \"$OPTARG\" [OPTIND=${OPTIND}]";; # Notate che lopzione q deve avere un argomento associato, # altrimenti salta alla voce predefinita del costrutto case. r | s ) echo "Scenario nr.5: opzione -$Opzione-";; * ) echo " stata scelta unopzione non implementata.";; # DEFAULT esac done shift $(($OPTIND - 1)) # Decrementa il puntatore agli argomenti in modo che punti al successivo. # $1 fa ora riferimento al primo elemento non-opzione fornito da riga di #+ comando, ammesso che ci sia. exit 0 # Come asserisce Bill Gradwohl, # "Il funzionamento di getopts permette di specificare: nomescript -mnop -mnop, #+ ma non esiste, utilizzando OPTIND, nessun modo affidabile per differenziare #+ da dove proviene che cosa."
211
Capitolo 11. Comandi interni e builtin codice allinterno dello script, accodandolo (stesso effetto della direttiva #include di un programma C ). Il risultato nale uguale allinserimento di righe di codice nel corpo dello script. utile in situazioni in cui diversi script usano un le dati comune o una stessa libreria di funzioni. Esempio 11-20. Includere un le dati
#!/bin/bash . file-dati # Carica un file dati. # Stesso effetto di "source file-dati", ma pi portabile. # Il file "file-dati" deve essere presente nella directory di lavoro #+ corrente, poich vi si fa riferimento per mezzo del suo basename. # Ora utilizziamo alcuni dati del file. echo "variabile1 (dal file-dati) = $variabile1" echo "variabile3 (dal file-dati) = $variabile3" let "sommma = $variabile2 + $variabile4" echo "Somma della variabile2 + variabile4 (dal file-dati) = $somma" echo "messaggio1 (dal file-dati) \"$messaggio1\"" # Nota: apici doppi con escape. visualizza_messaggio Questa la funzione di visualizzazione messaggio \ presente in file-dati.
exit 0
Il le file-dati per lEsempio 11-20 precedente. Devessere presente nella stessa directory.
# # # Questo il file dati caricato dallo script. File di questo tipo possono contenere variabili, funzioni, ecc. Pu essere caricato con il comando source o . da uno script di shell.
# Inizializziamo alcune variabili. variabile1=22 variabile2=474 variabile3=5 variabile4=97 messaggio1="Ciao, come stai?" messaggio2="Per ora piuttosto bene. Arrivederci." visualizza_messaggio () { # Visualizza qualsiasi messaggio passato come argomento. if [ -z "$1" ] then return 1
212
Se il le caricato con source anchesso uno script eseguibile, verr messo in esecuzione e, alla ne, il controllo ritorner allo script che lha richiamato. A questo scopo, uno script eseguibile caricato con source pu usare return. Si possono passare (opzionalmente) degli argomenti al le caricato con source come parametri posizionali.
source $nomefile $arg1 arg2
anche possibile per uno script usare source in riferimento a se stesso, sebbene questo non sembri avere reali applicazioni pratiche. Esempio 11-21. Un (inutile) script che carica se stesso
#!/bin/bash # self-source.sh: uno script che segue se stesso "ricorsivamente." # Da "Stupid Script Tricks," Volume II. MAXPASSCNT=100 # Numero massimo di esecuzioni.
echo -n "$conta_passi " # Al primo passaggio, vengono visualizzati solo due spazi, #+ perch $conta_passi non stata inizializzata. let "conta_passi += 1" # Si assume che la variabile $conta_passi non inizializzata possa essere #+ incrementata subito. # Questo funziona con Bash e pdksh, ma si basa su unazione non portabile
213
# Esercizio: # --------# Scrivete uno script che usi questo espediente per fare qualcosa #+ di veramente utile.
exit Termina in maniera incondizionata uno script. Il comando exit opzionalmente pu avere come argomento un intero che viene restituito alla shell come exit status dello script. buona pratica terminare tutti gli script, tranne quelli pi semplici, con exit 0, indicandone con ci la corretta esecuzione.
Nota: Se uno script termina con un exit senza argomento, lexit status dello script corrisponde a quello dellultimo comando eseguito nello script, escludendo exit. Equivale a exit $?.
exec Questo builtin di shell sostituisce il processo corrente con un comando specicato. Normalmente, quando la shell incontra un comando, genera (forking) un processo glio che quello che esegue effettivamente il comando. Utilizzando il builtin exec, la shell non esegue il forking ed il comando lanciato con exec sostituisce la shell. Se viene usato in uno script ne forza luscita quando il comando eseguito con exec termina. 3
214
exec serve anche per riassegnare i descrittori dei le. Per esempio, exec <zzz-file sostituisce lo stdin con il le zzz-file.
Nota: Lopzione -exec di nd non la stessa cosa del builtin di shell exec.
215
Capitolo 11. Comandi interni e builtin shopt Questo comando permette di cambiare le opzioni di shell al volo (vedi Esempio 24-1 e Esempio 24-2). Appare spesso nei le di avvio (startup) Bash, ma pu essere usato anche in altri script. necessaria la versione 2 o seguenti di Bash.
shopt -s cdspell # Consente le errate digitazioni, non gravi, dei nomi delle directory quando #+ si usa cd cd /hpme pwd # Oops! Errore /home. # /home # La shell ha corretto lerrore di digitazione.
caller Inserendo il comando caller allinterno di una funzione vengono visualizzate allo stdout informazioni su chi ha richiamato quella funzione.
#!/bin/bash funzione1 () { # Allinterno di funzione1 (). caller 0 # Dimmi tutto. } funzione1 # Riga 9 dello script.
Numero della riga dove la funzione stata richiamata. Invocata dalla parte "main" dello script. Nome dello script chiamante.
Il comando caller pu restituire anche informazioni sul chiamante se inserita uno script caricato con source allinterno di un altro script. Come una funzione, si tratta di una chiamata di subroutine. Questo comando potrebbe essere utile nel debugging.
Comandi
true Comando che restituisce zero come exit status di una corretta esecuzione, ma nientaltro.
216
false Comando che restituisce lexit status di una esecuzione non andata a buon ne, ma nientaltro.
Prova di "false" if false then echo "false valuta \"vero\"" else echo "false valuta \"falso\"" fi # false valuta "falso"
# Ciclo while "falso" (ciclo nullo) while false do # Il codice seguente non verr eseguito. operazione-1 operazione-2 ... operazione-n # Non succede niente! done
type [comando] Simile al comando esterno which, type comando fornisce il percorso completo di comando. A differenza di which, type un builtin di Bash. Lutile opzione -a di type identica le parole chiave ed i builtin, individuando anche i comandi di sistema che hanno gli stessi nomi.
217
hash [comandi] Registra i percorsi assoluti dei comandi specicati -- nella tabella degli hash della shell 4 -- in modo che la shell o lo script non avranno bisogno di cercare $PATH nelle successive chiamate di quei comandi. Se hash viene eseguito senza argomenti, elenca semplicemente i comandi presenti nella tabella. Lopzione -r cancella la tabella degli hash. bind Il builtin bind visualizza o modica la congurazione duso della tastiera tramite readline 5. help Fornisce un breve riepilogo dellutilizzo di un builtin di shell. il corrispettivo di whatis, per i builtin.
bash$ help exit exit: exit [n] Exit the shell with a status of N. If N is omitted, the exit status is that of the last command executed.
218
disown Cancella il/i job dalla tabella dei job attivi della shell. fg bg Il comando fg modica lesecuzione di un job da background (sfondo) in foreground (primo piano). Il comando bg fa ripartire un job che era stato sospeso, mettendolo in esecuzione in background. Se non viene specicato nessun numero di job, allora il comando fg o bg agisce sul job attualmente in esecuzione. wait Arresta lesecuzione dello script nch tutti i job in esecuzione in background non sono terminati, o nch non terminato il job o il processo il cui ID stato passato come opzione. Restituisce lexit status di attesa-comando. Il comando wait pu essere usato per evitare che uno script termini prima che un job in esecuzione in background abbia ultimato il suo compito (ci creerebbe un temibile processo orfano). Esempio 11-24. Attendere la ne di un processo prima di continuare
#!/bin/bash ROOT_UID=0 # Solo gli utenti con $UID 0 posseggono i privilegi di root. E_NONROOT=65 E_NOPARAM=66 if [ "$UID" -ne "$ROOT_UID" ] then echo "Bisogna essere root per eseguire questo script." # "Cammina ragazzo, hai finito di poltrire." exit $E_NONROOT fi if [ -z "$1" ] then echo "Utilizzo: basename $0 nome-cercato" exit $E_NOPARAM fi
219
Opzionalmente, wait pu avere come argomento un identicatore di job, per esempio, wait%1 o wait $PPID. Vedi la tabella degli identicatori di job.
Suggerimento: In uno script, far eseguire un comando in background, per mezzo della E commerciale (&), pu causare la sospensione dello script nch non viene premuto il tasto INVIO. Questo sembra capitare con i comandi che scrivono allo stdout. Pu rappresentare un grande fastidio.
#!/bin/bash # test.sh ls -l & echo "Fatto."
bash$ ./test.sh Fatto. [bozo@localhost test-scripts]$ total 1 -rwxr-xr-x 1 bozo bozo _
Mettendo wait dopo il comando che deve essere eseguito in background si rimedia a questo comportamento.
#!/bin/bash # test.sh ls -l & echo "Fatto." wait
bash$ ./test.sh Fatto. [bozo@localhost test-scripts]$ total 1 -rwxr-xr-x 1 bozo bozo
220
suspend Ha un effetto simile a Control-Z, ma sospende la shell (il processo genitore della shell pu, ad un certo momento stabilito, farle riprendere lesecuzione). logout il comando di uscita da una shell di login. Facoltativamente pu essere specicato un exit status. times Fornisce statistiche sul tempo di sistema impiegato per lesecuzione dei comandi, nella forma seguente:
0m0.020s 0m0.020s
Questo comando ha un valore molto limitato perch non di uso comune tracciare proli o benchmark degli script di shell. kill Termina immediatamente un processo inviandogli un appropriato segnale di terminazione (vedi Esempio 13-6). Esempio 11-25. Uno script che uccide s stesso
#!/bin/bash # self-destruct.sh kill $$ # Lo script in questo punto "uccide" il suo stesso processo. # Ricordo che "$$" il PID dello script.
echo "Questa riga non viene visualizzata." # Invece, la shell invia il messaggio "Terminated" allo stdout. exit 0 # Dopo che lo script terminato prematuramente, qual lexit #+ status restituito? # # sh self-destruct.sh # echo $? # 143 # # 143 = 128 + 15 # segnale SIGTERM
Nota: kill -l elenca tutti i segnali. kill -9 il killer infallibile, che solitamente interrompe un processo che si riuta ostinatamente di terminare con un semplice kill. Talvolta funziona
221
command La direttiva command COMANDO disabilita gli alias e le funzioni del comando COMANDO.
Nota: una delle tre direttive di shell attinenti allelaborazione dei comandi di uno script. Le altre sono builtin ed enable.
builtin Invocando builtin COMANDO_BUILTIN viene eseguito COMANDO_BUILTIN come se fosse un builtin di shell, disabilitando temporaneamente sia le funzioni che i comandi di sistema esterni aventi lo stesso nome. enable Abilita o disabilita un builtin di shell. Ad esempio, enable -n kill disabilita il builtin di shell kill, cos quando Bash successivamente incontra un kill, invocher /bin/kill. Lopzione -a di enable elenca tutti i builtin di shell, indicando se sono abilitati o meno. Lopzione -f nomefile permette ad enable di caricare un builtin come un modulo di una libreria condivisa (DLL) da un le oggetto correttamente compilato. 6.
autoload un adattamento per Bash dellautoloader ksh. In presenza di un autoload , viene caricata una funzione contenente una dichiarazione autoload da un le esterno, alla sua prima invocazione. 7 Questo fa risparmiare risorse di sistema. da notare che autoload non fa parte dellinstallazione normale di Bash. Bisogna caricarlo con enable -f (vedi sopra).
Signicato numero associato al job [N] Chiamata (da riga di comando) del job che inizia con la stringa S
222
Signicato Chiamata (da riga di comando) del job con al suo interno la stringa S job corrente (ultimo job arrestato in foreground o iniziato in background) job corrente (ultimo job arrestato in foreground o iniziato in background) Ultimo job Ultimo processo in background
Note
1. Uneccezione rappresentata dal comando time, citato nella documentazione ufciale Bash come parola chiave. 2. Unopzione un argomento che funziona come un interruttore, attivando/disattivando le modalit di azione di uno script. Largomento associato ad una particolare opzione ne indica o meno labilitazione. 3. Tranne quando exec viene usato per riassegnare i descrittori dei le. 4. L
hashing un metodo per la creazione di chiavi di ricerca per i dati registrati in una tabella. Le chiavi vengono create codicando i dati stessi per mezzo di uno tra i numerosi e semplici algoritmi matematici. Il vantaggio dellhashing la velocit. Lo svantaggio che possono vericarsi delle collisioni -- quando una sola chiave fa riferimento a pi di un dato. Per esempi di hashing vedi Esempio A-21 e Esempio A-22.
5. La libreria readline quella che Bash utilizza per leggere linput in una shell interattiva. 6. I sorgenti C per un certo numero di builtin caricabili, solitamente, si trovano nella directory/usr/share/doc/bash-?.??/functions.
E da notare che lopzione -f di enable non portabile su tutti i sistemi.
223
224
# Crea il "sommario" dei file. ls -lRF $DIRECTORY_IMMAGINE > $DIRECTORY_IMMAGINE/$CONTENUTIFILE # Lopzione "l" fornisce un elenco "dettagliato". # Lopzione "R" rende lelencazione ricorsiva. # Lopzione "F" evidenzia i tipi di file (le directory hanno una #+ "/" dopo il nome). echo "Il sommario stato creato." # Crea limmagine del file che verr registrato sul CDR. mkisofs -r -o $FILEIMMAGINE $DIRECTORY_IMMAGINE echo " stata creata limmagine ($FILEIMMAGINE) su file system ISO9660." # Registra il CDR. echo "Sto \"bruciando\" il CD." echo "Siate pazienti, occorre un po di tempo." cdrecord -v -isosize speed=$VELOC dev=$DISPOSITIVO $FILEIMMAGINE exit $?
cat tac cat lacronimo di concatenato, visualizza un le allo stdout. In combinazione con gli operatori di redirezione (> o >>) comunemente usato per concatenare le.
# Usi di cat cat nomefile cat file.1 file.2 file.3 > file.123 # Visualizza il contenudo del file. # Concatena tre file in uno.
Lopzione -n di cat numera consecutivamente le righe del/dei le di riferimento. Lopzione -b numera solo le righe non vuote. Lopzione -v visualizza i caratteri non stampabili, usando la notazione ^ . Lopzione -s comprime tutte le righe vuote consecutive in ununica riga vuota. Vedi anche Esempio 12-25 e Esempio 12-21.
Nota: In una pipe, risulta pi efciente redirigere lo stdin in un le piuttosto che usare cat.
cat nomefile | tr a-z A-Z tr a-z A-Z < nomefile # Stesso risultato, ma si avvia un processo in meno, #+ e senza dover usare la pipe.
tac linverso di cat e visualizza un le in senso contrario, vale a dire, partendo dalla ne.
225
Capitolo 12. Filtri, programmi e comandi esterni rev inverte ogni riga di un le e la visualizza allo stdout. Non ha lo stesso effetto di tac poich viene preservato lordine delle righe, semplicemente rovescia ciascuna riga.
cp
il comando per la copia dei le. cp file1 file2 copia file1 in file2, sovrascrivendo file2 nel caso esistesse gi (vedi Esempio 12-6).
Suggerimento: Sono particolarmente utili le opzioni -a di archiviazione (per copiare un intero albero di directory), -u di aggiornamento, e -r e -R di ricorsivit.
cp -u dir_sotgente/* dir_destinazione # "Sincronizza" dir_destinazione con dir_sorgente #+ copiando i file pi recenti e quelli precedentemente inesistenti.
mv il comando per lo spostamento di le. equivalente alla combinazione di cp e rm. Pu essere usato per spostare pi le in una directory o anche per rinominare una directory. Per alcune dimostrazioni sulluso di mv in uno script, vedi Esempio 9-18 e Esempio A-2.
Nota: Se usato in uno script non interattivo, mv vuole lopzione -f (forza) per evitare linput dellutente. Quando una directory viene spostata in unaltra preesistente, diventa la sottodirectory di questultima.
226
rm Cancella (rimuove) uno o pi le. Lopzione -f forza la cancellazione anche dei le in sola lettura. utile per evitare linput dellutente in uno script.
Nota: Il semplice comando rm non riesce a cancellare i le i cui nomi iniziano con un trattino.
bash$ rm -bruttonome rm: invalid option -- b Try rm --help for more information.
Un modo per riuscirci far precedere il nome del le che deve essere rimosso da punto-barra.
bash$ rm ./-bruttonome
Attenzione
Se usato con lopzione di ricorsivit -r, il comando cancella tutti i le della directory corrente. Uno sbadato rm -rf * pu eliminare buona parte della struttura di una directory.
rmdir Cancella una directory. Afnch questo comando funzioni necessario che la directory non contenga alcun le -- neanche gli invisibili dotle 1. mkdir Crea una nuova directory. Per esempio, mkdir -p progetto/programmi/Dicembre crea la directory indicata. Lopzione -p crea automaticamente tutte le necessarie directory indicate nel percorso.
227
Capitolo 12. Filtri, programmi e comandi esterni chmod Modica gli attributi di un le esistente (vedi Esempio 11-12).
chmod +x nomefile # Rende eseguibile "nomefile" per tutti gli utenti. chmod u+s nomefile # Imposta il bit "suid" di "nomefile". # Un utente comune pu eseguire "nomefile" con gli stessi privilegi del #+ proprietario del file (Non applicabile agli script di shell).
chmod 644 nomefile # D al proprietario i permessi di lettura/scrittura su "nomefile", il #+ permesso di sola lettura a tutti gli altri utenti # (modalit ottale).
chmod 1777 nome-directory # D a tutti i permessi di lettura, scrittura ed esecuzione nella #+ directory, inoltre imposta lo "sticky bit". Questo significa che solo il #+ proprietario della directory, il proprietario del file e, naturalmente, root #+ possono cancellare dei file particolari presenti in quella directory.
chattr Modica gli attributi del le. Ha lo stesso effetto di chmod, visto sopra, ma con sintassi ed opzioni diverse, e funziona solo su un lesystem di tipo ext2. Unopzione particolarmente interessante di chattr i. chattr +i nomefile contrassegna quel le come immodicabile. Il le non pu essere in alcun modo modicato, soggetto a link o cancellato, neanche da root. Questo attributo pu essere impostato o rimosso solo da root. In modo simile, lopzione a contrassegna il le come scrivibile, ma solo per accodamento.
root# rm file1.txt rm: remove write-protected regular file file1.txt? y rm: cannot remove file1.txt: Operation not permitted
228
Capitolo 12. Filtri, programmi e comandi esterni Se un le ha impostato lattributo s (secure), in caso di cancellazione il/i blocco/hi che occupava sul disco verr/anno sovrascritto/i con degli zero. Se un le ha impostato lattributo u (undelete), in caso di cancellazione sar ancora possibile recuperarne il contenuto (non cancellato). Se un le ha impostato lattributo c (compress), viene automaticamente compresso prima della scrittura su disco e decompresso per la lettura.
Nota: Gli attributi di un le impostati con chattr non vengono elencati (se si usato (ls -l).
ln Crea dei link a le esistenti. Un link un riferimento a un le, un nome alternativo. Il comando ln permette di fare riferimento al le collegato (linkato) con pi di un nome e rappresenta unalternativa di livello superiore alluso degli alias (vedi Esempio 4-6). ln crea semplicemente un riferimento, un puntatore al le, che occupa solo pochi byte.
Il comando ln usato molto spesso con lopzione -s, simbolico o soft. Uno dei vantaggi delluso dellopzione -s che consente link alle directory o a le di lesystem diversi. La sintassi del comando un po ingannevole. Per esempio: ln -s vecchiofile nuovofile collega nuovofile, creato con listruzione, allesistente vecchiofile.
Cautela
Nel caso sia gi presente un le di nome nuovofile, viene visualizzato un messaggio derrore.
229
Nota a margineQuale tipo di link usare? Ecco la spiegazione di John Macdonald: Entrambi i tipi (simbolico e hard [N.d.T.]) forniscono uno strumento sicuro di referenziazione doppia -- se si modica il contenuto del le usando uno dei due nomi, le modiche riguarderanno sia il le con il nome originario che quello con il nome nuovo, sia esso un hard link oppure un link simbolico. Le loro differenze si evidenziano quando si opera ad un livello superiore. Il vamtaggio di un hard link che il nuovo nome completamente indipendente da quello vecchio -- se si cancella o rinomina il vecchio le, questo non avr alcun effetto su un hard link, che continua a puntare ai dati reali, mentre spezzerebbe un link simbolico che punta al vecchio nome che non esiste pi. Il vantaggio di un link simbolico che pu far riferimento ad un diverso lesystem (dal momento che si tratta di un semplice collegamento al nome di un le, non ai dati reali). E, a differenza di un hard link, pu far riferimento a una directory.
Con i link si ha la possibilit di invocare uno stesso script (o qualsiasi altro eseguibile) con nomi differenti ottenendo un comportamento diverso in base al nome con cui stato invocato. Esempio 12-2. Ciao o arrivederci
#!/bin/bash # hello.sh: Visualizzare "ciao" o "arrivederci" #+ secondo le modalit di invocazione dello script. # # # # # Eseguiamo un collegamento allo script nella directory di lavoro corrente($PWD): ln -s hello.sh goodbye Ora proviamo ad invocare lo script in entrambi i modi: ./hello.sh ./goodbye
CHIAMATA_CIAO=65 CHIAMATA_ARRIVEDERCI=66 if [ $0 = "./goodbye" ] then echo "Arrivederci!" # Se si desidera, qualche altro saluto dello stesso tipo. exit $CHIAMATA_ARRIVEDERCI fi echo "Ciao!" # Qualche altro comando appropriato. exit $CHIAMATA_CIAO
230
man info Questi comandi danno accesso alle informazioni e alle pagine di manuale dei comandi di sistema e delle utility installate. Quando sono disponibili, le pagine info, di solito, contengono una descrizione pi dettagliata che non le pagine di manuale.
Se COMANDO contiene {}, allora nd sostituisce {} con il percorso completo del le selezionato.
find ~/ -name core* -exec rm {} \; # Cancella tutti i file core presenti nella directory home dellutente.
find /home/bozo/projects -mtime 1 # Elenca tutti i file della directory /home/bozo/projects #+ che sono stati modificati il giorno precedente. # # mtime = ora dellultima modifica del file in questione # ctime = ora dellultima modifica di stato (tramite chmod o altro) # atime = ora dellultimo accesso DIR=/home/bozo/junk_files
231
find /etc -exec grep [0-9][0-9]*[.][0-9][0-9]*[.][0-9][0-9]*[.][0-9][0-9]* {} \; # # Trova tutti gli indirizzi IP (xxx.xxx.xxx.xxx) nei file della directory /etc. Ci sono alcuni caratteri non essenziali. Come possono essere rimossi?
# Ecco una possibilit: find /etc -type f -exec cat {} \; | tr -c .[:digit:] \n \ | grep ^[^.][^.]*\.[^.][^.]*\.[^.][^.]*\.[^.][^.]*$ # # [:digit:] una delle classi di caratteri #+ introdotta con lo standard POSIX 1003.2. # Grazie, Stphane Chazelas.
Nota: Lopzione -exec di nd non deve essere confusa con il builtin di shell exec.
Esempio 12-3. Badname, elimina, nella directory corrente, i le i cui nomi contengono caratteri inappropriati e spazi.
#!/bin/bash # badname.sh # Cancella i file nella directory corrente contenenti caratteri inadatti. for nomefile in * do nomestrano=echo "$nomefile" | sed -n /[\+\{\;\"\\\=\?~\(\)\<\>\&\*\|\$]/p # Anche in questo modo: # nomestrano=echo "$nomefile" | sed -n /[+{;"\=?~()<>&*|$]/p # Cancella i file contenenti questi caratteri: + { ; " \ = ? ~ ( ) < > & * | $ # rm $nomestrano 2>/dev/null # ^^^^^^^^^^^ Vengono eliminati i messaggi derrore. done
232
# Ora ci occupiamo find . -name "* *" # Il percorso del # La \ assicura #+ comando. exit 0
dei file contenenti ogni tipo di spaziatura. -exec rm -f {} \; file che "find" cerca prende il posto di "{}". che il ; sia interpretato correttamente come fine del
#-----------------------------------------------------------------------# I seguenti comandi non vengono eseguiti a causa dell"exit" precedente. # Unalternativa allo script visto prima: find . -name *[+{;"\\=?~()<>&*|$ ]* -exec rm -f {} \; # (Grazie, S.C.)
233
Vedi Esempio 12-27, Esempio 3-4 ed Esempio 10-9 per script che utilizzano nd. La relativa pagina di manuale fornisce tutti i dettagli di questo potente e complesso comando.
xargs Un ltro per fornire argomenti ad un comando ed anche uno strumento per assemblare comandi. Suddivide il usso di dati in parti sufcientemente piccole per essere elaborate da ltri o comandi. Lo si consideri un potente sostituto degli apici inversi. In situazioni in cui la sostituzione di comando potrebbe fallire con il messaggio derrore too many arguments sostituendola con xargs, spesso, il problema si risolve. 2 Normalmente xargs legge dallo stdin o da una pipe, ma anche dalloutput di un le. Il comando predenito per xargs echo. Questo signica che linput collegato a xargs perde i ritorni a capo o qualsiasi altro carattere di spaziatura.
bash$ ls -l total 0 -rw-rw-r--rw-rw-r--
1 bozo 1 bozo
bozo bozo
bash$ ls -l | xargs total 0 -rw-rw-r-- 1 bozo bozo 0 Jan 29 23:58 file1 -rw-rw-r-- 1 bozo bozo 0 Jan 29 23:58 file2
bash$ find ~/mail -type f | xargs grep "Linux" ./misc:User-Agent: slrn/0.9.8.1 (Linux) ./sent-mail-jul-2005: hosted by the Linux Documentation Project. ./sent-mail-jul-2005: (Linux Documentation Project Site, rtf version) ./sent-mail-jul-2005: Subject: Criticism of Bozos Windows/Linux article ./sent-mail-jul-2005: while mentioning that the Linux ext2/ext3 filesystem . . .
234
Capitolo 12. Filtri, programmi e comandi esterni ls | xargs -p -l gzip comprime con gzip tutti i le della directory corrente, uno alla volta, ed attende un INVIO prima di ogni operazione.
Suggerimento: Uninteressante opzione di xargs -n NN , che limita a NN il numero degli argomenti passati. ls | xargs -n 8 echo elenca i le della directory corrente su 8 colonne.
Suggerimento: Unaltra utile opzione -0, in abbinamento con nd -print0 o grep -lZ. Permette di gestire gli argomenti che contengono spazi o apici. find / -type f -print0 | xargs -0 grep -liwZ GUI | xargs -0 rm -f grep -rliwZ GUI / | xargs -0 rm -f Entrambi gli esempi precedenti cancellano tutti i le che contengono GUI. (Grazie, S.C.)
Esempio 12-5. Creare un le di log utilizzando xargs per vericare i log di sistema
#!/bin/bash # Genera un file di log nella directory corrente #+ partendo dalla fine del file /var/log/messages. # Nota: /var/log/messages deve avere i permessi di lettura #+ nel caso lo script venga invocato da un utente ordinario. # #root chmod 644 /var/log/messages RIGHE=5 ( date; uname -a ) >>logfile # Data e nome della macchina echo ----------------------------------------------------------- >>logfile tail -$RIGHE /var/log/messages | xargs | fmt -s >>logfile echo >>logfile echo >>logfile exit 0 # # # #+ #+ # # # Nota: ---Come ha sottolineato Frank Wang, gli apici non verificati (siano essi singoli o doppi) nel file sorgente potrebbero far fare indigestione ad xargs. Suggerisce, quindi, di sostituire la riga 15 con la seguente: tail -$RIGHE /var/log/messages | tr -d "\"" | xargs | fmt -s >>logfile
# Esercizio:
235
Come nel caso di nd, le due parentesi graffe sostituiscono un testo. Esempio 12-6. Copiare i le della directory corrente in unaltra
#!/bin/bash # copydir.sh # Copia (con dettagli) tutti i file della directory corrente ($PWD) #+ nella directory specificata da riga di comando. E_NOARG=65 if [ -z "$1" ] # Esce se non viene fornito nessun argomento. then echo "Utilizzo: basename $0 directory-in-cui-copiare" exit $E_NOARG fi ls # # # # # # # #+ #+ . | xargs -i -t cp ./{} $1 ^^ ^^ ^^ -t lopzione "verbose" (invia la riga di comando allo stderr). -i lopzione "sostituisci stringhe". {} il segnaposto del testo di output. E simile alluso di una coppia di parentesi graffe in "find." Elenca i file presenti nella directory corrente (ls .), passa loutput di "ls" come argomenti a "xargs" (opzioni -i -t), quindi copia (cp) questi argomenti ({}) nella nuova directory ($1).
# Il risultato finale lequivalente esatto di # cp * $1 # a meno che qualche nome di file contenga caratteri di "spaziatura". exit 0
236
NOME_PROCESSO="$1" ps ax | grep "$NOME_PROCESSO" | awk {print $1} | xargs -i kill {} 2&>/dev/null # ^^ ^^ # # # # # # -------------------------------------------------------------------Note: -i lopzione "sostituisci stringhe" di xargs. Le parentesi graffe rappresentano il segnaposto per la sostituzione. 2&>/dev/null elimina i messaggi derrore indesiderati. --------------------------------------------------------------------
exit $?
# 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
237
######################################################## cat "$1" | xargs -n1 | \ # Elenca il file una parola per riga. tr A-Z a-z | \ # Cambia tutte le lettere maiuscole in minuscole. sed -e s/\.//g -e s/\,//g -e s/ /\ /g | \ # Filtra i punti e le virgole, e #+ cambia gli spazi tra le parole in linefeed. sort | uniq -c | sort -nr # Infine premette il conteggio delle occorrenze e le #+ ordina in base al numero. ######################################################## # Svolge lo stesso lavoro dellesempio "wf.sh", #+ ma in modo un po pi greve e lento (perch?). exit 0
expr Comando multiuso per la valutazione delle espressioni: Concatena e valuta gli argomenti secondo le operazioni specicate (gli argomenti devono essere separati da spazi). Le operazioni possono essere aritmetiche, logiche, su stringhe o confronti. expr 3 + 5 restituisce 8 expr 5 % 3 restituisce 2 expr 1 / 0 restituisce il messaggio derrore: expr: divisione per zero Non permessa unoperazione aritmetica illecita.
expr 5 \* 3 restituisce 15 Loperatore di moltiplicazione deve essere usato con lescaping nelle espressioni aritmetiche che impiegano expr.
238
Capitolo 12. Filtri, programmi e comandi esterni y=expr $y + 1 Incrementa la variabile, con lo stesso risultato di let y=y+1 e y=$(($y+1)). Questo un esempio di espansione aritmetica. z=expr substr $stringa $posizione $lunghezza Estrae da $stringa una sottostringa di $lunghezza caratteri, iniziando da $posizione. Esempio 12-9. Utilizzo di expr
#!/bin/bash # Dimostrazione di alcuni degli usi di expr # =========================================== echo # Operatori aritmetici # --------- ---------echo "Operatori aritmetici" echo a=expr 5 + 3 echo "5 + 3 = $a" a=expr $a + 1 echo echo "a + 1 = $a" echo "(incremento di variabile)" a=expr 5 % 3 # modulo echo echo "5 modulo 3 = $a" echo echo # Operatori logici # --------- -----# Restituisce 1 per vero, 0 per falso, #+ il contrario della normale convenzione Bash. echo "Operatori logici" echo x=24 y=25 b=expr $x = $y echo "b = $b" echo
239
echo echo
# Operatori per stringhe # --------- --- -------echo "Operatori per stringhe" echo a=1234zipper43231 echo "La stringa su cui opereremo \"$a\"." # length: lunghezza della stringa b=expr length $a echo "La lunghezza di \"$a\" $b." # index: posizione, in stringa, del primo carattere # della sottostringa verificato b=expr index $a 23 echo "La posizione numerica del primo \"2\" in \"$a\" \"$b\"." # substr: estrae una sottostringa, iniziando da posizione & lunghezza #+ specificate b=expr substr $a 2 6 echo "La sottostringa di \"$a\", iniziando dalla posizione 2,\ e con lunghezza 6 caratteri \"$b\"."
# Il comportamento preimpostato delle operazioni match quello #+ di cercare loccorrenza specificata all***inizio*** della stringa. # # usa le Espressioni Regolari
240
Importante: Loperatore : pu sostituire match. Per esempio, b=expr $a : [0-9]* lequivalente esatto di b=expr match $a [0-9]* del listato precedente.
#!/bin/bash echo echo "Operazioni sulle stringhe usando il costrutto \"expr \$stringa : \"" echo "===================================================" echo a=1234zipper5FLIPPER43231 echo "La stringa su cui opereremo \"expr "$a" : \(.*\)\"." # Operatore di raggruppamento parentesi con escape. == == # #+ #+ # *************************** Le parentesi con lescape verificano una sottostringa ***************************
# Se non si esegue lescaping delle parentesi... #+ allora expr converte loperando stringa in un intero. echo "La lunghezza di \"$a\" expr "$a" : .*."# Lunghezza della stringa echo "Il numero di cifre allinizio di \"$a\" expr "$a" : [0-9]*." # ------------------------------------------------------------------------- # echo echo "Le cifre allinizio di \"$a\" sono expr "$a" : \([0-9]*\)." # == == echo "I primi 7 caratteri di \"$a\" sono expr "$a" : \(.......\)." # ===== == == # Ancora, le parentesi con lescape forzano la verifica della sottostringa. # echo "Gli ultimi 7 caratteri di \"$a\" sono expr "$a" : .*\(.......\)." # ====== operatore di fine stringa ^^ # (in realt questo vuol dire saltare uno o pi caratteri finch non viene #+ raggiunta la sottostringa specificata) echo exit 0
241
Lo script precedente illustra come expr usa le parentesi con lescaping -- \( ... \) -- per raggruppare operatori, in coppia con la verica di espressione regolare, per trovare una sottostringa. Ecco un altro esempio, questa volta preso dal mondo reale.
# Toglie le spaziature iniziali e finali. LRFDATE=expr "$LRFDATE" : [[:space:]]*\(.*\)[[:space:]]*$ # Dallo script di Peter Knowles "booklistgen.sh" #+ per la conversione di file nel formato Sony Librie. # (http://booklistgensh.peterknowles.com)
Perl, sed e awk possiedono strumenti di gran lunga superiori per la verica delle stringhe. Una breve subroutine sed o awk in uno script (vedi la Sezione 33.2) unalternativa attraente ad expr. Vedi la Sezione 9.2 per approfondimenti sulluso di expr nelle operazioni sulle stringhe.
242
Il comando date possiede un certo numero di opzioni. Per esempio %N visualizza la parte di nanosecondi dellora corrente. Un uso interessante quello per generare interi casuali di sei cifre.
date +%N | sed -e s/000$// -e s/^0// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ # Toglie gli zeri iniziali e finali, se presenti.
# Il parametro TZ consente di ottenere il tempo di una zona diversa da #+ quella di default. date # Mon Mar 28 21:42:16 MST 2005 TZ=EST date # Mon Mar 28 23:42:16 EST 2005 # Grazie a Frank Kannemann e Pete Sjoberg, per il suggerimento.
SeiGiorniFa=$(date --date=6 days ago) UnMeseFa=$(date --date=1 month ago) # Quattro settimane (non un mese). UnAnnoFa=$(date --date=1 year ago)
243
time Fornisce statistiche molto dettagliate sul tempo di esecuzione di un comando. time ls -l / visualizza qualcosa di simile:
0.00user 0.01system 0:00.05elapsed 16%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (149major+27minor)pagefaults 0swaps
touch Utility per aggiornare allora corrente di sistema, o ad altra ora specicata, lora di accesso/modica di un le. Viene usata anche per creare un nuovo le. Il comando touch zzz crea un nuovo le vuoto, di nome zzz, nellipotesi che zzz non sia gi esistente. Creare le vuoti, che riportano lora e la data della loro creazione, pu rappresentare un utile sistema per la registrazione del tempo, per esempio per tener traccia delle successive modiche di un progetto.
Nota: Il comando touch equivale a : : >> nuovofile o a >> nuovofile (per i le regolari).
244
Capitolo 12. Filtri, programmi e comandi esterni at Il comando di controllo di job at esegue una data serie di comandi ad unora determinata. Ad uno sguardo superciale, assomiglia a cron. Tuttavia, at viene utilizzato principalmente per eseguire la serie di comandi una sola volta. at 2pm January 15 visualizza un prompt per linserimento della serie di comandi da eseguire a quella data e ora. I comandi dovrebbero essere shell-script compatibili poich, per questioni pratiche, lutente sta digitando una riga alla volta in uno script di shell eseguibile. Linput deve terminare con un Ctl-D. Con luso dellopzione -f o della redirezione dellinput (<), at pu leggere lelenco dei comandi da un le. Questo le uno script di shell eseguibile e, naturalmente, non dovrebbe essere interattivo. Risulta particolarmente intelligente inserire il comando run-parts nel le per eseguire una diversa serie di script.
batch Il comando di controllo di job batch simile ad at, ma esegue lelenco dei comandi quando il carico di sistema cade sotto 0.8. Come at, pu leggere, con lopzione -f, i comandi da un le. cal Visualizza allo stdout un calendario mensile in un formato molto elegante. Pu fare riferimento allanno corrente o ad un ampio intervallo di anni passati e futuri. sleep lequivalente shell di un ciclo wait. Sospende lesecuzione per il numero di secondi indicato. utile per la temporizzazione o per i processi in esecuzione in background che hanno il compito di vericare in continuazione il vericarsi di uno specico evento (polling), come in Esempio 29-6.
sleep 3 # Pausa di 3 secondi.
Nota: Il comando sleep conta, in modo predenito, i secondi. Possono per essere specicati minuti, ore o giorni.
sleep 3 h # Pausa di 3 ore!
Nota: Per lesecuzione di comandi da effettuarsi ad intervalli determinati, il comando watch pu rivelarsi una scelta migliore di sleep.
245
usleep Microsleep (la u deve interpretarsi come la lettera dellalfabeto greco mu, usata come presso per micro). uguale a sleep, visto prima, ma sospende per intervalli di microsecondi. Pu essere impiegato per una temporizzazione pi accurata o per la verica, ad intervalli di frequenza elevati, di un processo in esecuzione.
usleep 30
# Pausa di 30 microsecondi.
Cautela
Il comando usleep non esegue una temporizzazione particolarmente precisa e, quindi, non pu essere impiegato per calcolare il tempo di cicli critici.
hwclock clock Il comando hwclock d accesso e permette di regolare lorologio hardware della macchina. Alcune opzioni richiedono i privilegi di root. Il le di avvio /etc/rc.d/rc.sysinit usa hwclock per impostare, in fase di boot, lora di sistema dallorologio hardware. Il comando clock il sinonimo di hwclock.
246
Capitolo 12. Filtri, programmi e comandi esterni tsort Esegue un ordinamento topologico di stringhe lette in coppia secondo i modelli forniti nellinput. uniq Questo ltro elimina le righe duplicate di un le 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.
Lopzione -c premette ad ogni riga del le di input il numero delle sue occorrenze.
bash$ cat fileprova Questa riga presente Questa riga presente Questa riga presente Questa riga presente Questa riga presente Questa riga presente
fileprova
riga presente una sola volta. riga presente due volte. 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 le FILEINPUT (le opzioni -nr di sort generano un ordinamento numerico inverso). Questo modello viene usato nellanalisi dei le di log e nelle liste dizionario, od ogni volta che necessario esaminare la struttura lessicale di un documento. Esempio 12-11. Analisi di frequenza delle parole
#!/bin/bash # wf.sh: Unanalisi sommaria, su un file di testo, della #+ frequenza delle parole. # una versione pi efficiente dello script "wf2.sh".
247
# 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
248
bash$ cat fileprova Questa riga presente Questa riga presente Questa riga presente Questa riga presente Questa riga presente Questa riga presente
bash$ ./wf.sh fileprova 6 riga 6 questa 6 presente 6 5 volte 3 tre 2 due 1 volta 1 una 1 sola
expand unexpand Il ltro expand trasforma le tabulazioni in spazi. spesso usato in una pipe. Il ltro unexpand trasforma gli spazi in tabulazioni. Esegue lazione opposta di expand.
cut Strumento per estrarre i campi dai le. 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 - eld specier). Usare cut per ottenere lelenco dei lesystem montati:
cut -d -f1,2 /etc/mtab
249
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
-f3,7,19 filetesto
Questa la riga 3 di filetesto. Questa la riga 7 di filetesto. Questa la riga 19 di filetesto.
250
Capitolo 12. Filtri, programmi e comandi esterni paste Strumento per riunire pi le in un unico le impaginato su diverse colonne. In combinazione con cut utile per creare le di log di sistema. join Lo si pu considerare il cugino specializzato di paste. Questa potente utility consente di fondere due le in modo da fornire un risultato estremamente interessante. Crea, in sostanza, una versione semplicata di un database relazionale. Il comando join opera solo su due le, ma unisce soltanto quelle righe che possiedono una corrispondenza di campo comune (solitamente unetichetta numerica) e visualizza il risultato allo stdout. I le che devono essere uniti devono essere anche ordinati in base al campo comune, se si vuole che labbinamento delle righe avvenga correttamente.
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
251
Capitolo 12. Filtri, programmi e comandi esterni head visualizza la parte iniziale di un le allo stdout (il numero di righe preimpostato 10, il valore pu essere modicato). Possiede un certo numero di opzioni interessanti. Esempio 12-12. Quali le sono degli script?
#!/bin/bash # script-detector.sh: Rileva gli script presenti in una directory. VERCAR=2 INTERPRETE=#! # Verifica i primi 2 caratteri. # Gli script iniziano con "#!".
for file in * # Verifica tutti i file della directory corrente. do if [[ head -c$VERCAR "$file" = "$INTERPRETE" ]] # head -c2 #! # Lopzione -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.
252
# Lautore di questo script spiega lazione di sed come segue. # head -c4 /dev/urandom | od -N4 -tu4 | sed -ne 1s/.* //p # ----------------------------------> | # Assumiamo che loutput 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 1 comandi s/.* //p
Il numero della riga nellintervallo, 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" lopzione del comando "s", ed differente dal comando "p").
# sed ora continua la lettura dellinput. (Notate che prima di continuare, se #+ non fosse stata passata lopzione -n, sed avrebbe visualizzato la riga #+ unaltra 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 lultima). 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
253
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 lopzione "-n".
# ======================================================================= # # Unalternativa ancor pi semplice al precedente script di una sola riga, #+ potrebbe essere: # head -c4 /dev/urandom| od -An -tu4 exit 0
Vedi anche Esempio 12-35. tail visualizza la parte nale di un le allo stdout (il valore preimpostato di 10 righe). Viene comunemente usato per tenere traccia delle modiche al le di log di sistema con luso dellopzione -f, che permette di visualizzare le righe accodate al le. Esempio 12-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
Suggerimento: Per individuare una riga specica in un le di testo, si colleghi con una pipe loutput di head a tail -1. Per esempio head -8 database.txt | tail -1 rintraccia l8 riga del le database.txt. Per impostare una variabile ad un determinato blocco di un le di testo:
var=$(head -$m $nomefile | tail -$n) # nomefile = nome del file # m = dallinizio del file, numero di righe mancanti alla fine del blocco # n = numero di righe a cui va impostata la variabile (dalla fine del blocco)
254
grep Strumento di ricerca multifunzione che fa uso delle Espressioni Regolari. In origine era un comando/ltro del venerabile editor di linea ed: g/re/p -- global - regular expression - print.
grep modello [file...] Ricerca nel/nei le indicato/i loccorrenza di modello, dove modello pu essere o un testo letterale o unEspressione Regolare.
bash$ grep [rst]ystem.$ osinfo.txt The GPL governs the distribution of the Linux operating system.
Se non vengono specicati i le, grep funziona come ltro 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
Lopzione -i abilita una ricerca che non fa distinzione tra maiuscole e minuscole. Lopzione -w verica solo le parole esatte. Lopzione -l elenca solo i le in cui la ricerca ha avuto successo, ma non le righe vericate. Lopzione -r (ricorsivo) ricerca i le nella directory di lavoro corrente e in tutte le sue sottodirectory. Lopzione -n visualizza le righe vericate insieme al loro numero.
bash$ grep -n Linux osinfo.txt 2:This is a file containing information about Linux.
255
# 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 lopzione -z specifica del "grep" di GNU.
# Grazie, S.C.
Quando viene invocato con pi di un le, grep specica qual il le 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.
Suggerimento: Per forzare grep a visualizzare il nome del le quando ne presente soltanto uno, si deve indicare come secondo le /dev/null
bash$ grep Linux osinfo.txt /dev/null osinfo.txt:This is a file containing information about Linux.
256
Se la ricerca ha avuto successo, grep restituisce come exit status 0. Questo lo rende utile per un costrutto di verica in uno script, specialmente in abbinamento con lopzione -q che sopprime loutput.
SUCCESSO=0 parola=Linux file=file.dati # se la ricerca di grep riuscita
grep -q "$parola" "$file" # Lopzione "-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
LEsempio 29-6 dimostra come usare grep per cercare una parola in un le di log di sistema. Esempio 12-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" ] then echo -n "$file: " # Cosa succede se si usa "$output" #+ senza i doppi apici?
257
# 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 le o i le 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 le seguente:
# File: tstfile Questo un file desempio. Questo un file di testo ordinario. Questo file non contiene testo strano. Questo file non insolito. Altro testo.
--
258
Capitolo 12. Filtri, programmi e comandi esterni 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 essibile. fgrep - (fast grep) grep veloce - uguale a grep -F. Esegue la ricerca letterale della stringa (niente espressioni regolari), il che solitamente accelera sensibilmente loperazione.
Nota: In alcune distribuzioni Linux, egrep e fgrep sono link simbolicia, o alias, di grep, invocato per con le opzioni -E e -F, rispettivamente.
Esempio 12-16. Cercare una denizione nel Websters Dictionary ed. 1913
#!/bin/bash # dict-lookup.sh # # #+ #+ # # #+ # # Questo script ricerca delle definizioni nel Websters 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 lalgoritmo 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,"
259
if [ -z "$2" ]
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 lelaborazione. 2) Trasformate lo script in unapplicazione GUI, + usando qualcosa tipo "gdialog" . . . Lo script non ricever pi, di conseguenza, lo/gli argomento(i) + da riga di comando. Modificate lo script per una verifica in uno degli altri Dizionari + di Dominio Pubblico disponibili, quale il U.S. Census Bureau Gazetteer.
3)
260
Capitolo 12. Filtri, programmi e comandi esterni agrep (approximate grep) grep dapprossimazione, estende le capacit di grep per una ricerca per approssimazione. La stringa da ricercare differisce per un numero specico di caratteri dalle occorrenze effettivamente risultanti. Questa utility non , di norma, inclusa in una distribuzione Linux.
Suggerimento: Per la ricerca in le compressi vanno usati i comandi zgrep, zegrep o zfgrep. Sebbene possano essere usati anche con i le non compressi, svolgono il loro compito pi lentamente che non grep, egrep, fgrep. Sono invece utili per la ricerca in una serie di le misti, alcuni compressi altri no. Per la ricerca in le compressi con bzip si usa il comando bzgrep.
look Il comando look opera come grep, ma la ricerca viene svolta in un dizionario, un elenco di parole ordinate. In modo predenito, look esegue la ricerca in /usr/dict/words. Naturalmente si pu specicare un diverso percorso del le dizionario. Esempio 12-17. Vericare 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 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. # File dati le cui parole devono essere controllate.
echo exit 0 # ---------------------------------------------------------------# Le righe di codice seguenti non vengono eseguite a causa del
261
# 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
sed awk Linguaggi di scripting particolarmente adatti per la verica di le di testo e delloutput dei comandi. Possono essere inseriti, singolarmente o abbinati, nelle pipe e negli script di shell. sed Editor di usso non interattivo, consente lutilizzo di molti comandi ex in modalit batch. Viene impiegato principalmente negli script di shell. awk Analizzatore e rielaboratore programmabile di le, ottimo per manipolare e/o localizzare campi (colonne) in le di testo strutturati. Ha una sintassi simile a quella del linguaggio C. wc wc fornisce il numero di parole (word count) presenti in un le o in un usso 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.
262
Capitolo 12. Filtri, programmi e comandi esterni Uso di wc per contare quanti le .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 le i cui nomi iniziano con le lettere comprese nellintervallo d - h.
bash$ wc [d-h]* | grep total | awk {print $3} 71832
Uso di wc per contare le occorrenze della parola Linux nel le sorgente di questo libro.
bash$ grep Linux abs-book.sgml | wc -l 50
Vedi anche Esempio 12-35 e Esempio 16-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 dellopzione "-c" (o "--count") di grep. # Grazie, S.C.
263
Cautela
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 lespansione 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 [**]. Lopzione -d cancella un intervallo di caratteri.
echo "abcdef" echo "abcdef" | tr -d b-d # abcdef # aef
Lopzione --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
Lopzione -c complemento inverte la serie di caratteri da vericare. Con questa opzione, tr agisce soltanto su quei caratteri che non vericano la serie specicata.
264
Capitolo 12. Filtri, programmi e comandi esterni Esempio 12-18. toupper: Trasforma tutte le lettere di un le 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 12-19. lowercase: Modica tutti i nomi dei le della directory corrente in lettere minuscole
#! # # # # #+ #+ /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 dallautore di Guida ABS.
for file in * do fnome=basename $file n=echo $fnome | tr A-Z a-z if [ "$fnome" != "$n" ] then mv $fnome $n fi done exit $?
# #+ # #+
Cambia il nome del file in tutte lettere minuscole. Rinomina solo quei file che non sono gi in minuscolo.
265
# 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 lalternativa 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 $?
266
chiave=ETAOINSHRDLUBCFGJMQPVWZYXK # La "chiave" non nientaltro che lalfabeto rimescolato. # Modificando la "chiave" cambia la cifratura. # # # # Il costrutto cat "$@" permette linput sia dallo stdin che dai file. Se si usa lo stdin, linput 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.
267
# 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.
Nota a margineLe varianti di tr Lutility tr ha due varianti storiche. La versione BSD che non usa le parentesi quadre (tr a-z A-Z), a differenza della versione SysV (tr [a-z] [A-Z]). La versione GNU di tr assomiglia a quella BSD, per cui obbligatorio luso del quoting degli intervalli delle lettere allinterno delle parentesi quadre.
fold Filtro che dimensiona le righe di input ad una larghezza specicata. particolarmente utile con lopzione -s che interrompe le righe in corrispondenza degli spazi tra una parola e laltra (vedi Esempio 12-23 e Esempio A-1). fmt Semplice formattatore di le usato come ltro, in una pipe, per ridimensionare lunghe righe di testo per loutput. Esempio 12-23. Dimensionare un elenco di le
#!/bin/bash AMPIEZZA=40 b=ls /usr/local/bin echo $b | fmt -w $AMPIEZZA # Ampiezza di 40 colonne. # Esegue lelenco dei file...
268
col Questo ltro, dal nome fuorviante, rimuove i cosiddetti line feed inversi dal usso di input. Cerca anche di sostituire gli spazi con caratteri di tabulazione. Luso principale di col quello di ltrare loutput proveniente da alcune utility di elaborazione di testo, come groff e tbl. column Riordina il testo in colonne. Questo ltro trasforma loutput di un testo, che apparirebbe come un elenco, in una graziosa tabella, inserendo caratteri di tabulazione in posizioni appropriate. Esempio 12-24. Utilizzo di column per impaginare un elenco di directory
#!/bin/bash # Lesempio 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". # Lopzione -t di "column" visualizza loutput in forma tabellare. exit 0
colrm Filtro per la rimozione di colonne. Elimina le colonne (caratteri) da un le. Il risultato viene visualizzato allo stdout. colrm 2 4 <nomefile cancella dal secondo no al quarto carattere di ogni riga del le di testo nomefile.
269
Cautela
Se il le contiene caratteri non visualizzabili, o di tabulazione, il risultato potrebbe essere imprevedibile. In tali casi si consideri luso, in una pipe, dei comandi expand e unexpand posti prima di colrm.
nl Filtro per lenumerazione delle righe. nl nomefile visualizza nomele allo stdout inserendo, allinizio di ogni riga non vuota, il numero progressivo. Se nomele viene omesso, lazione viene svolta sullo stdin.. Loutput di nl assomiglia molto a quello di cat -n, tuttavia, in modo predenito, nl non visualizza le righe vuote. Esempio 12-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 #-----------------------------------------------------------------
pr Filtro di formato di visualizzazione. Impagina i le (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 laltro, abbinare e numerare le righe, impostare i margini, aggiungere intestazioni ed unire le. Il comando pr riunisce molte delle funzionalit di nl, paste, fold, column e expand.
270
Capitolo 12. Filtri, programmi e comandi esterni pr -o 5 --width=65 fileZZZ | more visualizza sullo schermo una piacevole impaginazione del contenuto del le fileZZZ con i margini impostati a 5 e 65. Lopzione -d particolarmente utile per forzare la doppia spaziatura (stesso effetto di sed -G).
gettext 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.
msgfmt Programma per generare cataloghi di messaggi in formato binario. Viene utilizzato per la localizzazione. iconv Utility per cambiare la codica (set di caratteri) del/dei le. 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)
recode Va considerato come la versione pi elaborata del precedente iconv. Questa versatile utility, usata per modicare la codica di un le, non fa parte dellinstallazione standard di Linux. TeX gs TeX e Postscript sono linguaggi per la composizione di testo usati per preparare copie per la stampa o per la visualizzazione a video.
271
Capitolo 12. Filtri, programmi e comandi esterni TeX lelaborato 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) linterprete Postscript rilasciato sotto licenza GPL.
enscript Utility per la conversione in PostScript di un le in formato testo. Per esempio, enscript nomele.txt -p nomele.ps d come risultato il le PostScript nomefile.ps.
groff tbl eqn Un altro linguaggio di composizione e visualizzazione formattata di testo groff. la versione GNU, migliorata, dellormai venerabile pacchetto UNIX roff/troff. Le pagine di manuale utilizzano groff. Lutility per lelaborazione 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 lutility per lelaborazione 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 12-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
272
lex yacc Lanalizzatore lessicale lex genera programmi per la verica doccorrenza. Sui sistemi Linux stato sostituito dal programma non proprietario ex. Lutility yacc crea un analizzatore lessicale basato su una serie di speciche. Sui sistemi Linux stato sostituito dal non proprietario bison.
273
Cautela
Questa opzione non funziona sui dispositivi a nastro magnetico.
4. -r accoda (le ad un archivio esistente) 5. -A accoda (le tar ad un archivio esistente) 6. -t elenca (il contenuto di un archivio esistente) 7. -u aggiorna larchivio 8. -d confronta larchivio con un lesystem specicato 9. -z usa gzip sullarchivio (lo comprime o lo decomprime in base allabbinamento con lopzione -c o -x) 10. -j comprime larchivio con bzip2
Cautela
Poich potrebbe essere difcile ripristinare dati da un archivio tar compresso con gzip consigliabile, per larchiviazione di le importanti, eseguire salvataggi (backup) multipli.
shar Utility di archiviazione shell. I le di un archivio shell vengono concatenati senza compressione. Quello che risulta essenzialmente uno script di shell, completo di intestazione #!/bin/sh e contenente tutti i necessari comandi di ripristino. Gli archivi shar fanno ancora la loro comparsa solo nei newsgroup Internet, dal momento che shar stata sostituita molto bene da tar/gzip. Il comando unshar ripristina gli archivi shar. ar Utility per la creazione e la manipolazione di archivi, usata principalmente per le librerie di le oggetto binari. rpm Il Red Hat Package Manager, o utility rpm, un gestore per archivi binari o sorgenti. Tra gli altri, comprende comandi per linstallazione e la verica dellintegrit dei pacchetti.
274
Capitolo 12. Filtri, programmi e comandi esterni Un semplice rpm -i nome_pacchetto.rpm di solito sufciente per installare un pacchetto, sebbene siano disponibili molte pi opzioni.
Suggerimento: rpm -qf identica il pacchetto che ha fornito un determinato le.
bash$ rpm -qf /bin/ls coreutils-5.2.1-31
Suggerimento: rpm -qa fornisce lelenco completo dei pacchetti rpm installati su un sistema. rpm -qa nome_pacchetto elenca solo il pacchetto corrispondente a nome_pacchetto.
bash$ rpm -qa redhat-logos-1.1.3-1 glibc-2.2.4-13 cracklib-2.7-12 dosfstools-2.7-1 gdbm-1.8.0-10 ksymoops-2.4.1-1 mktemp-1.5-11 perl-5.6.0-17 reiserfs-utils-3.x.0j-2 ...
bash$ rpm -qa docbook | grep docbook docbook-dtd31-sgml-1.0-10 docbook-style-dsssl-1.64-3 docbook-dtd30-sgml-1.0-10 docbook-dtd40-sgml-1.0-11 docbook-utils-pdf-0.6.9-2 docbook-dtd41-sgml-1.0-10 docbook-utils-0.6.9-2
cpio Comando specializzato per la copia di archivi (copy input and output), si incontra molto raramente, essendo stato soppiantato da tar/gzip. Le sue funzionalit, comunque, vengono ancora utilizzate, ad esempio per spostare una directory.
275
Capitolo 12. Filtri, programmi e comandi esterni Esempio 12-27. Utilizzo di cpio per spostare una directory
#!/bin/bash # Copiare una directory usando cpio. # Vantaggi delluso di cpio: # Velocit nella copia. Con le pipe pi veloce di tar. # Adatto per la copia di file speciali (named pipe, ecc.) #+ dove cp potrebbe fallire. ARG=2 E_ERR_ARG=65 if [ $# -ne "$ARG" ] then echo "Utilizzo: basename $0 directory_origine exit $E_ERR_ARG fi origine=$1 destinazione=$2 find "$origine" -depth | cpio -admvp "$destinazione" # ^^^^^ ^^^^^ # Leggete le pagine di manuale di find e cpio per decifrare queste opzioni.
directory_destinazione"
# Esercizio: # --------# Aggiungete del codice per verificare lexit status ($?) della pipe #+ find | cpio e che visualizzi degli appropriati messaggi derrore nel caso #+ qualcosa non abbia funzionato correttamente. exit 0
rpm2cpio Questo comando crea un archivio cpio da un archivio rpm. Esempio 12-28. Decomprimere un archivio rpm
#!/bin/bash # de-rpm.sh: Decomprime un archivio rpm : ${1?"Utilizzo: basename $0 file_archivio"} # Bisogna specificare come argomento un archivio rpm.
TEMPFILE=$$.cpio
276
Compressione
gzip Utility di compressione standard GNU/UNIX che ha sostituito la meno potente e proprietaria compress. Il corrispondente comando di decompressione gunzip, equivalente a gzip -d. Il ltro zcat decomprime un le compresso con gzip allo stdout, come input per una pipe o una redirezione. In effetti, il comando cat che agisce sui le compressi (compresi quelli ottenuti con la vecchia utility compress). Il comando zcat equivale a gzip -dc.
Cautela
Su alcuni sistemi commerciali UNIX, zcat il sinonimo di uncompress -c, di conseguenza non funziona su le compressi con gzip.
bzip2 Utility di compressione alternativa, pi efciente (ma pi lenta) di gzip, specialmente con le di ampie dimensioni. Il corrispondente comando di decompressione bunzip2.
Nota: Le versioni pi recenti di tar sono state aggiornate per supportare bzip2.
compress uncompress la vecchia utility proprietaria di compressione presente nelle distribuzioni commerciali UNIX. stata ampiamente sostituita dalla pi efciente gzip . Le distribuzioni Linux includono, di solito,
277
Capitolo 12. Filtri, programmi e comandi esterni compress per ragioni di compatibilit, sebbene gunzip possa decomprimere i le trattati con compress.
Suggerimento: Il comando znew trasforma i le dal formato compress al formato gzip.
sq Altra utility di compressione. un ltro che opera solo su elenchi di parole ASCII ordinate. Usa la sintassi standard dei ltri, sq < le-input > le-output. Veloce, ma non cos efciente come gzip. Il corrispondente ltro di decompressione unsq, con la stessa sintassi di sq.
Suggerimento: Loutput di sq pu essere collegato per mezzo di una pipe a gzip per una ulteriore compressione.
zip unzip Utility di archiviazione e compressione multipiattaforma, compatibile con il programma DOS pkzip.exe. Gli archivi zippati sembrano rappresentare, su Internet, il mezzo di scambio pi gradito rispetto ai tarball. unarc unarj unrar Queste utility Linux consentono di decomprimere archivi compressi con i programmi DOS arc.exe, arj.exe e rar.exe.
Informazioni sui le
le Utility per identicare i tipi di le. Il comando file nome_file restituisce la specica di nome_file, come ascii text o data. Fa riferimento ai magic number che si trovano in /usr/share/magic, /etc/magic o /usr/lib/magic, secondo le distribuzioni Linux/UNIX. Lopzione -f esegue le in modalit batch, per leggere lelenco dei le contenuto nel le indicato. Lopzione -z tenta di vericare il formato e le caratteristiche dei le compressi.
bash$ file test.tar.gz test.tar.gz: gzip compressed data, deflated, last modified: Sun Sep 16 13:34:51 2001, os: Unix
bash file -z test.tar.gz test.tar.gz: GNU tar archive (gzip compressed data, deflated, last modified: Sun Sep 16 13:34:51 2
278
# Ricerca gli script sh e Bash in una data directory: DIRECTORY=/usr/local/bin PAROLACHIAVE=Bourne # Script di shell Bourne e Bourne-Again file $DIRECTORY/* | fgrep $PAROLACHIAVE # Risultato: # # # # # /usr/local/bin/burn-cd: /usr/local/bin/burnit: /usr/local/bin/cassette.sh: /usr/local/bin/copy-cd: . . . Bourne-Again Bourne-Again Bourne shell Bourne-Again shell script text executable shell script text executable script text executable shell script text executable
# Verifica il corretto tipo di file. tipo=file $1 | awk { print $2, $3, $4, $5 } # "file $1" restituisce nome e tipo di file . . . # quindi awk rimuove il primo campo, il nome . . . # Dopo di che il risultato posto nella variabile "tipo". tipo_corretto="ASCII C program text" if [ "$tipo" != "$tipo_corretto" ] then echo echo "Questo script funziona solo su file sorgenti C." echo exit $E_TIPO_FILE_ERRATO fi
279
# necessario aggiungere ancora una riga allo script sed per trattare #+ quei casi in cui una riga di codice seguita da un commento. # Questo viene lasciato come esercizio (niente affatto banale). # Ancora, il codice precedente cancella anche le righe con un "*/" o "/*" #+ che non sono commenti, il che non un risultato desiderabile. exit 0
# ---------------------------------------------------------------------------# Il codice oltre la linea non viene eseguito a causa del precedente exit 0. # Stephane Chazelas suggerisce la seguente alternativa: utilizzo() { echo "Utilizzo: basename $0 file-C" >&2 exit 1 } STRANO=echo -n -e \377 # oppure STRANO=$\377 [[ $# -eq 1 ]] || utilizzo case file "$1" in *"C program text"*) sed -e "s%/\*%${STRANO}%g;s%\*/%${STRANO}%g" "$1" \ | tr \377\n \n\377 \ | sed -ne p;n \ | tr -d \n | tr \377 \n;; *) utilizzo;; esac # # # # # # #+ #+ Questo pu ancora essere ingannato da occorrenze come: printf("/*"); o /* /* errato commento annidato */ Per poter gestire tutti i casi particolari (commenti in stringhe, commenti in una stringa in cui presente \", \\" ...) lunico modo scrivere un parser C (usando, forse, lex o yacc?).
exit 0
280
Capitolo 12. Filtri, programmi e comandi esterni which which comando-xxx restituisce il percorso di comando-xxx. utile per vericare se un particolare comando o utility installato sul sistema. $bash which rm
/usr/bin/rm
whereis Simile al precedente which, whereis comando-xxx restituisce il percorso di comando-xxx ed anche della sua pagina di manuale. $bash whereis rm
rm: /bin/rm /usr/share/man/man1/rm.1.bz2
whatis whatis lexxx ricerca lexxx nel database whatis. utile per identicare i comandi di sistema e i le di congurazione. Pu essere considerato una semplicazione del comando man. $bash whatis whatis
whatis (1) - search the whatis database for complete words
281
vdir Visualizza lelenco dettagliato delle directory. Leffetto simile a ls -l. Questa una delle leutils GNU.
4034 Jul 18 22:04 data1.xrolo 4602 May 25 13:58 data1.xrolo.bak 877 Dec 17 2000 employment.xrolo
4034 Jul 18 22:04 data1.xrolo 4602 May 25 13:58 data1.xrolo.bak 877 Dec 17 2000 employment.xrolo
locate slocate Il comando locate esegue la ricerca dei le usando un database apposito. Il comando slocate la versione di sicurezza di locate (che pu essere lalias di slocate). $bash locate hickson
/usr/lib/xephem/catalogs/hickson.edb
282
Capitolo 12. Filtri, programmi e comandi esterni strings Il comando strings viene usato per cercare le stringhe visualizzabili in un le dati o binario. Elenca le sequenze di caratteri trovate nel le di riferimento. E utile per un esame rapido e sommario di un le core di scarico della memoria o per dare unocchiata ad un le di immagine sconosciuto (strings file-immagine | more potrebbe restituire qualcosa come JFIF che indica un le graco jpeg). In uno script, si pu controllare loutput di strings con grep o sed. Vedi Esempio 10-7 e Esempio 10-9. Esempio 12-31. Un comando strings migliorato
#!/bin/bash # wstrings.sh: "word-strings" (comando "strings" migliorato) # # Questo script filtra loutput di "strings" confrontandolo #+ con un file dizionario. # In questo modo viene eliminato efficacemente il superfluo, #+ restituendo solamente le parole riconosciute. # ==================================================================== # Verifica standard del/degli argomento/i dello script ARG=1 E_ERR_ARG=65 E_NOFILE=66 if [ $# -ne $ARG ] then echo "Utilizzo: basename $0 nomefile" exit $E_ERR_ARG fi if [ ! -f "$1" ] # Verifica lesistenza del file. then echo "Il file \"$1\" non esiste." exit $E_NOFILE fi # ====================================================================
LUNMINSTR=3 # DIZIONARIO=/usr/share/dict/linux.words # # #+ #+
Lunghezza minima della stringa. File dizionario. Pu essere specificato un file dizionario diverso purch di una parola per riga.
elenco=strings "$nome_file" | tr A-Z a-z | tr [:space:] Z | \ tr -cs [:alpha:] Z | tr -s \173-\377 Z | tr Z # # # # Modifica loutput del comando strings mediante diversi passaggi a tr. "tr A-Z a-z" trasforma le lettere maiuscole in minuscole. "tr [:space:] Z" trasforma gli spazi in Z. "tr -cs [:alpha:] Z" trasforma i caratteri non alfabetici in Z,
283
# # # #
Importante: non bisogna usare $elenco col quoting. "$elenco" non funziona. Perch no?
do lunstr=${#parola} if [ "$lunstr" -lt "$LUNMINSTR" ] then continue fi grep -Fw $parola "$DIZIONARIO" ^^^ # Cerca solo le parole complete. # Opzioni "stringhe Fisse" e #+ "parole (words) intere". # Lunghezza della stringa. # Salta le stringhe con meno #+ di 3 caratteri.
done
exit $?
Confronti
diff patch diff: essibile utility per il confronto di le. Confronta i le di riferimento riga per riga, sequenzialmente. In alcune applicazioni, come nei confronti di dizionari, vantaggioso ltrare i le di riferimento con sort e uniq prima di collegarli tramite una pipe a diff. diff file-1 file-2 visualizza le righe dei le che differiscono, con le parentesi acute ad indicare a quale le ogni particolare riga appartiene. Lopzione --side-by-side di diff visualizza riga per riga, in colonne separate, ogni le confrontato con un segno indicante le righe non coincidenti. Le opzioni -c e -u, similmente, rendono pi facile linterpretazione delloutput del comando.
284
Capitolo 12. Filtri, programmi e comandi esterni Sono disponibili diversi front-end per diff, quali sdiff, wdiff, xdiff e mgdiff.
Suggerimento: Il comando diff restituisce exit status 0 se i le confrontati sono identici, 1 in caso contrario. Questo consente di utilizzare diff per un costrutto di verica in uno script di shell (vedi oltre).
Luso pi comune di diff quello per creare le di differenze da utilizzare con patch. Lopzione -e produce script idonei allutilizzo con leditor ed o ex. patch: essibile utility per gli aggiornamenti. Dato un le di differenze prodotto da diff, patch riesce ad aggiornare un pacchetto alla versione pi recente. molto pi conveniente distribuire un le di differenze, di dimensioni relativamente minori, che non lintero pacchetto aggiornato. Il patching del kernel diventato il metodo preferito per la distribuzione delle frequenti release del kernel Linux.
patch -p1 <file-patch # Prende tutte le modifiche elencate in file-patch # e le applica ai file che sono specificati in "file-patch". # Questo esegue laggiornamento del pacchetto alla versione pi recente.
cd /usr/src gzip -cd patchXX.gz # Aggiornamento dei # Dal file "README" # di autore anonimo
| patch -p0 sorgenti del kernel usando patch. della documentazione del kernel Linux, (Alan Cox?).
Nota: Il comando diff riesce anche ad eseguire un confronto ricorsivo tra directory (sui le in esse contenuti).
bash$ diff -r ~/notes1 ~/notes2 Only in /home/bozo/notes1: file02 Only in /home/bozo/notes1: file03 Only in /home/bozo/notes2: file04
285
diff3 Versione estesa di diff che confronta tre le alla volta. Questo comando restituisce, come exit status, 0 in caso di successo, ma sfortunatamente non fornisce alcuna informazione sui risultati del confronto.
sdiff Confronta e/o visualizza due le con lo scopo di unirli in un unico le. A causa della sua natura interattiva, difcile che questo comando venga impiegato negli script. cmp Il comando cmp la versione pi semplice di diff. Mentre diff elenca le differenze tra i due le, cmp mostra semplicemente i punti in cui differiscono.
Nota: Come diff, cmp restituisce exit status 0 se i le confrontati sono identici, 1 in caso contrario. Questo ne consente limpiego per un costrutto di verica in uno script di shell.
286
if [ $? -eq 0 ] # Verifica lexit status del comando "cmp". then echo "Il file \"$1\" identico al file \"$2\"." else echo "Il file \"$1\" diverso dal file \"$2\"." fi exit 0
comm Versatile utility per il confronto di le. I le da confrontare devono essere ordinati. comm -opzioni primo-file secondo-file comm file-1 file-2 visualizza il risultato su tre colonne:
colonna 1 = righe uniche appartenenti a file-1 colonna 2 = righe uniche appartenenti a file-2 colonna 3 = righe comuni ad entrambi i le.
sopprime la colonna 1 sopprime la colonna 2 sopprime la colonna 3 sopprime entrambe le colonne 1 e 2, ecc.
-12
287
Utility
basename Elimina il percorso del le, visualizzando solamente il suo nome. Il costrutto basename $0 permette allo script di conoscere il proprio nome, vale a dire, il nome con cui stato invocato. Si pu usare per i messaggi di utilizzo se, per esempio, uno script viene eseguito senza argomenti:
echo "Utilizzo: basename $0 arg1 arg2 ... argn"
dirname Elimina basename, dal nome del le, visualizzando solamente il suo percorso.
Nota: basename e dirname possono operare su una stringa qualsiasi. Non necessario che largomento si riferisca ad un le esistente e neanche essere il nome di un le (vedi Esempio A-7).
split csplit Utility per suddividere un le in porzioni di dimensioni minori. Sono solitamente impiegate per suddividere le di grandi dimensioni allo scopo di eseguirne il salvataggio su oppy disk, per linvio tramite e-mail o per effettuarne lupload su un server. Il comando csplit suddivide il le in base ad un dato criterio. La suddivisione viene eseguita nei punti in cui i modelli sono vericati.
288
Capitolo 12. Filtri, programmi e comandi esterni sum cksum md5sum sha1sum Sono utility per creare le checksum. Una checksum un numero ricavato con un calcolo matematico eseguito sul contenuto di un le, con lo scopo di vericarne lintegrit. Uno script potrebbe vericare un elenco di checksum a ni di sicurezza, per esempio per assicurarsi che il contenuto di indispensabili le di sistema non sia stato modicato o corrotto. Per applicazioni di sicurezza, si dovrebbe utilizzare il comando md5sum a 128-bit (message digest 5 checksum) o, ancor meglio, il nuovissimo sha1sum (Secure Hash Algorithm).
bash$ cksum /boot/vmlinuz 1670054224 804083 /boot/vmlinuz bash$ echo -n "Top Secret" | cksum 3391003827 10
bash$ md5sum /boot/vmlinuz 0f43eccea8f09e0a0b2b5cf1dcf333ba /boot/vmlinuz bash$ echo -n "Top Secret" | md5sum 8babc97a6f62a4649716f4df8d61728f -
Nota: Il comando cksum visualizza anche la dimensione, in byte, del suo riferimento. sia esso un le o lo stdout. I comandi md5sum e sha1sum visualizzano un trattino quando linput proviene dallo stdout.
289
if [ "${record[n]}" = "${checksum[n]}" ] then echo "${nomefile[n]} non stato modificato." elif [ "basename ${nomefile[n]}" != "$dbfile" ] # Salta il database delle checksum, #+ perch cambia ad ogni invocazione dello script. # --# Questo significa, purtroppo, che quando si esegue #+ lo script su $PWD, la coincidenza con il #+ file database delle checksum non viene rilevata. # Esercizio: Risolvete questo problema.
290
clear # Pulisce lo schermo. echo " In esecuzione il controllo dellintegrit dei file in $directory" echo # ------------------------------------------------------------------- # if [ ! -r "$dbfile" ] # Occorre creare il database? then echo "Sto creando il database, \""$directory"/"$dbfile"\"."; echo crea_database fi # ------------------------------------------------------------------- # verifica_database echo # Sarebbe desiderabile redirigere lo stdout dello script in un file, #+ specialmente se la directory da verificare contiene molti file. exit 0 # Per una verifica dintegrit molto pi approfondita, #+ considerate limpiego del pacchetto "Tripwire", #+ http://sourceforge.net/projects/tripwire/. # Esegue il lavoro di verifica.
Vedi anche Esempio A-19 e Esempio 33-14 per un uso creativo del comando md5sum.
291
filetesto
shred Cancella in modo sicuro un le, sovrascrivendolo diverse volte con caratteri casuali prima di cancellarlo denitivamente. Questo comando ha lo stesso effetto di Esempio 12-55, ma esegue il compito in maniera pi completa ed elegante. Questa una delle leutils GNU.
Cautela
Tecniche di indagine avanzate potrebbero essere ancora in grado di recuperare il contenuto di un le anche dopo luso di shred.
Codica e Cifratura
uuencode Questa utility codica i le binari in caratteri ASCII, rendendoli disponibili per la trasmissione nel corpo di un messaggio e-mail o in un post di newsgroup. uudecode Inverte la codica, ripristinando i le binari codicati con uuencode al loro stato originario. Esempio 12-35. Decodicare le
#!/bin/bash # Decodifica con uudecode tutti i file della directory di lavoro corrente #+ cifrati con uuencode. righe=35 for File in * # 35 righe di intestazione (molto generoso). # Verifica tutti i file presenti in $PWD.
292
# Notate che se si invoca questo script su se stesso, lesecuzione ingannata #+ perch pensa di trovarsi in presenza di un file codificato con uuencode, #+ poich contiene sia "begin" che "end". # Esercizio: # Modificate lo script per verificare in ogni file lintestazione di un #+ newsgroup, saltando al file successivo nel caso non venga trovata. exit 0
Suggerimento: Il comando fold -s pu essere utile (possibilmente in una pipe) per elaborare messaggi di testo di grandi dimensioni, decodicati con uudecode, scaricati dai newsgroup Usenet.
mimencode mmencode I comandi mimencode e mmencode elaborano gli allegati e-mail nei formati di codica MIME. Sebbene i gestori di e-mail (mail user agents come pine o kmail) siano normalmente in grado di gestirli automaticamente, queste particolari utility consentono di manipolare tali allegati manualmente, da riga di comando, o in modalit batch per mezzo di uno script di shell. crypt Una volta questa era lutility standard UNIX di cifratura di le. 6 Regolamenti governativi (USA N.d.T.), attuati per ragioni politiche, che proibiscono lesportazione di software crittograco, hanno portato alla scomparsa di crypt da gran parte del mondo UNIX, nonch dalla maggioranza delle distribuzioni Linux. Per fortuna i programmatori hanno prodotto molte alternative decenti, tra le quali cruft (ftp://metalab.unc.edu/pub/Linux/utils/le/cruft-0.2.tar.gz) realizzata proprio dallautore del libro (vedi Esempio A-4).
293
Miscellanea
mktemp Crea un le temporaneo 7 con nome univoco. Invocata da riga di comando senza alcun argomento, crea un le vuoto (lunghezza zero) nella directory /tmp.
PREFISSO=nomefile tempfile=mktemp $PREFISSO.XXXXXX # ^^^^^^ Occorrono almeno 6 posti per #+ il suffisso del nome del file. # Se non viene indicato nessun nome di file, #+ viene usato "tmp.XXXXXXXXXX" come nome di default. echo "nome del file temporaneo = $tempfile" # nome del file temporaneo = nomefile.QA2ZpY # o qualcosa del genere... # #+ # # Crea un file con quel nome nella directory di lavoro corrente con impostazione dei permessi a 600. "umask 177" diventa, quindi, inutile, sebbene il suo uso sia sempre una buona pratica di programmazione.
make Utility per costruire e compilare pacchetti binari. Pu anche essere usata per una qualsiasi serie di operazioni che devono essere eseguite a seguito di successive modiche nei le sorgenti.
Il comando make verica Makefile, che un elenco di dipendenze ed operazioni che devono essere svolte.
install Comando speciale per la copia di le. simile a cp, ma in grado di impostare i permessi e gli attributi dei le copiati. Questo comando sembra fatto su misura per linstallazione di pacchetti software e come tale appare frequentemente nei Makefile (nella sezione make install). Potrebbe essere usato allo stesso modo in script dinstallazione.
294
Capitolo 12. Filtri, programmi e comandi esterni dos2unix Questa utility, scritta da Benjamin Lin e collaboratori, converte i le di testo in formato DOS (righe che terminano con CR-LF) nel formato UNIX (righe che terminano con il solo LF), e viceversa. ptx Il comando ptx [le-indicato] produce un indice permutato (elenco a riferimento incrociato) del le. Questo, se necessario, pu essere successivamente ltrato e ordinato in una pipe. more less Comandi per visualizzare un le, o un usso, di testo allo stdout, una schermata alla volta. Possono essere usati per ltrare loutput dello stdout . . . o di uno script. Unapplicazione interessante di more la verica preventiva di una sequenza di comandi, per prevenire conseguenze potenzialmente spiacevoli.
ls /home/bozo | awk {print "rm -rf " $1} | more # ^^^^ # Verifica leffetto della seguente (disastrosa) riga di comando: # ls /home/bozo | awk {print "rm -rf " $1} | sh # Evita lesecuzione da parte della shell . . . ^^
Informazioni e statistiche
host Cerca informazioni su un host Internet per mezzo del nome o dellindirizzo IP usando il DNS.
295
Capitolo 12. Filtri, programmi e comandi esterni ipcalc Visualizza informazioni su un indirizzo IP. Con lopzione -h, ipcalc esegue una ricerca DNS inversa, per trovare il nome dellhost (server) a partire dallindirizzo IP.
nslookup Esegue la risoluzione del nome del server di un host Internet per mezzo dellindirizzo IP. Essenzialmente equivale a ipcalc -h o dig -x. Il comando pu essere eseguito sia in modalit interattiva che non, vale a dire allinterno di uno script. Il comando nslookup stato immotivatamente deprecato, ma viene ancora utilizzato.
bash$ nslookup -sil 66.97.104.180 nslookup kuhleersparnis.ch Server: 135.116.137.2 Address: 135.116.137.2#53 Non-authoritative answer: Name: kuhleersparnis.ch
dig Domain Information Groper. Simile a nslookup, esegue una risoluzione del nome del server Internet. Pu essere eseguito sia in modalit interattiva che non, vale a dire in uno script. Alcune interessanti opzioni di dig sono: +time=N per impostare la temporizzazione della ricerca a N secondi, +nofail per far proseguire linterrogazione dei server nch non si sia ottenuta una risposta e -x per effettuare una risoluzione inversa. Si confronti loutput di dig -x con ipcalc -h e nslookup.
bash$ dig -x 81.9.6.2 ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 11649 ;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 0
296
IN
PTR
IN
SOA
ns.eltel.net. noc.eltel.net.
Query time: 537 msec SERVER: 135.116.137.2#53(135.116.137.2) WHEN: Wed Jun 26 08:35:24 2002 MSG SIZE rcvd: 91
dig +short $1.contacts.abuse.net -c in -t txt # Provate anche: # dig +nssearch $1 # Cerca di trovare gli "authoritative name server" #+ visualizzando i record SOA. * # Funziona anche il seguente: # whois -h whois.abuse.net $1 # ^^ ^^^^^^^^^^^^^^^ Specifica lhost. # In questo modo possono essere rintracciati pi spammer, es." # whois -h whois.abuse.net $dominiospam1 $dominiospam2 . . .
# # # #+ #+ #
Esercizio: --------Espandete la funzionalit dello script in modo che invii automaticamente una notifica via e-mail al/i indirizzo/i del responsabile dellISP. Suggerimento: usate il comando "mail".
297
# Per una versione pi elaborata di questo script, #+ vedi la pagina home di SpamViz , http://www.spamviz.net/index.html. # * [N.d.T.] # Record SOA (Start of Authority). ii record che contiene informazioni #+ sulla zona e indica che il server "autoritativo" per quella zona.
298
299
300
# 3) Sostituite la "codifica" dei server BHL* con delle variabili generiche. # 4) Impostate una temporizzazione per lo script usando lopzione "+time=" del comando dig. # * Black Hole Lists - Elenchi dei server di istradamento e-mail aperti che, #+ come tali, sono utilizzati dagli spammer [N.d.T.].
Per unancor pi elaborata versione dello script precedente, vedi Esempio A-27.
traceroute Traccia il percorso intrapreso dai pacchetti inviati ad un host remoto. Questo comando funziona su una LAN, una WAN o su Internet. Lhost remoto deve essere specicato per mezzo di un indirizzo IP. Loutput pu essere ltrato da grep o sed in una pipe.
bash$ traceroute 81.9.6.2 traceroute to 81.9.6.2 (81.9.6.2), 30 hops max, 38 byte packets 1 tc43.xjbnnbrb.com (136.30.178.8) 191.303 ms 179.400 ms 179.767 ms 2 or0.xjbnnbrb.com (136.30.178.1) 179.536 ms 179.534 ms 169.685 ms 3 192.168.11.101 (192.168.11.101) 189.471 ms 189.556 ms * ...
ping Trasmette un pacchetto ICMP ECHO_REQUEST ad unaltra macchina, sia su rete locale che remota. uno strumento diagnostico per vericare le connessioni di rete e dovrebbe essere usato con cautela. Un ping che ha avuto successo restituisce exit status 0. Questo pu essere vericato in uno script.
bash$ ping localhost PING localhost.localdomain (127.0.0.1) from 127.0.0.1 : 56(84) bytes of data. 64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=0 ttl=255 time=709 usec 64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=1 ttl=255 time=286 usec --- localhost.localdomain ping statistics --2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max/mdev = 0.286/0.497/0.709/0.212 ms
301
Capitolo 12. Filtri, programmi e comandi esterni whois Esegue una ricerca DNS (Domain Name System). Lopzione -h consente di specicare quale particolare server whois devessere interrogato. Vedi Esempio 4-6 e Esempio 12-36. nger Rintraccia informazioni sugli utenti di una rete. Opzionalmente, il comando pu visualizzare i le ~/.plan, ~/.project e ~/.forward di un utente, se presenti.
bash$ finger Login Name bozo Bozo Bozeman bozo Bozo Bozeman bozo Bozo Bozeman
Office
Office Phone
bash$ finger bozo Login: bozo Directory: /home/bozo Office: 2355 Clown St., 543-1234 On since Fri Aug 31 20:13 (MST) on On since Fri Aug 31 20:13 (MST) on On since Fri Aug 31 20:13 (MST) on On since Fri Aug 31 20:31 (MST) on No mail. No Plan.
Name: Bozo Bozeman Shell: /bin/bash tty1 pts/0 pts/1 pts/2 1 hour 38 minutes idle 12 seconds idle 1 hour 16 minutes idle
Tralasciando considerazioni sulla sicurezza, molte reti disabilitano nger ed il demone ad esso associato. 8
chfn Modica le informazioni rivelate dal comando nger. vrfy Verica un indirizzo e-mail Internet.
302
Capitolo 12. Filtri, programmi e comandi esterni sz rz La serie di comandi sz e rz serve a trasferire le a e da un host remoto utilizzando il protocollo zmodem. Zmodem possiede alcuni vantaggi rispetto a xmodem, come una maggiore velocit di trasmissione e di ripresa di trasferimenti interrotti. Come sx e rx, generalmente sono compresi in un pacchetto comunicazioni. ftp Utility e protocollo per caricare/scaricare le su o da un host remoto. Una sessione ftp pu essere automatizzata in uno script (vedi Esempio 17-6, Esempio A-4 ed Esempio A-13). uucp uux cu uucp: UNIX to UNIX copy - copia da UNIX a UNIX. un pacchetto per comunicazioni che permette il trasferimento di le tra server UNIX. Uno script di shell rappresenta un modo efcace per gestire una sequenza di comandi uucp. Con lavvento di Internet e della e-mail, uucp sembra essere precipitato nel dimenticatoio, ma esiste ancora e rimane perfettamente funzionante nelle situazioni in cui una connessione Internet non adatta o non disponibile. --uux: UNIX to UNIX execute - esecuzione da UNIX a UNIX. Esegue un comando su un sistema remoto. Fa parte del pacchetto uucp. --cu: chiama (Call U p) un sistema remoto e si connette come semplice terminale. una specie di versione inferiore di telnet. Anche questo comando fa parte del pacchetto uucp.
Cautela
Nel protocollo telnet sono presenti falle inerenti alla sicurezza e, quindi, dovrebbe essere evitato.
303
Capitolo 12. Filtri, programmi e comandi esterni wget Lutility wget rintraccia o scarica in modo non-interattivo le dal Web o da un sito ftp. Funziona bene in uno script.
wget -p http://www.xyz23.com/file01.html # Lopzione -p o --page-requisite consente a wget di rintracciare tutti i file #+ necessari per visualizzare la pagina specificata. wget -r ftp://ftp.xyz24.net/~bozo/project_files/ -O $SAVEFILE # Segue lopzione di ricorsivit -r che recupera tutti i link #+ presenti sul sito specificato.
E_NOPARAM=66 if [ -z "$1" ] # Si deve specificare il titolo (sigla) da cercare. then echo "Utilizzo: basename $0 codice_titolo" exit $E_NOPARAM fi codice_titolo=$1 suffisso_file=.html # Cerca un file HTML, per cui bisogna usare un nome appropriato. URL=http://finance.yahoo.com/q?s= # Servizio finanziario di Yahoo, con suffisso di ricerca del titolo. # -------------------------------------------------------------wget -O ${codice_titolo}${suffisso_file} "${URL}${codice_titolo}" # --------------------------------------------------------------
# # # # # #
Per vedere la cosa allopera su http://search.yahoo.com: ----------------------------------------------------------URL="http://search.yahoo.com/search?fr=ush-news&p=${query}" wget -O "$salvanomefile" "${URL}" ----------------------------------------------------------Registra un elenco di importanti URL.
exit $? # Esercizi: # -------# # 1) Aggiungete una verifica che confermi allutente che esegue lo script
304
lynx Il brower per il Web ed i le lynx pu essere utilizzato allinterno di uno script (con lopzione -dump) per recuperare un le dal Web o da un sito ftp in modalit non-interattiva.
lynx -dump http://www.xyz23.com/file01.html >$SAVEFILE
Con lopzione -traversal, lynx inizia dallURL HTTP specicata come argomento e scorre lentamente tutti i link presenti su quel particolare server. Usato insieme allopzione -crawl produce una pagine di testo inserita in un le di log.
rlogin Remote login, inizia una sessione su un host remoto. Dal momento che questo comando ha dei problemi inerenti alla sicurezza, al suo posto meglio usare ssh. rsh Remote shell, esegue comandi su un host remoto. Anchesso ha problemi di sicurezza. Si utilizzi, quindi, ssh. rcp Remote copy (copia da remoto), copia le tra due differenti macchine collegate in rete. rsync Remote synchronize (sinconizzazione da remoto), aggiorna (sincronizza) le tra due differenti macchine collegate in rete.
305
# Scarica laggiornamento di Fedora 4 da un mirror usando rsync. # Per risparmiare spazio, scarica solamente lultima versione di un pacchetto, #+ nel caso ve ne sia pi duna. URL=rsync://distro.ibiblio.org/fedora-linux-core/updates/ # URL=rsync://ftp.kddilabs.jp/fedora/core/updates/ # URL=rsync://rsync.planetmirror.com/fedora-linux-core/updates/ DEST=${1:-/var/www/html/fedora/updates/} LOG=/tmp/repo-update-$(/bin/date +%Y-%m-%d).txt PID_FILE=/var/run/${0##*/}.pid E_RETURN=65 # Nel caso succeda qualcosa di inatteso.
# # # #
Opzioni generali di rsync -r: download ricorsivo -t: tempo rimanente -v: verbose - dettaglio
OPZ="-rtv --delete-excluded --delete-after --partial" # modello di inclusione per rsync # La barra iniziale indica la verifica del percorso assoluto. INCLUDE=( "/4/i386/kde-i18n-Chinese*" # ^ ^ # necessario il quoting per evitare il globbing. )
# modello di escusione per rsync # Commentate temporaneamente i pacchetti da non considerare usando "#" . . . ESCLUDE=( /1 /2 /3 /testing /4/SRPMS /4/ppc /4/x86_64 /4/i386/debug "/4/i386/kde-i18n-*" "/4/i386/openoffice.org-langpack-*" "/4/i386/*i586.rpm" "/4/i386/GFS-*" "/4/i386/cman-*" "/4/i386/dlm-*" "/4/i386/gnbd-*" "/4/i386/kernel-smp*"
306
init () { # Consente alla pipe dei comandi di visualizzare eventuali errori #+ di rsync, es. stalled network. set -o pipefail TMP=${TMPDIR:-/tmp}/${0##*/}.$$ trap "{ rm -f $TMP 2>/dev/null }" EXIT } # Registra la lista aggiornata #+ del download.
controlla_pid () { # Verifica lesistenza del processo. if [ -s "$PID_FILE" ]; then echo "Il file PID esiste?. Verifica ..." PID=$(/bin/egrep -o "^[[:digit:]]+" $PID_FILE) if /bin/ps --pid $PID &>/dev/null; then echo "Trovato processo $PID. ${0##*/} in esecuzione!" /usr/bin/logger -t ${0##*/} \ "Trovato processo $PID. ${0##*/} in esecuzione!" exit $E_RETURN fi echo "Processo $PID non trovato. Inizio un nuovo processo . . ." fi }
# Imposta lintervallo dellaggiornamento completo iniziando da root o da $URL, #+ secondo quanto specificato nei modelli precedenti. imposta_intervallo () { include= esclude= for p in "${INCLUDE[@]}"; do include="$include --include \"$p\"" done for p in "${ESCLUDE[@]}"; do esclude="$esclude --exclude \"$p\"" done }
307
308
# echo "subshell=$BASH_SUBSHELL" } # Le parentesi graffe sono necessarie per consentire a #+ "echo $pre_file >> $TMP" finale di rimanere nella stessa #+ subshell ( 1 ) assiema allintero ciclo. # Recupera il codice di ritorno della pipe.
RIT=$?
[ "$RIT" -ne 0 ] && { echo "Recupero della lista fallito con codice $RET" exit $E_RETURN } echo "fatto"; echo } # Download di rsync. scarica_file () { echo "In download..." /bin/nice /usr/bin/rsync \ $OPZ \ --filter "merge,+/ $TMP" \ --exclude * \ $URL $DEST \ | /usr/bin/tee $LOG RIT=$? # # # #+ #+ --filter merge,+/ cruciale allo scopo. + il modificatore significa includi e / percorso assoluto. Quindi, lelenco ordinato presente in $TMP conterr i nomi delle directory in ordine ascendente impedendo al successivo --exclude * di mandare tutto in "corto circuito."
echo "Fatto" rm -f $PID_FILE 2>/dev/null return $RIT } # ------# Main init controlla_pid imposta_intervallo crea_lista
309
Luso di rcp, rsync ed utility simili, che hanno problemi di sicurezza, in uno script di shell potrebbe non essere consigliabile. Si consideri, invece, lutilizzo di ssh, scp o di uno script expect.
ssh Secure shell, si connette ad un host remoto e vi esegue dei comandi. Questo sostituto di sicurezza di telnet, rlogin, rcp e rsh utilizza lautenticazione e la cifratura. Per i dettagli, si veda la sua pagina di manuale. Esempio 12-40. Uso di ssh
#!/bin/bash # remote.bash: Uso di ssh. # Esempio di Michael Zick. # Usato con il consenso dellautore.
# # # # # # # #+ # # # # # # # #
Presupposti: ----------il df-2 non devesere stato impegnato ( 2>/dev/null ). ssh/sshd presumono che lo stderr (2) verr visualizzato allutente. sshd deve essere in esecuzione sulla macchina. Probabilmente questa la situazione per qualsiasi distribuzione standard, e senza aver fatto qualche strana impostazione di ssh-keygen.
Provate ssh da riga di comando sulla vostra macchina: $ ssh $HOSTNAME Se non sono state fatte impostazioni ulteriori, vi verr chiesta la password. inserite la password quindi $ exit Ha funzionato? In questo caso siete pronti per un altro po di divertimento.
310
# Il fondamentale comando di visualizzazione allo stdout (locale). ls -l # E ora lo stesso comando su una macchina remota. # Se desiderate, potete passare USERNAME HOSTNAME diversi: USER=${USERNAME:-$(whoami)} HOST=${HOSTNAME:-$(hostname)} # Ora eseguiamo la precedente riga di comando su un host remoto, #+ la trasmissione totalmente criptata. ssh -l ${USER} ${HOST} " ls -l " # #+ # #+ Il risultato atteso lelenco dei file della directory home dellutente presente sulla macchina remota. Se volete vedere delle differenze, eseguite lo script da una qualsiasi directory diversa dalla vostra directory home.
# In altre parole, il comando Bash viene passato come stringa tra apici #+ alla shell remota, che lo esegue sulla macchina remota. # In questo caso sshd esegue bash -c "ls -l" per conto vostro. # Per informazioni su argomenti quali il non dover inserire la #+ password/passphrase ad ogni riga di comando, vedi #+ man ssh #+ man ssh-keygen #+ man sshd_config. exit 0
311
Cautela
In un ciclo, ssh potrebbe causare un comportamento inaspettato. Secondo un post Usenet (http://groupsbeta.google.com/group/comp.unix.shell/msg/dcb446b5fff7d230) negli archivi shell di comp.unix, ssh eredita lo stdin del ciclo. Per porvi rimedio, si passi ad ssh o lopzione -n o lopzione -f.
scp Secure copy , ha la stessa funzionalit di rcp, copia, cio, le tra due differenti macchine collegate in rete. Ma lo fa utilizzando lautenticazione e con un livello di sicurezza simile a ssh.
Rete Locale
write lutility per la comunicazione terminale-terminale. Consente di inviare righe di testo dal vostro terminale (console o xterm) a quello di un altro utente. Si pu usare, naturalmente, il comando mesg per disabilitare laccesso di write in scrittura su di un terminale. Poich write interattivo, normalmente non viene impiegato in uno script.
netcong Utility da riga di comando per la congurazione di un adattatore di rete (utilizzo di DHCP). un comando nativo delle distribuzioni Linux Red Hat.
Posta
mail Invia o legge messaggi e-mail. Questo client da riga di comando per il recupero della posta funziona altrettanto bene come comando inserito in uno script. Esempio 12-41. Uno script che si auto-invia
#!/bin/sh # self-mailer.sh: Script che si auto-invia adr=${1:-whoami} # Imposta lutente corrente come predefinito, se #+ non altrimenti specificato.
312
# ========================================================================= cat $0 | mail -s " Lo script \"basename $0\" si auto-inviato." "$adr" # ========================================================================= # -------------------------------------------------# Saluti dallo script che si auto-invia. # Una persona maliziosa ha eseguito questo script, #+ che ne ha provocato linvio a te. Apparentemente, #+ certa gente non ha niente di meglio da fare #+ con il proprio tempo. # -------------------------------------------------echo "Il date, lo script \"basename $0\" stato inviato a "$adr"." exit 0
mailto Simile al comando mail, mailto invia i messaggi e-mail da riga di comando o da uno script. Tuttavia, mailto consente anche linvio di messaggi MIME (multimedia). vacation Questa utility risponde in automatico alle e-mail indirizzate ad un destinatario che si trova in vacanza o temporaneamente indisponibile. Funziona su una rete, in abbinamento con sendmail, e non utilizzabile per un account di posta POP in dial-up.
bash$ tput longname xterm terminal emulator (XFree86 4.0 Window System)
313
Lesecuzione di tput cup X Y sposta il cursore alle coordinate (X,Y) nel terminale corrente. Normalmente dovrebbe essere preceduto dal comando clear per pulire lo schermo. Si noti che stty offre una serie di comandi pi potenti per il controllo di un terminale.
infocmp Questo comando visualizza informazioni dettagliate sul terminale corrente. Utilizza, allo scopo, il database terminfo.
bash$ infocmp # Reconstructed via infocmp from file: /usr/share/terminfo/r/rxvt rxvt|rxvt terminal emulator (X Window System), am, bce, eo, km, mir, msgr, xenl, xon, colors#8, cols#80, it#8, lines#24, pairs#64, acsc=aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, bel=^G, blink=\E[5m, bold=\E[1m, civis=\E[?25l, clear=\E[H\E[2J, cnorm=\E[?25h, cr=^M, ...
reset Annulla i parametri del terminale e pulisce lo schermo. Come nel caso di clear, il cursore ed il prompt vengono posizionati nellangolo in alto a sinistra dello schermo. clear Il comando clear cancella semplicemente lo schermo di una console o di un xterm. Il prompt e il cursore riappaiono nellangolo superiore sinistro dello schermo o della nestra xterm. Questo comando pu essere usato sia da riga di comando che in uno script. Vedi Esempio 10-25. script Questa utility registra (salva in un le) tutte le digitazioni da riga di comando eseguite dallutente su una console o in una nestra xterm. In pratica crea una registrazione della sessione.
314
bc Bash non in grado di gestire i calcoli in virgola mobile, quindi non dispone di operatori per alcune importanti funzioni matematiche. Fortunatamente viene in soccorso bc. Non semplicemente una versatile utility per il calcolo in precisione arbitraria, bc offre molte delle potenzialit di un linguaggio di programmazione. bc possiede una sintassi vagamente somigliante al C. Dal momento che si tratta di una utility UNIX molto ben collaudata, e che quindi pu essere utilizzata in una pipe, bc risulta molto utile negli script. Ecco un semplice modello di riferimento per luso di bc per calcolare una variabile di uno script. Viene impiegata la sostituzione di comando.
# #+ #+ #
Questa una modifica del codice del pacchetto "mcalc" (mortgage calculator), di Jeff Schmidt e Mendel Cooper (vostro devotissimo, autore di questo documento). http://www.ibiblio.org/pub/Linux/apps/financial/mcalc-1.6.tar.gz [15k]
echo
315