Il 0% ha trovato utile questo documento (0 voti)
907 visualizzazioni841 pagine

AdvancedBashScripting Guide

Caricato da

Carmelo Leggio
Copyright
© Attribution Non-Commercial (BY-NC)
Per noi i diritti sui contenuti sono una cosa seria. Se sospetti che questo contenuto sia tuo, rivendicalo qui.
Formati disponibili
Scarica in formato PDF, TXT o leggi online su Scribd
Il 0% ha trovato utile questo documento (0 voti)
907 visualizzazioni841 pagine

AdvancedBashScripting Guide

Caricato da

Carmelo Leggio
Copyright
© Attribution Non-Commercial (BY-NC)
Per noi i diritti sui contenuti sono una cosa seria. Se sospetti che questo contenuto sia tuo, rivendicalo qui.
Formati disponibili
Scarica in formato PDF, TXT o leggi online su Scribd
Sei sulla pagina 1/ 841

Guida avanzata di scripting Bash

Unapprofondita esplorazione dellarte dello scripting di shell

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

Lista delle Tabelle


11-1. Identicatori di job .........................................................................................................................222 30-1. Opzioni bash...................................................................................................................................528 33-1. Numeri che rappresentano i colori nelle sequenze di escape .........................................................561 B-1. Variabili speciali di shell..................................................................................................................761 B-2. Operatori di verica: confronti binari..............................................................................................761 B-3. Operatori di verica: le..................................................................................................................762 B-4. Sostituzione ed espansione di parametro.........................................................................................763 B-5. Operazioni su stringhe .....................................................................................................................764 B-6. Costrutti vari ....................................................................................................................................765 C-1. Operatori sed di base .......................................................................................................................767 C-2. Esempi di operatori sed ...................................................................................................................769 D-1. Codici di Exit riservati .................................................................................................................775 L-1. Parole chiave / variabili / operatori dei le batch e loro equivalenti di shell ...................................806 L-2. Comandi DOS e loro equivalenti UNIX ..........................................................................................807 N-1. Cronologia delle revisioni ...............................................................................................................823

Lista degli Esempi


2-1. cleanup: Uno script per cancellare i le di log in /var/log...................................................................4 2-2. cleanup: Lo script clean-up migliorato................................................................................................4 2-3. cleanup: Una versione avanzata e generalizzata degli script precedenti. ............................................4 4-4. Intero o stringa?..................................................................................................................................42 5-1. Visualizzare strane variabili ...............................................................................................................50 6-1. exit / exit status...................................................................................................................................60 7-1. Cos vero?.........................................................................................................................................63 7-2. Equivalenza di test, /usr/bin/test, [ ] e /usr/bin/[.................................................................67 7-3. Veriche aritmetiche utilizzando (( )) ................................................................................................69 7-4. Ricerca di link interrotti (broken link) ...............................................................................................72 7-5. Confronti numerici e di stringhe ........................................................................................................77 7-6. Vericare se una stringa nulla .........................................................................................................78 7-7. zmore .................................................................................................................................................80 8-4. Rappresentazione di costanti numeriche ............................................................................................92 9-10. Inserire una riga bianca tra i paragra di un le di testo................................................................118 9-13. Modi alternativi di estrarre sottostringhe .......................................................................................125 9-14. Sostituzione di parametro e messaggi derrore ..............................................................................130 9-15. Sostituzione di parametro e messaggi utilizzo............................................................................131 9-17. Ricerca di corrispondenza nella sostituzione di parametro ............................................................133 9-18. Rinominare le estensioni dei le: ...................................................................................................134 9-21. Utilizzare declare per tipizzare le variabili....................................................................................140 9-22. Referenziazioni indirette ................................................................................................................142 9-23. Passare una referenziazione indiretta a awk ..................................................................................144 9-24. Generare numeri casuali.................................................................................................................145 9-25. Scegliere una carta a caso dal mazzo .............................................................................................147 9-26. Numero casuale in un intervallo dato.............................................................................................149 9-27. Lanciare un dado con RANDOM...................................................................................................153

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.

Capitolo 1. Perch programmare la shell?


No programming language is perfect. There is not even a single best language; there are only languages well suited or perhaps poorly suited for particular purposes. Herbert Mayer La conoscenza pratica dello scripting di shell essenziale per coloro che desiderano diventare degli amministratori di sistema esperti, anche se mai avrebbero messo in preventivo di scrivere degli script. Occorre tener presente che quando viene avviata una macchina Linux, questa esegue gli script di shell contenuti nel le /etc/rc.d per ripristinare la congurazione del sistema ed attivarne i servizi. La comprensione dettagliata degli script di avvio importante per analizzare il comportamento di un sistema e, se possibile, modicarlo. Imparare a scrivere degli script non difcile, perch possono essere costituiti da sezioni di piccole dimensioni ed veramente esigua anche la serie di operatori ed opzioni speciche 1 che necessario conoscere. La sintassi semplice e chiara, come quella necessaria per eseguire e concatenare utility da riga di comando, e sono poche anche le regole da imparare. Nella maggior parte dei casi, gli script di piccole dimensioni funzionano correttamente n dalla prima volta che vengono eseguiti e non complicata neanche la fase di debugging di quelli di dimensioni maggiori. Uno script di shell un metodo rapido e grezzo per costruire un prototipo di unapplicazione complessa. Far eseguire anche una serie ridotta di funzionalit tramite uno script di shell spesso un utile primo passo nello sviluppo di un progetto. In questo modo si pu vericare e sperimentare la struttura di unapplicazione e scoprire i principali errori prima di procedere alla codica nale in C, C++, Java o Perl. Lo scripting di shell attento alla losoa classica UNIX di suddividere progetti complessi in sezioni di minori dimensioni che svolgono un compito particolare, concatenando componenti e utility. Questo considerato, da molti, un approccio migliore, o almeno esteticamente pi piacevole per risolvere un problema, che utilizzare uno dei linguaggi di nuova generazione , come Perl, che offrono funzionalit per ogni esigenza, ma al prezzo di costringere a modicare il modo di pensare un progetto per adattarlo al linguaggio utilizzato. Quando non usare gli script di shell

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)

Capitolo 1. Perch programmare la shell?

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.

Capitolo 2. Iniziare con #!


Shell programming is a 1950s juke box . .. Larry Wall Nel caso pi semplice, uno script non nientaltro che un le contenente un elenco di comandi di sistema. Come minimo si risparmia lo sforzo di ridigitare quella particolare sequenza di comandi tutte le volte che necessario. Esempio 2-1. cleanup: Uno script per cancellare i le di log in /var/log
# Cleanup # Da eseguire come root, naturalmente. cd /var/log cat /dev/null > messages cat /dev/null > wtmp echo "Log cancellati."

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.

Adesso incomincia ad assomigliare ad un vero script. Ma si pu andare oltre . . .

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.

DIR_LOG=/var/log ROOT_UID=0 # LINEE=50 # E_XCD=66 # E_NONROOT=67 #

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.

Capitolo 2. Iniziare con #!

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

Capitolo 2. Iniziare con #!


#!/bin/sh #!/bin/bash #!/usr/bin/perl #!/usr/bin/tcl #!/bin/sed -f #!/usr/awk -f

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.

Capitolo 2. Iniziare con #!

2.1. Eseguire uno script


Dopo aver creato uno script, lo si pu eseguire con sh nomescript 5 o, in alternativa, con bash nomescript. Non raccomandato luso di sh <nomescript perch, cos facendo, si disabilita la lettura dallo stdin allinterno dello script. molto pi conveniente rendere lo script eseguibile direttamente con chmod. O con: chmod 555 nomescript (che d a tutti gli utenti il permesso di lettura/esecuzione) 6 O con chmod +rx nomescript (come il precedente) chmod u+rx nomescript (che attribuisce solo al proprietario dello script il permesso di lettura/esecuzione)

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.

2.2. Esercizi preliminari


1. Gli amministratori di sistema spesso creano degli script per eseguire automaticamente compiti di routine. Si forniscano diversi esempi in cui tali script potrebbero essere utili. 2. Si scriva uno script che allesecuzione visualizzi lora e la data, elenchi tutti gli utenti connessi e fornisca il tempo di esecuzione uptime del sistema. Lo script, quindi, dovr salvare queste informazioni in un le di log.

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.

Capitolo 2. Iniziare con #!


Se, infatti, lo script include unaltra riga con #!, bash la interpreterebbe correttamente come un commento, dal momento che il primo #! ha gi svolto il suo compito.
#!/bin/bash echo "Parte 1 dello script." a=1 #!/bin/bash # Questo *non* eseguir un nuovo script. echo "Parte 2 dello script." echo $a # Il valore di $a rimasto 1.

3. Ci permette degli ingegnosi espedienti.


#!/bin/rm # Script che si autocancella. # Niente sembra succedere quando viene eseguito ... solo che il file scompare. # QUALUNQUECOSA=65 echo "Questa riga non verr mai visualizzata (scommettete!)." exit $QUALUNQUECOSA # Niente paura. Lo script non terminer a questo punto.

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

Capitolo 3. Caratteri speciali


Caratteri speciali che si trovano negli script e non solo
# Commenti. Le righe che iniziano con # (con leccezione di #!) sono considerate commenti.

# Questa riga un commento.

I commenti possono anche essere posti dopo un comando.

echo "Seguir un commento." # Qui il commento. # ^ Notate lo spazio prima del #

Sono considerati commenti anche quelli che seguono uno o pi spazi posti allinizio di una riga.

# Questo commento preceduto da un carattere di tabulazione.

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.

echo ${PATH#*:} echo $(( 2#101011 )) # Grazie, S.C.

I caratteri standard per il quoting e lescaping (" \) evitano la reinterpretazione di #.

11

Capitolo 3. Caratteri speciali

Anche alcune operazioni di ricerca di corrispondenza utilizzano il #.

; Separatore di comandi [punto e virgola]. Permette di inserire due o pi comandi sulla stessa riga.

echo ehil; echo ciao

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."

Si faccia attenzione che ;, talvolta, deve essere preceduto da un carattere di escape.

;; Delimitatore in unopzione case [doppio punto e virgola].

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

Capitolo 3. Caratteri speciali


-rw-r--r-1 bozo -rw-r--r-1 bozo employment.addressbook 4602 May 25 13:58 data1.addressbook.bak 877 Dec 17 2000 employment.addressbook

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

bozo bozo bozo bozo bozo bozo

1024 3072 4034 4602 877 0

Aug Aug Jul May Dec Aug

29 29 18 25 17 29

20:54 20:51 22:04 13:58 2000 20:54

./ ../ data1.addressbook data1.addressbook.bak employment.addressbook .file_nascosto

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

Capitolo 3. Caratteri speciali


while : do operazione-1 operazione-2 ... operazione-n done # Uguale a: # while true # do # ... # done

Istruzione nulla in un costrutto if/then:

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} #

Senza i : iniziali d un errore, tranne se "nomeutente" un comando o un builtin ...

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.

Espansione di variabile / sostituzione di sottostringa.

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.

bash$ echo * abs-book.sgml add-drive.sh agram.sh alias.sh

L * rappresenta anche tutti i caratteri (o nessuno) in una espressione regolare.

* 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

Capitolo 3. Caratteri speciali

$ 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)

{xxx,yyy,zzz,...} Espansione multipla.


cat {file1,file2,file3} > file_unico # Concatena i file file1, file2 e file3 in file_unico.

18

Capitolo 3. Caratteri speciali


cp file22.{txt,backup} # Copia "file22.txt" in "file22.backup"

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.

echo {file1,file2}\ :{\ A," B", C}

file1 : A file1 : B file1 : C file2 : A file2 : B file2 : C

{}

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=123 { a=321; } echo "a = $a" # Grazie, S.C.

# a = 321

(valore di a nel blocco di codice)

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

Capitolo 3. Caratteri speciali

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.

Esempio 3-2. Salvare i risultati di un blocco di codice in un le


#!/bin/bash # rpm-check.sh # Interroga un file rpm per visualizzarne la descrizione ed il #+contenuto, verifica anche se pu essere installato. # Salva loutput in un file. # # Lo script illustra lutilizzo del blocco di codice. SUCCESSO=0 E_ERR_ARG=65 if [ -z "$1" ] then echo "Utilizzo: basename $0 file-rpm" exit $E_ERR_ARG fi { echo echo "Descrizione Archivio:" rpm -qpi $1 # Richiede la descrizione. echo echo "Contenuto dellarchivio:" rpm -qpl $1 # Richiede il contenuto. echo rpm -i --test $1 # Verifica se il file rpm pu essere installato. if [ "$?" -eq $SUCCESSO ] then echo "$1 pu essere installato." else

20

Capitolo 3. Caratteri speciali


echo "$1 non pu essere installato." fi echo } > "$1.test" # Redirige loutput di tutte le istruzioni del blocco #+ in un file. echo "I risultati della verifica rpm si trovano nel file $1.test" # Vedere la pagina di manuale di rpm per la spiegazione delle opzioni. exit 0

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

\< \> delimitatore di parole in unespressione regolare.


bash$ grep \<il\> filetesto

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

Capitolo 3. Caratteri speciali

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

Ora si collega loutput di ls -l allo script.


bash$ ls -l | ./uppercase.sh -RW-RW-R-1 BOZO BOZO -RW-RW-R-1 BOZO BOZO -RW-R--R-1 BOZO BOZO 109 APR 7 19:49 1.TXT 109 APR 14 16:48 2.TXT 725 APR 20 20:56 DATA-FILE

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).

bash$ sleep 10 & [1] 850 [1]+ Done

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.

for i in 11 12 13 14 15 16 17 18 19 20 do echo -n "$i " done echo

# Questo echo alcune volte non verr eseguito.

# ====================================================== # Output atteso: # 1 2 3 4 5 6 7 8 9 10 # 11 12 13 14 15 16 17 18 19 20 # Talvolta si potrebbe ottenere: # 11 12 13 14 15 16 17 18 19 20 # 1 2 3 4 5 6 7 8 9 10 bozo $

25

Capitolo 3. Caratteri speciali


# (Il secondo echo non stato eseguito. Perch?) # Occasionalmente anche: # 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # (Il primo echo non stato eseguito. Perch?) # # # #+ Molto raramente qualcosa come: 11 12 13 1 2 3 4 5 6 7 8 9 10 14 15 16 17 18 19 20 Il ciclo in primo piano (foreground) ha la precedenza su quello in background.

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

sort -dfu $nomefile

set -- $variabile

if [ $file1 -ot $file2 ] then echo "Il file $file1 pi vecchio di $file2." fi if [ "$a" -eq "$b" ]

26

Capitolo 3. Caratteri speciali


then echo "$a uguale a $b." fi if [ "$c" -eq 24 -a "$d" -eq 47 ] then echo "$c uguale a 24 e $d uguale a 47." fi

redirezione dallo/allo stdin o stdout [trattino].

(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)

| ( ... ) cd /dest/directory && tar xpvf -

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

Capitolo 3. Caratteri speciali


bunzip2 linux-2.6.13.tar.bz2 | tar xvf # --decomprime il file tar -| --quindi lo passa a "tar"-# Se "tar" non stato aggiornato per trattare "bunzip2", #+ occorre eseguire loperazione in due passi successivi utilizzando una pipe. # Lo scopo dellesercizio di decomprimere i sorgenti del kernel # compressi con "bzip".

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.

bash$ echo "qualsiasi cosa" | cat qualsiasi cosa

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

Capitolo 3. Caratteri speciali

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

"echo -n", che non visualizza nulla.

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.

Meno. Segno meno in una operazione aritmetica. = Uguale. Operatore di assegnamento


a=28 echo $a # 28

In un contesto differente, il simbolo di = loperatore di confronto tra stringhe.

+ Pi. Operatore aritmetico di addizione. In un contesto differente, il simbolo + un operatore di Espressione Regolare.

30

Capitolo 3. Caratteri speciali

+ 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

Capitolo 3. Caratteri speciali Caratteri di controllo

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-B Backspace (ritorno non distruttivo).

Ctl-C Interruzione. Termina unapplicazione in primo piano.

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.

#!/bin/bash # Inserire Ctl-H in una stringa.

32

Capitolo 3. Caratteri speciali

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-I Tabulazione orizzontale.

Ctl-J Nuova riga (line feed).

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

Capitolo 3. Caratteri speciali


echo >&2 ### read -n 1 -s -p $E Control-K\x0b lo sposta direttamente in basso. echo >&2 # Control-K indica la tabulazione verticale. # Un esempio migliore delleffetto di una tabulazione verticale il seguente: var=$\x0aQuesta la riga finale\x0bQuesta la riga iniziale\x0a echo "$var" # Stesso risultato dellesempio precedente. Tuttavia: echo "$var" | col # Questo provoca linversione nella visualizzazione delle righe. # Inoltre spiega il motivo per cui sono stati posti dei line feed allinizio e #+ alla fine della riga: evitare una visualizzazione confusa. # La spiegazione di Lee Maschmeyer: # -------------------------------# Nel primo esempio [di tabulazione verticale] . . . questa esegue #+ una semplice visualizzazione alla riga inferiore senza il ritorno a capo. # Ma questo vale solo per i dispositivi, quali la console Linux, #+ che non consentono di andare "in senso inverso." # Il vero scopo della TV quello di andare in S, non in gi. # Ci pu essere sfruttato per stampare dei soprascritti. # Lutility col pu essere usata per simulare il corretto comportamento #+ di una TV. exit 0 # Control-J indica nuova riga (linefeed).

Ctl-Q Ripristino (XON). Ripristina lo stdin di un terminale.

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-V particolarmnete utile in un editor di testo.

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.

Ctl-Z Sospende unapplicazione in primo piano.

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

Capitolo 3. Caratteri speciali

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

Capitolo 4. Introduzione alle variabili ed ai parametri


Le variabili rappresentano il modo in cui i linguaggi di scripting e di programmazione identicano i dati. Compaiono nelle operazioni aritmetiche, nelle manipolazioni quantitative e nelle veriche di stringhe e sono indispensabili per lavorare a livello astratto per mezzo dei simboli - parole che rappresentano qualcosaltro. Una variabile non nientaltro che unetichetta assegnata a una locazione, o a una serie di locazioni, di memoria del computer che contiene un dato.

4.1. Sostituzione di variabile


Il nome di una variabile il contenitore del suo valore, il dato memorizzato. Il riferimento a questo valore chiamato sostituzione di variabile. $ Bisogna fare una netta distinzione tra il nome di una variabile ed il suo valore. Se variabile1 il nome di una variabile, allora $variable1 il riferimento al suo valore, il dato in essa contenuto.

bash$ variabile=23

bash$ echo variabile variabile bash$ echo $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 "". #----------------------------------------------------------------------------

echo ciao echo $ciao echo ${ciao}

# Non un riferimento a variabile, ma solo la stringa "ciao".

# 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

Capitolo 4. Introduzione alle variabili ed ai parametri

# Notate leffetto dei differenti tipi di quoting.

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

Capitolo 4. Introduzione alle variabili ed ai parametri

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.

Ci nonostante possibile eseguire operazioni aritmetiche su una variabile non inizializzata.


echo "$non_inizializzata" let "non_inizializzata += 5" echo "$non_inizializzata" # # #+ # # (riga vuota) # Aggiunge 5 alla # 5

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

Vedi anche Esempio 11-21.

4.2. Assegnamento di variabile


= loperatore di assegnamento (nessuno spazio prima e dopo)

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.

Esempio 4-2. Assegnamento esplicito di variabile


#!/bin/bash # Variabili nude echo # Quando una variabile "nuda", cio, senza il $ davanti? # Durante lassegnamento, ma non nella referenziazione. # Assegnamento a=879

40

Capitolo 4. Introduzione alle variabili ed ai parametri


echo "Il valore di \"a\" $a." # Assegnamento con lutilizzo di let let a=16+5 echo "Il valore di \"a\" ora $a." echo # In un ciclo for (in realt, un tipo di assegnamento mascherato): echo -n "I valori di \"a\" nel ciclo sono: " for a in 7 8 9 11 do echo -n "$a " done echo echo # In echo read echo echo exit 0 un enunciato read (un altro tipo di assegnamento): -n "Immetti il valore di \"a\" " a "Il valore di \"a\" ora $a."

Esempio 4-3. Assegnamento di variabile, esplicito e indiretto


#!/bin/bash a=23 echo $a b=$a echo $b # Caso comune

# 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.

a=ls -l echo $a echo echo "$a"

41

Capitolo 4. Introduzione alle variabili ed ai parametri


# (Vedi il capitolo sul "Quoting.") exit 0

Assegnamento di variabile utilizzando $(...) (metodo pi recente rispetto agli apici inversi). In realt si tratta di una forma particolare di sostituzionedi comando.

# Dal file /etc/rc.d/rc.local R=$(cat /etc/redhat-release) arch=$(uname -m)

4.3. Le variabili Bash non sono tipizzate

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

# # # #

c = BB34 Sostituisce "BB" con "23". Questo trasforma $d in un intero. d = 2334

42

Capitolo 4. Introduzione alle variabili ed ai parametri


let "d += 1" echo "d = $d" echo # 2334 + 1 = # d = 2335

# 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 in Bash non sono tipizzate. exit 0

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.

4.4. Tipi speciali di variabili


variabili locali sono variabili visibili solo allinterno di un blocco di codice o funzione (vedi anche variabili locali in funzioni) variabili dambiente sono variabili relative al comportamento della shell o allinterfaccia utente
Nota: Pi in generale, ogni processo possiede un proprio ambiente, ovvero un gruppo di variabili contenenti delle informazioni a cui il processo fa riferimento. Da questo punto di vista, la shell si comporta come qualsiasi altro processo. Ogni volta che la shell viene eseguita crea le variabili di shell che corrispondono alle sue variabili dambiente. Laggiornamento o laggiunta di nuove variabili di shell provoca laggiornamento del suo ambiente. Tutti i processi generati dalla shell (i comandi eseguiti) ereditano questo ambiente.

43

Capitolo 4. Introduzione alle variabili ed ai parametri

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

(Grazie a Stphane Chazelas per i chiarimenti e per aver fornito lesempio.)

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

Capitolo 4. Introduzione alle variabili ed ai parametri

# 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}" ]

# I parametri > $9 devono essere racchiusi #+ tra {parentesi graffe}.

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

Capitolo 4. Introduzione alle variabili ed ai parametri Esempio 4-7. Uso di shift


#!/bin/bash # Utilizzo di shift per elaborare tutti i parametri posizionali. # # Chiamate lo script shft ed invocatelo con alcuni parametri, per esempio ./shft a b c def 23 skidoo # Finch ci sono parametri...

until [ -z "$1" ] do echo -n "$1 " shift done echo exit 0

# 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 *.)

bash$ ls -l [Vv]* -rw-rw-r-1 bozo -rw-rw-r-1 bozo -rw-rw-r-1 bozo

bozo bozo bozo

324 Apr 2 15:05 VIEWDATA.BAT 507 May 4 14:25 vartrace.sh 539 Apr 14 17:11 viewdata.sh

bash$ ls -l [Vv]* ls: [Vv]*: No such file or directory

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

bash$ echo "$(ls -l)"

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

5.1. Quoting di variabili


Nella referenziazione di una variabile, generalmente consigliabile racchiudere il nome della variabile tra apici doppi. Questo preserva dallinterpretazione tutti i caratteri speciali della stringa -- il nome della variabile 2 -- tranne $, (apice inverso) e \ (escape). 3 Mantenere il $ come carattere speciale consente la referenziazione di una variabile racchiusa tra doppi apici ("$variabile"), cio, sostituire la variabile con il suo valore (vedi Esempio 4-1, precedente). Lutilizzo degli apici doppi previene la suddivisione delle parole. 4 Un argomento tra apici doppi viene considerato come ununica parola, anche se contiene degli spazi.
variabile1="una variabile contenente cinque parole" COMANDO Questa $variabile1 # Esegue COMANDO con 7 argomenti: # "Questa" "" "una" "variabile" "contenente" "cinque" "parole" COMANDO "Questa $variabile1" # Esegue COMANDO con 1 argomento: # "Questa una variabile contenente cinque parole"

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.

Esempio 5-1. Visualizzare strane variabili


#!/bin/bash # weirdvars.sh: Visualizzare strane variabili.

50

Capitolo 5. Quoting

var="(]\\{}\$\"" echo $var # (]\{}$" echo "$var" # (]\{}$" echo IFS=\ echo $var echo "$var"

Nessuna differenza.

# (] {}$" # (]\{}$"

\ trasformata in spazio. Perch?

# Esempi forniti da Stephane Chazelas. exit 0

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.]).

Signicati speciali di alcuni caratteri preceduti da quello di escape:


Da usare con echo e sed

\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

echo "VIRGOLETTE" echo -e "\042" echo "=============="

# Visualizza " (42 il valore ottale del #+ carattere ASCII virgolette).

# 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

echo; echo exit 0

Vedi Esempio 34-1 per unaltra dimostrazione di $ come costrutto di espansione di stringa.

\" mantiene il signicato letterale dei doppi apici

echo "Ciao" echo "\"Ciao\", disse."

# Ciao # "Ciao", disse.

\$ mantiene il signicato letterale del segno del dollaro (la variabile che segue \$ non verr referenziata)

echo "\$variabile01"

# visualizza $variabile01

\\ mantiene il signicato letterale della barra inversa

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

echo echo echo echo echo

\z \\z \z \\z "\z"

54

Capitolo 5. Quoting
echo "\\z" # \z # # # # # # # # # Sostituzione di comando z z \z \z \z \\z \z \z

echo echo echo echo echo echo echo echo

echo echo echo echo echo echo echo echo

\z \\z \\\z \\\\z \\\\\\z \\\\\\\z "\z" "\\z"

# Here document cat <<EOF \z EOF cat <<EOF \\z EOF

# \z

# \z

# Esempi forniti da Stphane Chazelas.

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

variabile=\\\\ echo "$variabile"

# # #+ #

\\ 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!

bash$ echo "ciao\!" ciao\!

bash$ echo -e x\ty xty

bash$ echo -e "x\ty" x y

(Grazie a Wayne Pollock per la precisazione.)

4. La divisione delle parole, in questo contesto, signica suddividere una stringa di caratteri in un certo numero di argomenti separati e distinti.

58

Capitolo 6. Exit ed exit status


...there are dark corners in the Bourne shell, and people use all of them. Chet Ramey Il comando exit pu essere usato per terminare uno script, proprio come in un programma in linguaggio C. Pu anche restituire un valore disponibile al processo genitore dello script. Ogni comando restituisce un exit status (talvolta chiamato anche return status ). Un comando che ha avuto successo restituisce 0, mentre, in caso di insuccesso, viene restituito un valore diverso da zero, che solitamente pu essere interpretato come un codice derrore. Comandi, programmi e utility UNIX correttamente eseguiti restituiscono come codice di uscita 0, con signicato di successo, sebbene ci possano essere delle eccezioni. In maniera analoga, sia le funzioni allinterno di uno script che lo script stesso, restituiscono un exit status che nientaltro se non lexit status dellultimo comando eseguito dalla funzione o dallo script. In uno script, il comando exit nnn pu essere utilizzato per inviare lexit status nnn alla shell (nnn deve essere un numero decimale compreso nellintervallo 0 - 255).
Nota: Quando uno script termina con exit senza alcun parametro, lexit status dello script quello dellultimo comando eseguito (quello che precede exit).
#!/bin/bash COMANDO_1 . . . # Esce con lo status dellultimo comando. ULTIMO_COMANDO exit

Lequivalente del solo exit exit $? o, addirittura, tralasciando semplicemente exit.


#!/bin/bash COMANDO_1 . . . # Esce con lo status dellultimo comando. ULTIMO_COMANDO exit $?

59

Capitolo 6. Exit ed exit status


#!/bin/bash COMANDO1 . . . # Esce con lo status dellultimo comando. ULTIMO_COMANDO

$? 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.

Esempio 6-1. exit / exit status


#!/bin/bash echo ciao echo $? lskdf echo $?

# 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

Capitolo 6. Exit ed exit status


# Notate che "!" deve essere seguito da uno spazio. # !true restituisce lerrore "command not found" # # Loperatore ! anteposto ad un comando richiama la cronologia dei #+ comandi di Bash. true !true # Questa volta nessun errore, ma neanche nessuna negazione. # Viene ripetuto semplicemente il comando precedente (true). # Grazie a Stphane Chazelas e Kristopher Newsome.

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.

7.1. Costrutti condizionali

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

if COMANDO_CON_EXIT_STATUS_0_SE_NON_SI_VERIFICA_UN_ERRORE then echo "Comando eseguito." else echo "Comando fallito." fi

Un costrutto if/then pu contenere confronti e veriche annidate.


if echo "Il prossimo *if* parte del costrutto del primo *if*." if [[ $confronto = "intero" ]] then (( a < b )) else [[ $a < $b ]] fi then echo $a inferiore a $b fi

Dettagliata spiegazione della condizione-if cortesia di Stphane Chazelas.

Esempio 7-1. Cos vero?


#!/bin/bash # Suggerimento: # se non siete sicuri di come certe condizioni verranno valutate, #+ controllatele con una verifica if. echo echo "Verifica \"0\"" if [ 0 ] # zero then echo "0 vero." else

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=

# Inizializzata, ma impostata a valore nullo.

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

Esercizio. Si spieghi il comportamento del precedente Esempio 7-1.

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

Esempio 7-2. Equivalenza di test, /usr/bin/test, [ ] e /usr/bin/[


#!/bin/bash echo if test -z "$1" then echo "Nessun argomento da riga di comando." else echo "Il primo argomento da riga di comando $1." fi echo if /usr/bin/test -z "$1" # Stesso risultato del builtin "test". then echo "Nessun argomento da riga di comando." else echo "Il primo argomento da riga di comando $1." fi

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

Il costrutto [[ ]] la versione Bash pi versatile di [ ]. il comando di verica esteso, adottato da ksh88.


Nota: Non pu aver luogo alcuna espansione di nome di le o divisione di parole tra [[ e ]], mentre sono consentite lespansione di parametro e la sostituzione di comando.

file=/etc/passwd if [[ -e $file ]] then echo "Il file password esiste." fi

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

7.2. Operatori di verica di le


Restituiscono vero se...
-e il le esiste -a il le esiste Effetto identico a -e, ma stato deprecato e scoraggiato lutilizzo.

-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

Capitolo 7. Veriche Esempio 7-4. Ricerca di link interrotti (broken link)


#!/bin/bash # broken-link.sh # Scritto da Lee Bigelow <[email protected]> # Utilizzato con il consenso dellautore. # Uno script di pura shell per cercare i link simbolici "morti" e visualizzarli #+ tra virgolette, in modo tale che possano essere trattati e dati in pasto a #+ xargs :) es. broken-link.sh /unadirectory /altradirectory | xargs rm # #Il seguente, tuttavia, il metodo migliore: # #find "unadirectory" -type l -print0|\ #xargs -r0 file|\ #grep "broken symbolic"| #sed -e s/^\|: *broken symbolic.*$/"/g # #ma non sarebbe bash pura, come deve essere. #Prudenza: state attenti al file di sistema /proc e a tutti i link circolari! #############################################################################

# 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.

7.3. Altri operatori di confronto


Un operatore di verica binario confronta due variabili o due grandezze. Si faccia attenzione alla differenza tra il confronto di interi e quello di stringhe.

confronto di interi
-eq

uguale a if [ "$a" -eq "$b" ]

-ne diverso (non uguale) da if [ "$a" -ne "$b" ]

-gt maggiore di if [ "$a" -gt "$b" ]

-ge maggiore di o uguale a if [ "$a" -ge "$b" ]

74

Capitolo 7. Veriche -lt minore di if [ "$a" -lt "$b" ]

-le minore di o uguale a if [ "$a" -le "$b" ]

< minore di (tra doppie parentesi) (("$a" < "$b"))

<= minore di o uguale a (tra doppie parentesi) (("$a" <= "$b"))

> maggiore di (tra doppie parentesi) (("$a" > "$b"))

>= maggiore di o uguale a (tra doppie parentesi) (("$a" >= "$b"))

confronto di stringhe
=

uguale a

75

Capitolo 7. Veriche if [ "$a" = "$b" ]

== uguale a if [ "$a" == "$b" ] sinonimo di =.


Nota: Il comportamento delloperatore di confronto == allinterno del costrutto di verica doppie parentesi quadre diverso rispetto a quello nel costrutto parentesi quadre singole.
[[ $a == z* ]] [[ $a == "z*" ]] [ $a == z* ] [ "$a" == "z*" ] # Vero se $a inizia con una "z" (corrispondenza di modello). # Vero se $a uguale a z* (corrispondenza letterale). # Esegue il globbing e la divisione delle parole. # Vero se $a uguale a z* (corrispondenza letterale).

# Grazie a Stphane Chazelas

!= 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.

-z la stringa nulla, cio, ha lunghezza zero -n la stringa non nulla.

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

Esempio 7-5. Confronti numerici e di stringhe


#!/bin/bash a=4 b=5 # # #+ # # #+ # Qui "a" e "b" possono essere trattate sia come interi che come stringhe. Ci si pu facilmente confondere tra i confronti numerici e quelli sulle stringhe, perch le variabili Bash non sono tipizzate. Bash consente le operazioni di interi e il confronto di variabili il cui valore composto solamente da cifre. Comunque attenzione, siete avvisati.

echo if [ "$a" -ne "$b" ]

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

Esempio 7-6. Vericare se una stringa nulla


#!/bin/bash # str-test.sh: Verifica di stringhe nulle e di stringhe senza quoting (*) # Utilizzando if [ ... ]

# 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.).

Esempio 7-7. zmore


#!/bin/bash #zmore # Visualizza i file gzip con NOARG=65 NONTROVATO=66 NONGZIP=67 if [ $# -eq 0 ] # stesso risultato di: if [ -z "$1" ] # $1 pu esserci, ma essere vuota: zmore "" arg2 arg3 then echo "Utilizzo: basename $0 nomefile" >&2 # Messaggio derrore allo stderr. exit $NOARG # Restituisce 65 come exit status dello script (codice derrore). fi nomefile=$1 if [ ! -f "$nomefile" ] # Il quoting di $nomefile mantiene gli spazi. then echo "File $nomefile non trovato!" >&2 # Messaggio derrore allo stderr. exit $NONTROVATO fi if [ ${nomefile##*.} != "gz" ] # Uso delle parentesi graffe nella sostituzione di variabile. then echo "Il file $1 non un file gzip!" exit $NONGZIP fi zcat $1 | more # Usa il filtro more. # Lo si pu sostituire con less, se si desidera. more

exit $?

# Lo script restituisce lexit status della pipe.

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.

-o or logico exp1 -o exp2 restituisce vero se vera o exp1 o exp2.

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.

7.4. Costrutti condizionali if/then annidati


possibile annidare i costrutti condizionali if/then. Il risultato lo stesso di quello ottenuto utilizzando loperatore di confronto composto && visto precedentemente.

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.

7.5. Test sulla conoscenza delle veriche


Il le di sistema xinitrc viene di solito impiegato, tra laltro, per mettere in esecuzione il server X. Questo le contiene un certo numero di costrutti if/then, come mostra il seguente frammento.

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

Capitolo 8. Operazioni ed argomenti correlati


8.1. Operatori
assegnamento
assegnamento di variabile Inizializzare o cambiare il valore di una variabile = Operatore di assegnamento multiuso, utilizzato sia per gli assegnamenti aritmetici che di stringhe.

var=27 categoria=minerali

# Non sono consentiti spazi n prima n dopo l"=".

Cautela
Non bisogna assolutamente confondere l= operatore di assegnamento con l= operatore di verica.

= come operatore di verifica

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

Capitolo 8. Operazioni ed argomenti correlati / diviso ** elevamento a potenza


# La versione 2.02 di Bash ha introdotto loperatore di elevamento a potenza let "z=5**3" echo "z = $z" "**".

# z = 125

% modulo, o mod (restituisce il resto di una divisione tra interi)

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.

# ---------------------------------------------------------# Verifica degli argomenti ARG=2 E_ERR_ARG=65

84

Capitolo 8. Operazioni ed argomenti correlati


if [ $# -ne "$ARG" ] then echo "Utilizzo: basename $0 primo-numero secondo-numero" exit $E_ERR_ARG fi # ----------------------------------------------------------

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.

mcd $1 $2 echo; echo "MCD di $1 e $2 = $dividendo"; echo

# 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

Capitolo 8. Operazioni ed argomenti correlati


#* Evitate questo costrutto perch obsoleto e non portabile. # Grazie, Stephane Chazelas. echo -n "$n " # Ora con gli operatori di incremento in stile C. # Grazie a Frank Wang per averlo segnalato. let "n++" echo -n "$n " (( n++ )) echo -n "$n " : $(( n++ )) echo -n "$n " : $[ n++ ] echo -n "$n " echo exit 0 # anche con let "++n".

# anche con (( ++n ).

# anche con : $(( ++n )).

# 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)

Dalla versione 2.05b, Bash supporta gli interi di 64 bit.

87

Capitolo 8. Operazioni ed argomenti correlati

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

Capitolo 8. Operazioni ed argomenti correlati


# Notate che loperatore || non consentito nel costrutto [ ... ].

Nota: Bash verica lexit status di ogni enunciato collegato con un operatore logico.

Esempio 8-3. Condizioni di verica composte utilizzando && e ||


#!/bin/bash a=24 b=47 if [ "$a" -eq 24 ] && [ "$b" -eq 47 ] then echo "Verifica nr.1 eseguita con successo." else echo "Verifica nr.1 fallita." fi # ERRORE: if [ "$a" -eq 24 && "$b" -eq 47 ] #+ cerca di eseguire [ "$a" -eq 24 #+ e fallisce nella ricerca di corrispondenza di ]. # # Nota: if [[ $a -eq 24 && $b -eq 24 ]] funziona # La verifica if con le doppie parentesi quadre pi flessibile #+ della versione con le paretesi quadre singole. # ("&&" ha un significato diverso nella riga 17 di quello della riga 6.). # Grazie a Stephane Chazelas per averlo evidenziato.

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

Capitolo 8. Operazioni ed argomenti correlati

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

Gli operatori && e || vengono utilizzati anche nel contesto matematico.

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

Capitolo 8. Operazioni ed argomenti correlati

8.2. Costanti numeriche


Lo script di shell interpreta un numero come numero decimale (base 10), tranne quando quel numero scritto in una notazione particolare: con un presso specico. Un numero preceduto da 0 un numero ottale (base 8). Un numero preceduto da 0x un numero esadecimale (base 16). Un numero contenente un # viene valutato come BASE#NUMERO (con limitazioni di notazione ed ampiezza). Esempio 8-4. Rappresentazione di costanti numeriche
#!/bin/bash # numbers.sh: Rappresentazione di numeri con basi differenti. # Decimale: quella preimpostata let "dec = 32" echo "numero decimale = $dec" # Qui non c niente di insolito.

# 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 + @ + _

echo echo $((36#zz)) $((2#10101010)) $((16#AF16)) $((53#1aA))

92

Capitolo 8. Operazioni ed argomenti correlati


# 1295 170 44822 3375

# # # #+

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

Part 3. Oltre i fondamenti

Capitolo 9. Variabili riviste


Utilizzate in modo appropriato, le variabili possono aumentare la potenza e la essibilit degli script. Per questo necessario conoscere tutte le loro sfumature e sottigliezze.

9.1. Variabili interne


Variabili builtin (incorporate) sono quelle variabili che determinano il comportamento dello script bash
$BASH

il percorso delleseguibile Bash


bash$ echo $BASH /bin/bash

$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

Capitolo 9. Variabili riviste


# (uguale a $MACHTYPE).

$BASH_VERSION

la versione Bash installata

bash$ echo $BASH_VERSION 3.00.14(1)-release

tcsh% echo $BASH_VERSION BASH_VERSION: Undefined variable.

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

leditor di testo predenito invocato da uno script, solitamente vi o emacs.


$EUID

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

Capitolo 9. Variabili riviste


$FUNCNAME

nome della funzione corrente

xyz23 () { echo "$FUNCNAME in esecuzione." } xyz23 echo "NOME FUNZIONE = $FUNCNAME"

# xyz23 in esecuzione.

# NOME FUNZIONE = # Valore nullo allesterno della funzione.

$GLOBIGNORE

un elenco di nomi di le da escludere dalla ricerca nel globbing


$GROUPS

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.

root# echo $GROUPS 0

root# echo ${GROUPS[1]} 1

root# echo ${GROUPS[5]} 6

$HOME

directory home dellutente, di solito /home/nomeutente (vedi Esempio 9-14)


$HOSTNAME

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

Capitolo 9. Variabili riviste


$HOSTTYPE

tipo di macchina Come $MACHTYPE, identica il sistema hardware, ma in forma ridotta.


bash$ echo $HOSTTYPE i686

$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.

bash$ echo $IFS | cat -vte $

bash$ bash -c set w x y z; IFS=":-;"; echo "$*" w:x:y:z

98

Capitolo 9. Variabili riviste

Cautela
$IFS non tratta la spaziatura allo stesso modo degli altri caratteri.

Esempio 9-1. $IFS e gli spazi


#!/bin/bash # $IFS gestisce gli spazi in modo diverso dagli altri caratteri. output_arg_uno_per_riga() { for arg do echo "[$arg]" done } echo; echo "IFS=\" \"" echo "-------" IFS=" " var=" a b c " output_arg_uno_per_riga $var # # [a] # [b] # [c]

# output_arg_uno_per_riga echo " a

echo; echo "IFS=:" echo "-----" IFS=: var=":a::b:c:::" output_arg_uno_per_riga $var # # [] # [a] # [] # [b] # [c] # [] # [] # []

# Come prima, ma con ":" anzich " ".

# In awk si ottiene lo stesso risultato con il separatore di campo "F # Grazie, Stephane Chazelas. echo exit 0

(Grazie, S. C., per i chiarimenti e gli esempi.)

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

tipo di macchina Identica il sistema hardware in modo dettagliato.


bash$ echo $MACHTYPE i486-slackware-linux-gnu

100

Capitolo 9. Variabili riviste


$OLDPWD

directory di lavoro precedente (OLD-print-working-directory, la directory in cui vi trovavate prima dellultimo comando cd)
$OSTYPE

nome del sistema operativo


bash$ echo $OSTYPE linux

$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).

bash$ echo $PATH /bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin:/sbin:/usr/sbin

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

Capitolo 9. Variabili riviste


bash$ echo $PIPESTATUS 141 bash$ ls -al | comando_errato bash: comando_errato: command not found bash$ echo $? 127

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

I comandi precedenti, eseguiti in uno script, avrebbero prodotto il risultato atteso 0 1 0.

Grazie a Wayne Pollock per la puntualizzazione e per aver fornito lesempio precedente.

Nota: La variabile $PIPESTATUS, in alcuni contesti, d risultati inaspettati.


bash$ echo $BASH_VERSION 3.00.14(1)-release bash$ $ ls | comando_errato | wc bash: comando_errato: command not found 0 0 0 bash$ echo ${PIPESTATUS[@]} 141 127 0

102

Capitolo 9. Variabili riviste


Chet Ramey attribuisce il risultato precedente al comportamento di ls. Se ls scrive in una pipe il cui output non viene letto, allora SIGPIPE lo termina, restituendo exit status 141. Altrimenti lexit status latteso 0. La stessa cosa vale per tr.

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

il prompt principale, quello che compare sulla riga di comando.


$PS2

Prompt secondario. Compare quando atteso un ulteriore input (il comando non ancora terminato). Viene visualizzato come >.

103

Capitolo 9. Variabili riviste


$PS3

Prompt di terzo livello, visualizzato in un ciclo select (vedi Esempio 10-29).


$PS4

Prompt di quarto livello. Viene visualizzato allinizio di ogni riga di output quando lo script stato invocato con lopzione -x. Viene visualizzato come +.
$PWD

Directory di lavoro (directory corrente) analoga al comando builtin 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

Capitolo 9. Variabili riviste


exit 0

$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

Numero di secondi trascorsi dallinizio dellesecuzione dello script.

#!/bin/bash TEMPO_LIMITE=10 INTERVALLO=1 echo echo "Premi Control-C per terminare prima di $TEMPO_LIMITE secondi." echo

105

Capitolo 9. Variabili riviste

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

lelenco delle opzioni di shell abilitate. una variabile in sola lettura


bash$ echo $SHELLOPTS braceexpand:hashall:histexpand:monitor:history:interactive-comments:emacs

$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

Capitolo 9. Variabili riviste


canzone="(nessuna risposta)" # Risposta preimpostata. fi echo "La tua canzone preferita $canzone."

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

Capitolo 9. Variabili riviste


} trap Int14Vettore 14 # Interrupt del timer (14) modificato allo scopo.

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

Unalternativa lutilizzo di stty. Esempio 9-3. Input temporizzato, un ulteriore esempio


#!/bin/bash # timeout.sh # Scritto da Stephane Chazelas #+ e modificato dallautore del libro. INTERVALLO=5 # intervallo di timeout

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

Capitolo 9. Variabili riviste


echo "Ti chiami $nome." else echo "Tempo scaduto." fi echo # Il comportamento di questo script un po diverso da "timed-input.sh". # Ad ogni pressione di tasto, la temporizzazione ricomincia da capo. exit 0

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

Capitolo 9. Variabili riviste Esempio 9-5. Sono root?


#!/bin/bash # am-i-root.sh: ROOT_UID=0

Sono root o no?

# Root ha $UID 0. # Il vero "root" avr la compiacenza #+ di aspettare?

if [ "$UID" -eq "$ROOT_UID" ]

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

Vedi anche Esempio 2-3.


Nota: Le variabili $ENV, $LOGNAME, $MAIL, $TERM, $USER, e $USERNAME non sono builtin di Bash. Vengono, comunque, impostate spesso come variabili dambiente in uno dei le di avvio di Bash. $SHELL, il nome della shell di login dellutente, pu essere impostata dal le /etc/passwd o da uno script init. Anche questa non un builtin di Bash.
tcsh% echo $LOGNAME bozo tcsh% echo $SHELL /bin/tcsh tcsh% echo $TERM rxvt bash$ echo $LOGNAME bozo bash$ echo $SHELL /bin/tcsh bash$ echo $TERM rxvt

110

Capitolo 9. Variabili riviste

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)
$*

Tutti i parametri posizionali visti come ununica parola


Nota: $* devessere usata con il quoting.

$@

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.

Esempio 9-6. arglist: Elenco degli argomenti con $* e $@


#!/bin/bash # arglist.sh # Invocate lo script con molti argomenti, come "uno due tre". E_ERR_ARG=65 if [ ! -n "$1" ] then echo "Utilizzo: basename $0 argomento1 argomento2 ecc." exit $E_ERR_ARG fi echo

111

Capitolo 9. Variabili riviste


indice=1 # Inizializza il contatore.

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.

Esempio 9-7. Comportamento incoerente di $* e $@


#!/bin/bash # Comportamento non corretto delle variabili interne Bash "$*" e "$@", #+ dipendente dal fatto che vengano utilizzate o meno con il "quoting". # Gestione incoerente della suddivisione delle parole e del ritorno a capo. set -- "Il primo" "secondo" "il:terzo" "" "Il: :quinto" # Imposta gli argomenti dello script, $1, $2, ecc. echo echo IFS con il valore preimpostato, utilizzando "$*" c=0 for i in "$*" # tra doppi apici do echo "$((c+=1)): [$i]" # Questa riga rimane invariata in tutti gli esempi. # Visualizza gli argomenti. done echo --echo IFS con il valore preimpostato, utilizzando $* c=0 for i in $* # senza apici do echo "$((c+=1)): [$i]" done echo --echo IFS con il valore preimpostato, utilizzando "$@" c=0 for i in "$@" do echo "$((c+=1)): [$i]" done echo --echo IFS con il valore preimpostato, utilizzando $@ c=0 for i in $@ do echo "$((c+=1)): [$i]" done echo ---

113

Capitolo 9. Variabili riviste

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

Capitolo 9. Variabili riviste


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 # Provate questo script con ksh o zsh -y. exit 0 # Script desempio di Stephane Chazelas, # con piccole modifiche apportate dallautore.

Nota: I parametri $@ e $* differiscono solo quando vengono posti tra doppi apici.

115

Capitolo 9. Variabili riviste Esempio 9-8. $* e $@ quando $IFS vuota


#!/bin/bash # Se $IFS impostata, ma vuota, allora "$*" e "$@" non #+ visualizzano i parametri posizionali come ci si aspetterebbe. mecho () # Visualizza i parametri posizionali. { echo "$1,$2,$3"; }

IFS="" set a b c mecho "$*" mecho $* mecho $@ mecho "$@"

# Impostata, ma vuota. # Parametri posizionali. # abc # a,b,c # a,b,c # a,b,c

# 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.

# Grazie Stephane Chazelas. exit 0

Altri parametri particolari


$-

Opzioni passate allo script (utilizzando set). Vedi Esempio 11-15.

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

Capitolo 9. Variabili riviste


$!

PID (ID di processo) dellultimo job eseguito in background

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"

# Grazie a Jacques Lederer, per il suggerimento.

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)

du >/dev/null echo $_ ls -al >/dev/null echo $_ : echo $_

# :

117

Capitolo 9. Variabili riviste


$?

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.

9.2. Manipolazione di stringhe

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.

Lunghezza della stringa


${#stringa}

expr length $stringa

expr "$stringa" : .*

stringaZ=abcABC123ABCabc echo ${#stringaZ} echo expr length $stringaZ echo expr "$stringaZ" : .* # 15 # 15 # 15

Esempio 9-10. Inserire una riga bianca tra i paragra di un le di testo


#!/bin/bash # paragraph-space.sh # Inserisce una riga bianca tra i paragrafi di un file di testo con #+ spaziatura semplice. # Utilizzo: $0 <NOMEFILE LUNMIN=45 # Potrebbe rendersi necessario modificare questo valore. # Si assume che le righe di lunghezza inferiore a $LUNMIN caratteri #+ siano le ultime dei paragrafi.

118

Capitolo 9. Variabili riviste

while read riga do echo "$riga"

# Per tutte le righe del file di input... # Visualizza la riga.

len=${#riga} if [ "$len" -lt "$LUNMIN" ] then echo # Aggiunge la riga bianca. fi done exit 0

Lunghezza della sottostringa vericata nella parte iniziale della stringa


expr match "$stringa" $sottostringa $sottostringa una espressione regolare. expr "$stringa" : $sottostringa $sottostringa unespressione regolare.

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.

stringaZ=abcABC123ABCabc echo expr index "$stringaZ" C12

# 6 # Posizione di C.

echo expr index "$stringaZ" 1c # 3 # c (in terza posizione) viene verificato prima di 1.

quasi uguale alla funzione strchr() del C.

119

Capitolo 9. Variabili riviste

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.

${stringa:posizione:lunghezza} Estrae una sottostringa di $lunghezza caratteri da $stringa 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.

echo ${*:2} echo ${@:2} echo ${*:2:3}

# #

Visualizza tutti i parametri iniziando dal secondo. Come prima.

# Visualizza tre parametri posizionali #+ iniziando dal secondo.

120

Capitolo 9. Variabili riviste

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

Capitolo 9. Variabili riviste

echo expr match "$stringaZ" .*\([A-C][A-C][A-C][a-c]*\) echo expr "$stringaZ" : .*\(......\)

# 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.

OPERAZIONE=macptopbm ESTENSIONE=pbm if [ -n "$1" ] then directory=$1 else directory=$PWD fi

# Nuova estensione dei nomi dei file.

# 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".

Una semplice emulazione di getopt utilizzando i costrutti di estrazione di sottostringa.

123

Capitolo 9. Variabili riviste Esempio 9-12. Emulare getopt


#!/bin/bash # getopt-simple.sh # Autore: Chris Morgan # Usato in Guida ASB con il suo consenso.

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

Capitolo 9. Variabili riviste

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}

9.2.1. Manipolare stringhe con awk


Uno script Bash pu ricorrere alle capacit di manipolazione delle stringhe di awk, come alternativa allutilizzo dei propri operatori builtin.

125

Capitolo 9. Variabili riviste Esempio 9-13. Modi alternativi di estrarre sottostringhe


#!/bin/bash # substring-extraction.sh Stringa=23skidoo1 # 012345678 Bash # 123456789 awk # Fate attenzione al diverso sistema di indicizzazione della stringa: # Bash numera il primo carattere della stringa con 0. # Awk numera il primo carattere della stringa con 1. echo ${Stringa:2:4} # posizione 3 (0-1-2), 4 caratteri di lunghezza # skid # Lequivalente awk di ${stringa:pos:lunghezza} #+ substr(stringa,pos,lunghezza). echo | awk { print substr(""${Stringa}"",3,4) # skid } # Collegando ad awk un semplice comando "echo" gli viene dato un #+ input posticcio, in questo modo non diventa pi necessario #+ fornirgli il nome di un file. exit 0

9.2.2. Ulteriori approfondimenti


Per altro materiale sulla manipolazione delle stringhe negli script, si faccia riferimento alla Sezione 9.3 e all importante sezione relativa allelenco dei comandi expr. Per gli script desempio, si veda: 1. Esempio 12-9 2. Esempio 9-16 3. Esempio 9-17 4. Esempio 9-18 5. Esempio 9-20

9.3. Sostituzione di parametro

126

Capitolo 9. Variabili riviste

Manipolare e/o espandere le variabili


${parametro} Uguale a $parametro, cio, valore della variabile parametro. In alcuni contesti funziona solo la forma meno ambigua ${parametro}. Pu essere utilizzato per concatenare delle stringhe alle variabili.

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

Capitolo 9. Variabili riviste


echo "nomeutente1 = ${nomeutente1-whoami}" # Viene visualizzato. nomeutente2= echo "nomeutente2 stata dichiarata, ma contiene un valore nullo." echo "nomeutente2 = ${nomeutente2:-whoami}" # # Viene visualizzato perch sono stati utilizzati :- al posto del semplice -. # Confrontatelo con il primo esempio visto sopra.

# # 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

Capitolo 9. Variabili riviste ${parametro=default} ${parametro:=default}

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.

echo ${nomeutente=whoami} # La variabile "nomeutente" stata ora impostata con whoami.

${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 =

param5= a=${param5:+xyz} echo "a = $a" # a = # Risultato diverso da a=${param5+xyz}

129

Capitolo 9. Variabili riviste

param6=123 a=${param6+xyz} echo "a = $a"

# 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.

Esempio 9-14. Sostituzione di parametro e messaggi derrore


#!/bin/bash # # # #+ Verifica alcune delle variabili dambiente di sistema. una buona misura preventiva. Se, per sempio, $USER, il nome dellutente corrente, non impostata, la macchina non pu riconoscervi.

: ${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

Capitolo 9. Variabili riviste


: ${ZZXy23AB?"ZZXy23AB non stata impostata."} # Se ZZXy23AB non stata impostata, #+ allora lo script termina con un messaggio derrore. # Il messaggio derrore pu essere specificato. # : ${nomevariabile?"MESSAGGIO DERRORE"}

# 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

# NON termina in questo punto.

# Infatti lo script restituisce come exit status (echo $?) 1.

Esempio 9-15. Sostituzione di parametro e messaggi utilizzo


#!/bin/bash # usage-message.sh : ${1?"Utilizzo: $0 ARGOMENTO"} # Lo script termina qui, se non vi un parametro da riga di comando, #+ e viene visualizzato il seguente messaggio derrore. # usage-message.sh: 1: Utilizzo: usage-message.sh ARGOMENTO echo "Queste due righe vengono visualizzate solo se stato fornito un argomento." echo "argomento da riga di comando = \"$1\"" exit 0 # Lo script termina a questo punto solo se stato #+ eseguito con largomento richiesto.

# 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

Capitolo 9. Variabili riviste

Lunghezza della variabile / rimozione di sottostringa


${#var} Lunghezza della stringa (numero dei caratteri di $var). Nel caso di un array, ${#array} rappresenta la lunghezza del primo elemento dellarray.
Nota: Eccezioni:

${#*} e ${#@} forniscono il numero dei parametri posizionali . Per gli array, ${#array[*]} e ${#array[@]} forniscono il numero degli elementi che compongono larray.

Esempio 9-16. Lunghezza di una variabile


#!/bin/bash # length.sh E_NO_ARG=65 if [ $# -eq 0 ] # Devono essere forniti degli argomenti allo script. then echo "Siete pregati di seguire lo script con uno o pi argomenti." exit $E_NO_ARG fi var01=abcdEFGH28ij echo "var01 = ${var01}" echo "Lunghezza di var01 = ${#var01}" # Proviamo ora ad inserire uno spazio. var02="abcd EFGH28ij" echo "var02 = ${var02}" echo "Lunghezza di var02 = ${#var02}" echo "Numero di argomenti passati allo script = ${#@}" echo "Numero di argomenti passati allo script = ${#*}" exit 0

${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

Capitolo 9. Variabili riviste

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.

Variante, pi elaborata dellesempio precedente, di Manfred Schwarb:


toglie_zero_iniziale2 () { shopt -s extglob local val=${1##+(0)} # Toglie possibili zeri iniziali, altrimenti #+ Bash interpreta tali numeri come valori ottali. # Abilita il globbing esteso. # Usa una variabile locale, verifica doccorrenza pi #+ lunga delle serie di 0. shopt -u extglob # Disabilita il globbing esteso. _toglie_zero_iniziale2=${val:-0} # Nel caso linput sia 0, restituisce 0 invece di "".

Altro esempio di utilizzo:


echo basename $PWD echo "${PWD##*/}" echo echo basename $0 echo $0 echo "${0##*/}" echo nomefile=test.dat echo "${nomefile##*.}" # Nome della directory di lavoro corrente. # Nome della directory di lavoro corrente. # Nome dello script. # Nome dello script. # Nome dello script.

# dat # Estensione del nome del file.

${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

Capitolo 9. Variabili riviste


var1=abcd12345abc6789 modello1=a*c # * (carattere jolly) verifica tutto quello che #+ compreso tra a - c. echo echo "var1 = $var1" echo "var1 = ${var1}"

# 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

Capitolo 9. Variabili riviste Esempio 9-18. Rinominare le estensioni dei le:


#!/bin/bash # rfe.sh: Rinomuinare le estensioni dei file. # # rfe vecchia_estensione nuova_estensione # # Esempio: # Per rinominare tutti i file *.gif della directory di lavoro in *.jpg, # rfe gif jpg

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

Espansione di variabile / Sostituzione di sottostringa


I costrutti seguenti sono stati adottati da ksh. ${var:pos} La variabile var viene espansa iniziando da pos. ${var:pos:lun} Espansione di un massimo di lun caratteri della variabile var , iniziando da pos. Vedi Esempio A-14 per una dimostrazione delluso creativo di questo operatore. ${var/Modello/Sostituto} La prima occorrenza di Modello in var viene rimpiazzata da Sostituto. Se si omette Sostituto allora la prima occorrenza di Modello viene rimpiazzata con niente, vale a dire, cancellata.

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

Capitolo 9. Variabili riviste


#+ basename non ne ha, ma dirname s, complica la faccenda. # (Grazie, S.C.) echo t=${percorso:11} echo "$percorso, senza i primi 11 caratteri = $t" t=${percorso:11:5} echo "$percorso, senza i primi 11 caratteri e ridotto alla \ lunghezza di 5 caratteri = $t" echo t=${percorso/bozo/clown} echo "$percorso con \"bozo\" sostituito da \"clown\" = $t" t=${percorso/oggi/} echo "$percorso con \"oggi\" cancellato = $t" t=${percorso//o/O} echo "$percorso con tutte le o minuscole cambiate in O maiuscole = $t" t=${percorso//o/} echo "$percorso da cui sono state cancellate tutte le o = $t" exit 0

${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

Capitolo 9. Variabili riviste


# echo # --------------------------------------------------------# La verifica deve avvenire allinizio/fine della stringa, #+ altrimenti non verr eseguita alcuna sostituzione. # --------------------------------------------------------v3=${v0/#123/000} # verificata, ma non allinizio. echo "v3 = $v3" # abc1234zip1234abc # NESSUNA SOSTITUZIONE. v4=${v0/%123/000} # stata verificata, ma non alla fine. echo "v4 = $v4" # abc1234zip1234abc # NESSUNA SOSTITUZIONE. exit 0 |----|

${!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

# La versione 2.04 di Bash possiede questa funzionalit.

9.4. Tipizzare le variabili: declare o typeset


I builtin declare o typeset (sono sinonimi esatti) consentono di limitare le propriet delle variabili. una forma molto debole di tipizzazione, se confrontata con quella disponibile per taluni linguaggi di programmazione. Il comando declare specico della versione 2 o successive di Bash. Il comando typeset funziona anche negli script ksh.

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 echo "n = $n" declare -i n n=6/3 echo "n = $n"

# n = 6/3

# n = 2

-a array

declare -a indici

La variabile indici verr trattata come un array.

-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

Capitolo 9. Variabili riviste


declare -f nome_funzione

Un declare -f nome_funzione elenca solo la funzione specicata.

-x export

declare -x var3

Dichiara la variabile come esportabile al di fuori dellambiente dello script stesso.

-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.

Esempio 9-21. Utilizzare declare per tipizzare le variabili


#!/bin/bash funz1 () { echo Questa una funzione. } declare -f echo declare -i var1 # var1 un intero. var1=2367 echo "var1 dichiarata come $var1" var1=var1+1 # La dichiarazione di intero elimina la necessit di #+ usare let. echo "var1 incrementata di 1 diventa $var1." # Tentativo di modificare il valore di una variabile dichiarata come intero. echo "Tentativo di modificare var1 nel valore in virgola mobile 2367.1." var1=2367.1 # Provoca un messaggio derrore, la variabile non cambia. echo "var1 ancora $var1" echo # Elenca la funzione precedente.

140

Capitolo 9. Variabili riviste

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.

# Grazie a Michael Iatrou, per il chiarimento.

141

Capitolo 9. Variabili riviste

9.5. Referenziazione indiretta delle variabili

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

Capitolo 9. Variabili riviste


# (Grazie a Stephane Chazelas, per aver chiarito il comportamento precedente.)

# 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" # ---------------------------------------------------------------------

reteRemota=$(eval reteRemota=$(eval reteRemota=$(eval reteRemota=$(eval echo "$reteRemota"

"echo "echo "echo "echo

\$$(echo isdn${isdnServizioOnline}ReteRemota)") \$$(echo isdnMioProviderReteRemota)") \$isdnMioProviderReteRemota") $isdnMioProviderReteRemota")

# 172.16.0.100

# ================================================================ # E fa ancor meglio.

# 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

Capitolo 9. Variabili riviste

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" . . .

Esempio 9-23. Passare una referenziazione indiretta a awk


#!/bin/bash # Altra versione dello script "column totaler" #+ che aggiunge una colonna (contenente numeri) nel file di destinazione. # Qui viene utilizzata la referenziazione indiretta. ARG=2 E_ERR_ARG=65 if [ $# -ne "$ARG" ] # Verifica il corretto nr. di argomenti da riga #+ di comando. then echo "Utilizzo: basename $0 nomefile numero_colonna" exit $E_ERR_ARG fi nomefile=$1 numero_colonna=$2 #===== Fino a questo punto uguale alloriginale =====#

# Script awk di pi di una riga vengono invocati con

awk .....

# Inizio script awk. # ---------------------------------------------------------awk " { totale += \$${numero_colonna} # referenziazione indiretta }

144

Capitolo 9. Variabili riviste


END { print totale } " "$nomefile" # -----------------------------------------------# Fine script awk. # La referenziazione indiretta evita le difficolt della referenziazione #+ di una variabile di shell allinterno di uno script awk incorporato. # Grazie, Stephane Chazelas. exit 0

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.

9.6. $RANDOM: genera un intero casuale


$RANDOM una funzione interna di Bash (non una costante) che restituisce un intero pseudocasuale 5

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

Capitolo 9. Variabili riviste


#+ interi casuali. do numero=$RANDOM echo $numero let "contatore += 1" # Incrementa il contatore. done echo "-----------------" # Se necessario un intero casuale entro un dato intervallo, si usa #+ loperatore modulo, che restituisce il resto di una divisione. INTERVALLO=500 echo numero=$RANDOM let "numero %= $INTERVALLO" # ^^ echo "Il numero casuale inferiore a $INTERVALLO echo

---

$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

Capitolo 9. Variabili riviste


numero=$RANDOM let "numero %= $INTERVALLO" # Riduce $numero entro $INTERVALLO. done echo "Numero casuale tra $LIMITE_INFERIORE e $INTERVALLO --- $numero" echo

# 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))]}

# $bozo sh pick-cards.sh # Fante di Fiori

# Grazie, "jipe," per aver puntualizzato questuso di $RANDOM. exit 0

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

Capitolo 9. Variabili riviste


numeroc=$((RANDOM%25+6)) # Generare un numero casuale, sempre nellintervallo 6 - 30, #+ ma che deve essere divisibile per 3. numeroc=$(((RANDOM%30/3+1)*3)) # # # da notare che questo non sempre funziona. Fallisce quando $RANDOM restituisce 0. Frank Wang suggerisce la seguente alternativa: numeroc=$(( RANDOM%27/3*3+6 ))

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

Capitolo 9. Variabili riviste


echo echo echo echo echo echo echo echo echo echo } local min=${1:-0} local max=${2:-32767} local divisibilePer=${3:-1} # Assegnazione dei valori preimpostati, nel caso di mancato passaggio #+ dei parametri alla funzione. local x local intervallo # Verifica che il valore di divisibilePer sia positivo. [ ${divisibilePer} -lt 0 ] && divisibilePer=$((0-divisibilePer)) # Controllo di sicurezza. if [ $# -gt 3 -o ${divisibilePer} -eq 0 -o sintassi return 1 fi # Verifica se min e max sono scambiati. if [ ${min} -gt ${max} ]; then # Li scambia. x=${min} min=${max} max=${x} fi # Se min non esattamente divisibile per $divisibilePer, #+ viene ricalcolato. if [ $((min/divisibilePer*divisibilePer)) -ne ${min} ]; then if [ ${min} -lt 0 ]; then min=$((min/divisibilePer*divisibilePer)) else min=$((((min/divisibilePer)+1)*divisibilePer)) fi fi # Se max non esattamente divisibile per $divisibilePer, #+ viene ricalcolato. if [ $((max/divisibilePer*divisibilePer)) -ne ${max} ]; then if [ ${max} -lt 0 ]; then max=$((((max/divisibilePer)-1)*divisibilePer)) " cio divisibile esattamente per questo numero."

"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."

${min} -eq ${max} ]; then

150

Capitolo 9. Variabili riviste


else max=$((max/divisibilePer*divisibilePer)) fi fi # # ----------------------------------------------------------------------Ora il lavoro vero.

# 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

Capitolo 9. Variabili riviste


else minimo=$((((minimo/divisibilePer)+1)*divisibilePer)) fi fi

# 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

# Controllo dei risultati

152

Capitolo 9. Variabili riviste

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

"tot_due += 1";; "tot_tre += 1";; "tot_quattro += 1";; "tot_cinque += 1";;

# Poich un dado non ha lo "zero", #+ lo facciamo corrispondere a 1. # 1 a 2, ecc.

153

Capitolo 9. Variabili riviste


5) let "tot_sei += 1";; esac } echo

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.

numeri_casuali () { contatore=0 while [ "$contatore" -lt "$MAX_NUMERI" ] do numero=$RANDOM

154

Capitolo 9. Variabili riviste


echo -n "$numero " let "contatore += 1" done } echo; echo RANDOM=1 numeri_casuali echo; echo RANDOM=1 numeri_casuali # Stesso seme... # ...riproduce esattamente la serie precedente. # # Ma, quant utile duplicare una serie di numeri "casuali"? # Impostazione del seme di RANDOM.

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

Capitolo 9. Variabili riviste


Esempio 9-29. Numeri pseudocasuali utilizzando awk
#!/bin/bash # random2.sh: Restituisce un numero pseudo-casuale nellintervallo 0 - 1. # Uso della funzione rand() di awk. SCRIPTAWK= { srand(); print rand() } # Comando(i) / parametri passati ad awk # Notate che srand() ricalcola il seme del generatore di numeri di awk.

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.

Anche il comando date si presta a generare sequenze di interi pseudocasuali.

9.7. Il costrutto doppie parentesi


Simile al comando let, il costrutto ((...)) consente lespansione e la valutazione aritmetica. Nella sua forma pi semplice, a=$(( 5 + 3 )) imposta a al valore 5 + 3, cio 8. Non solo, ma questo costrutto consente di gestire, in Bash, le variabili con la sintassi del linguaggio C. Esempio 9-30. Gestire le variabili in stile C
#!/bin/bash # Manipolare una variabile, in stile C, usando il costrutto ((...)).

echo (( a = 23 )) # Impostazione, in stile C, con gli spazi da entrambi i lati #+ dell "=". echo "a (valore iniziale) = $a"

156

Capitolo 9. Variabili riviste


(( a++ )) # Post-incremento di a, stile C. echo "a (dopo a++) = $a" (( a-- )) # Post-decremento di a, stile C. echo "a (dopo a--) = $a" (( ++a )) # Pre-incremento di a, stile C. echo "a (dopo ++a) = $a" (( --a )) # Pre-decremento di a, stile C. echo "a (dopo --a) = $a" echo ########################################################################### # Fate attenzione che, come nel C, gli operatoti di pre- e post-decremento #+ hanno effetti collaterali leggermente differenti. n=1; let --n && echo "Vero" || echo "Falso" n=1; let n-- && echo "Vero" || echo "Falso" # Falso # Vero

# 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.

Vedi anche Esempio 10-12.

157

Capitolo 9. Variabili riviste

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

Capitolo 10. Cicli ed alternative


Le operazioni sui blocchi di codice sono la chiave per creare script di shell ben strutturati e organizzati. I costrutti per gestire i cicli e le scelte sono gli strumenti che consentono di raggiungere questo risultato.

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.

for arg in [lista] do comando(i)... done

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

Capitolo 10. Cicli ed alternative for arg in [lista] ; do

Esempio 10-1. Semplici cicli for


#!/bin/bash # Elenco di pianeti. for pianeta in Mercurio Venere Terra Marte Giove Saturno Urano Nettuno Plutone do echo $pianeta # Ogni pianeta su una riga diversa done echo for pianeta in "Mercurio Venere Terra Marte Giove Saturno Urano Nettuno Plutone" # Tutti i pianeti su ununica riga. # Lintera "lista" racchiusa tra apici doppi crea ununica variabile. do echo $pianeta done exit 0

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

Capitolo 10. Cicli ed alternative

##-------due done

tab---- servono a concatenare gli zeri al parametro $2

# (Grazie, S.C., per i chiarimenti aggiuntivi.) exit 0

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

Capitolo 10. Cicli ed alternative

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:

Localizza le stringhe in un file binario.

# 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"

# Su suggerimento di Paulo Marcel Coelho Aragao.

163

Capitolo 10. Cicli ed alternative


for parola in $( strings "$2" | grep "$1" ) # Il comando "strings" elenca le stringhe nei file binari. # Loutput viene collegato (pipe) a "grep" che verifica la stringa cercata. do echo $parola done # Come ha sottolineato S.C., le righe 23 - 29 potrebbero essere #+ sostituite con la pi semplice # strings "$2" | grep "$1" | tr -s "$IFS" [\n*]

# Provate qualcosa come #+ con questo script. exit 0

"./bin-grep.sh mem /bin/ls"

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

# # # # #

UTENTE UTENTE UTENTE ... UTENTE

nr.1 = root nr.2 = bin nr.3 = daemon nr.30 = bozo

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"

# Vede quali file sono della FSF.

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

Capitolo 10. Cicli ed alternative


do echo "$file" done | sort # Se manca sort, lelenco #+ non verr ordinato. # Per essere precisi, in realt in questo caso un ciclo non sarebbe necessario, #+ perch loutput del comando "find" viene espanso in ununica parola. # Tuttavia, illustra bene questa modalit e ne facilita la comprensione. # #+ #+ # Come ha evidenziato Dominik Aeneas Schnitzer, se non si usa il "quoting" per $( find $directory -type l ) i nomi dei file contenenti spazi non vengono visualizzati correttamente. Il nome viene troncato al primo spazio incontrato.

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

stdout del ciclo rediretto al file di memorizzazione.

166

Capitolo 10. Cicli ed alternative


exit 0

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 "$".

# Un costrutto preso in prestito da ksh93.

Vedi anche Esempio 26-15, Esempio 26-16 e Esempio A-6.

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

fax make $2 for file in $(ls $2.0*) do fil="$fil $file" done

# Crea file fax formattati dai file di testo. # Concatena i file appena creati. # Usa il carattere jolly in lista.

efax -d /dev/ttyS3 -o1 -t "T$1" $fil

# 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

Capitolo 10. Cicli ed alternative while [condizione] do comando... done

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

Esempio 10-15. Un altro ciclo while


#!/bin/bash echo while [ "$var1" != "fine" ] do echo "Immetti la variabile #1 read var1 echo "variabile #1 = $var1" # # Equivalente a: while test "$var1" != "fine"

(fine per terminare) " # Non read $var1 (perch?). # necessario il "quoting"

169

Capitolo 10. Cicli ed alternative


#+ per la presenza di "#"... # Se linput fine, viene visualizzato a questo punto. # La verifica per linterruzione del ciclo, infatti, posta allinizio. echo done exit 0

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

Capitolo 10. Cicli ed alternative


let "a+=1" done echo; echo # +=================================================================+ # Rifatto con la sintassi del C. ((a = 1)) # a=1 # Le doppie parentesi consentono gli spazi nellimpostazione di una #+ variabile, come in C. while (( a <= LIMITE )) do echo -n "$a " ((a += 1)) # let "a+=1" # Si. # Le doppie parentesi consentono di incrementare una variabile #+ con la sintassi del C. done echo # Ora i programmatori in C si sentiranno a casa loro anche con Bash. exit 0 # Doppie parentesi senza "$" che precede #+ il nome della variabile.

# Fin qui nessuna novit.

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).

until [condizione-falsa] do comando... done

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

Esempio 10-18. Ciclo until


#!/bin/bash CONDIZIONE_CONCLUSIONE=fine until [ "$var1" = "$CONDIZIONE_CONCLUSIONE" ] # Condizione di verifica allinizio del ciclo. do echo "Immetti variabile nr.1 " echo "($CONDIZIONE_CONCLUSIONE per terminare)" read var1 echo "variabile nr.1 = $var1" echo done exit 0

10.2. Cicli annidati


Un ciclo annidato un ciclo in un ciclo, vale a dire un ciclo posto allinterno del corpo di un altro (chiamato ciclo esterno). Al suo primo passo, il ciclo esterno mette in esecuzione quello interno che esegue il proprio blocco di codice no alla conclusione. Quindi, al secondo passo, il ciclo esterno rimette in esecuzione quello interno. Questo si ripete nch il ciclo esterno non termina. Naturalmente, un break contenuto nel ciclo nterno o in quello esterno, pu interrompere lintero processo. Esempio 10-19. Cicli annidati
#!/bin/bash # nested-loop.sh: Cicli "for" annidati. esterno=1 # Imposta il contatore del ciclo esterno.

# Inizio del ciclo esterno. for a in 1 2 3 4 5

172

Capitolo 10. Cicli ed alternative


do echo "Passo $esterno del ciclo esterno." echo "---------------------" interno=1 # Imposta il contatore del ciclo interno. # ============================================================== # Inizio del ciclo interno. for b in 1 2 3 4 5 do echo "Passo $interno del ciclo interno." let "interno+=1" # Incrementa il contatore del ciclo interno. done # Fine del ciclo interno. # ============================================================== let "esterno+=1" echo # Incrementa il contatore del ciclo esterno. # Spaziatura tra gli output dei successivi #+ passi del ciclo esterno.

done # Fine del ciclo esterno. exit 0

Vedi Esempio 26-11 per unillustrazione di cicli while annidati e Esempio 26-13 per un ciclo while annidato in un ciclo until.

10.3. Controllo del ciclo


Comandi inerenti al comportamento del ciclo
break continue I comandi di controllo del ciclo break e continue 1 corrispondono esattamente ai loro analoghi degli altri linguaggi di programmazione. Il comando break interrompe il ciclo (esce), mentre continue provoca il salto alliterazione (ripetizione) successiva, tralasciando tutti i restanti comandi di quel particolare passo del ciclo. Esempio 10-20. Effetti di break e continue in un ciclo
#!/bin/bash LIMITE=19 # Limite superiore

echo echo "Visualizza i numeri da 1 fino a 20 (saltando 3 e 11)." a=0 while [ $a -le "$LIMITE" ]

173

Capitolo 10. Cicli ed alternative


do a=$(($a+1)) if [ "$a" -eq 3 ] || [ "$a" -eq 11 ] # Esclude 3 e 11. then continue # Salta la parte restante di questa particolare #+ iterazione del ciclo. fi echo -n "$a " # Non visualizza 3 e 11. done # Esercizio: # Perch il ciclo visualizza fino a 20? echo; echo echo Visualizza i numeri da 1 a 20, ma succede qualcosa dopo il 2. ################################################################## # Stesso ciclo, ma sostituendo continue con break. a=0 while [ "$a" -le "$LIMITE" ] do a=$(($a+1)) if [ "$a" -gt 2 ] then break # Salta lintero ciclo. fi echo -n "$a " done echo; echo; echo exit 0

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

Capitolo 10. Cicli ed alternative


do echo -n "Gruppo $cicloesterno: "

#--------------------------------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

Capitolo 10. Cicli ed alternative

done echo; echo # Esercizio: # Trovate un valido uso di "continue N" in uno script. exit 0

Esempio 10-23. Uso di continue N in un caso reale


# Albert Reiner fornisce un esempio di come usare "continue N": # --------------------------------------------------------# #+ #+ #+ #+ #+ Supponiamo di avere un numero elevato di job che devono essere eseguiti, con tutti i dati che devono essere trattati contenuti in un file, che ha un certo nome ed inserito in una data directory. Ci sono diverse macchine che hanno accesso a questa directory e voglio distribuire il lavoro su tutte queste macchine. Per far questo, solitamente, utilizzo nohup con il codice seguente su ogni macchina:

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

Capitolo 10. Cicli ed alternative


break done # #+ #+ #+ #+ #+ #+ #+ #+ #+ #+ #+ #+ # Oppure sleep 600 per evitare la conclusione.

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.

10.4. Veriche ed alternative


I costrutti case e select, tecnicamente parlando, non sono cicli, dal momento che non iterano lesecuzione di un blocco di codice. Come i cicli, tuttavia, hanno la capacit di dirigere il usso del programma in base alle condizioni elencate dallinizio alla ne del blocco.

Controllo del usso del programma in un blocco di codice


case (in) / esac Il costrutto case lequivalente di scripting di shell di switch del C/C++. Permette di dirigere il usso del programma ad uno dei diversi blocchi di codice, in base alle condizioni di verica. una specie di scorciatoia di enunciati if/then/else multipli e uno strumento adatto per creare menu.

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).

Esempio 10-24. Impiego di case


#!/bin/bash # Verificare intervalli di caratteri. echo; echo "Premi un tasto e poi invio." read Tasto case "$Tasto" in [[:lower:]] ) echo "Lettera minuscola";; [[:upper:]] ) echo "Lettera maiuscola";; [0-9] ) echo "Cifra";; * ) echo "Punteggiatura, spaziatura, o altro";; esac # Sono permessi gli intervalli di caratteri se #+ compresi tra [parentesi quadre] #+ o nel formato POSIX tra [[doppie parentesi quadre. # #+ #+ # #+ # # La prima versione di questesempio usava, per indicare gli intervalli di caratteri minuscoli e maiuscoli, le forme [a-z] e [A-Z]. Questo non pi possibile nel caso di particolari impostazioni locali e/o distribuzioni Linux. POSIX consente una maggiore portabilit. Grazie a Frank Wang per averlo evidenziato.

# 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

Capitolo 10. Cicli ed alternative


#+ visualizzi ogni tasto premuto e termini solo quando viene digitata una "X". # Suggerimento: racchiudete tutto in un ciclo "while". exit 0

Esempio 10-25. Creare menu utilizzando case


#!/bin/bash # Un database di indirizzi non molto elegante clear # Pulisce lo schermo. echo echo echo echo echo echo echo echo echo " Elenco Contatti" " ------ --------" "Scegliete una delle persone seguenti:" "[E]vans, Roland" "[J]ones, Mildred" "[S]mith, Julie" "[Z]ane, Morris"

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

Capitolo 10. Cicli ed alternative

# 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

while [ $# -gt 0 ]; do case "$1" in -d|--debug)

# Finch ci sono parametri . . .

180

Capitolo 10. Cicli ed alternative


# "-d" o "--debug" parametro? DEBUG=1 ;; -c|--conf) FILECONF="$2" shift if [ ! -f $FILECONF ]; then echo "Errore: il file indicato non esiste!" exit $E_ERR_FILECONF # Errore di file non trovato. fi ;; esac shift done # Verifica la serie successiva di parametri.

# Dallo script "Log2Rot" di Stefano Falsetto, #+ parte del suo pacchetto "rottlog". # Usato con il consenso dellautore.

Esempio 10-26. Usare la sostituzione di comando per creare la variabile di case


#!/bin/bash # case-cmd.sh: usare la sostituzione di comando per creare la variabile #+ di "case". case $( arch ) in i386 i486 i586 i686 * esac ) ) ) ) ) echo echo echo echo echo # "arch" restituisce larchitettura della macchina. # Equivale a uname -m... "Macchina con processore 80386";; "Macchina con processore 80486";; "Macchina con processore Pentium";; "Macchina con processore Pentium2+";; "Altro tipo di macchina";;

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

Capitolo 10. Cicli ed alternative

case "$1" in "$2") return $UGUALE;; * ) return $NONUGUALE;; esac }

a=uno b=due c=tre d=due

verifica_stringa $a echo $? verifica_stringa $a $b echo $? verifica_stringa $b $d echo $?

# numero di parametri errato # 91 # diverse # 90 # uguali # 0

exit 0

Esempio 10-28. Vericare un input alfabetico


#!/bin/bash # isalpha.sh: Utilizzare la struttura "case" per filtrare una stringa. SUCCESSO=0 FALLIMENTO=-1 isalpha () # Verifica se il *primo carattere* della stringa #+ di input una lettera. # Nessun argomento passato?

{ if [ -z "$1" ] then return $FALLIMENTO fi

case "$1" in [a-zA-Z]*) return $SUCCESSO;; # Inizia con una lettera? * ) return $FALLIMENTO;; esac } # Confrontatelo con la funzione "isalpha ()" del C.

isalpha2 () {

# Verifica se l*intera stringa* composta da lettere.

182

Capitolo 10. Cicli ed alternative


[ $# -eq 1 ] || return $FALLIMENTO case $1 in *[!a-zA-Z]*|"") return $FALLIMENTO;; *) return $SUCCESSO;; esac } isdigit () # Verifica se l*intera stringa* formata da cifre. { # In altre parole, verifica se una variabile numerica. [ $# -eq 1 ] || return $FALLIMENTO case $1 in *[!0-9]*|"") return $FALLIMENTO;; *) return $SUCCESSO;; esac }

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

Capitolo 10. Cicli ed alternative


b=H3llo c=-Cosa? d=Cosa? e=echo $b f=AbcDef g=27234 h=27a34 i=27.34

# 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

# Script perfezionato da S.C.

# 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.

select variabile [in lista] do comando... break done

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

Capitolo 10. Cicli ed alternative


# exit 0 passati alla funzione scelta_di()

Vedi anche Esempio 34-3.

Note
1. Sono builtin di shell, mentre altri comandi di ciclo, come while e case, sono parole chiave.

186

Capitolo 11. Comandi interni e builtin


Un builtin un comando appartenente alla serie degli strumenti Bash, letteralmente incorporato. Questo stato fatto sia per motivi di efcienza -- i builtin eseguono pi rapidamente il loro compito di quanto non facciano i comandi esterni, che di solito devono generare un processo separato (forking) -- sia perch particolari builtin necessitano di un accesso diretto alle parti interne della shell.

187

Capitolo 11. Comandi interni e builtin

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

2000 reflect.au 2000 seconds.au

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

Capitolo 11. Comandi interni e builtin

# 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.

Esempio 11-2. printf in azione


#!/bin/bash # printf demo PI=3,14159265358979 CostanteDecimale=31373 Messaggio1="Saluti," # Vedi nota a fine listato

191

Capitolo 11. Comandi interni e builtin


Messaggio2="un abitante della Terra." echo printf "Pi con 2 cifre decimali = %1.2f" $PI echo printf "Pi con 9 cifre decimali = %1.9f" $PI

# Esegue anche il corretto #+ arrotondamento. # # Esegue un ritorno a capo, equivale a echo.

printf "\n"

printf "Costante = \t%d\n" $CostanteDecimale

# Inserisce un carattere #+ di tabulazione (\t)

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.

Unutile applicazione di printf quella di impaginare i messaggi derrore

E_ERR_DIR=65 var=directory_inesistente errore() { printf "$@" >&2 # Organizza i parametri posizionali passati e li invia allo stderr. echo

192

Capitolo 11. Comandi interni e builtin


exit $E_ERR_DIR } cd $var || errore $"Non riesco a cambiare in %s." "$var" # Grazie, S.C.

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

Capitolo 11. Comandi interni e builtin


read var echo "\"var\" = "$var"" # Tutto come ci si aspetta. # -------------------------- # echo # ---------------------------------------------------------------- # echo -n "Immetti un altro valore: " read # Non viene fornita alcuna variabile a read, #+ quindi... linput di read viene assegnato alla #+ variabile predefinita $REPLY. var="$REPLY" echo "\"var\" = "$var"" # Stesso risultato del primo blocco di codice. # ---------------------------------------------------------------- # echo exit 0

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

Capitolo 11. Comandi interni e builtin

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

Capitolo 11. Comandi interni e builtin


if [ "$?" -eq $SUCCESSO ] then echo " stato premuto il tasto Freccia-su." exit $SUCCESSO fi echo -n "$tasto" | grep "$frecciagi" if [ "$?" -eq $SUCCESSO ] then echo " stato premuto il tasto Freccia-gi." exit $SUCCESSO fi echo -n "$tasto" | grep "$frecciadestra" if [ "$?" -eq $SUCCESSO ] then echo " stato premuto il tasto Freccia-destra." exit $SUCCESSO fi echo -n "$tasto" | grep "$frecciasinistra" if [ "$?" -eq $SUCCESSO ] then echo " stato premuto il tasto Freccia-sinistra." exit $SUCCESSO fi echo -n "$tasto" | grep "$ins" if [ "$?" -eq $SUCCESSO ] then echo " stato premuto il tasto \"Ins\"." exit $SUCCESSO fi echo -n "$tasto" | grep "$canc" if [ "$?" -eq $SUCCESSO ] then echo " stato premuto il tasto \"Canc\"." exit $SUCCESSO fi

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

Capitolo 11. Comandi interni e builtin


#+ corrispondere a quelli della vostra tastiera. # Verificateli e quindi, modificate lesercizio in modo che funzioni #+ correttamente.

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

Capitolo 11. Comandi interni e builtin


while read name passwd uid gid fullname ignore do echo "$name ($fullname)" done <etc/passwd # Redirezione I/O. IFS=$OIFS # Ripristina il valore originario di $IFS. # Anche questo frammento di codice di Heiner Steven.

# 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

Capitolo 11. Comandi interni e builtin


exit 0 # Fine del codice. # Segue loutput (parziale) dello script. # echo fornisce le parentesi graffe aggiuntive.

######################################################## ./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.

(cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xpvf -)

[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

Capitolo 11. Comandi interni e builtin

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

Capitolo 11. Comandi interni e builtin


dir2=/var/spool pushd $dir1 # Viene eseguito un dirs automatico (visualizza lo stack delle #+ directory allo stdout). echo "Ora sei nella directory pwd." # Uso degli apici singoli #+ inversi per pwd. # Ora si fa qualcosa nella directory dir1. pushd $dir2 echo "Ora sei nella directory pwd." # Adesso si fa qualcosaltro nella directory dir2. echo "Nella posizione pi alta dellarray DIRSTACK si trova $DIRSTACK." popd echo "Sei ritornato alla directory pwd." # Ora si fa qualche altra cosa nella directory dir1. popd echo "Sei tornato alla directory di lavoro originaria pwd." exit 0 # Cosa succede se non eseguite popd -- prima di uscire dallo script? # In quale directory vi trovereste alla fine? Perch?

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

Capitolo 11. Comandi interni e builtin


let "a -= 5" echo "32 - 5 = $a" # Equivale a # 27 let "a = a - 5"

let "a = a * 10" # Equivale a echo "27 * 10 = $a" # 270

let "a = a * 10"

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

Esempio 11-11. Dimostrazione degli effetti di eval


#!/bin/bash y=eval ls -l echo $y echo echo "$y" # Simile a y=ls -l #+ ma con i ritorni a capo tolti perch la variabile #+ "visualizzata" senza "quoting". # I ritorni a capo vengono mantenuti con il #+ "quoting" della variabile.

echo; echo y=eval df echo $y # Simile a y=df #+ ma senza ritorni a capo.

# Se non si preservano i ritorni a capo, la verifica delloutput #+ con utility come "awk" risulta pi facile.

202

Capitolo 11. Comandi interni e builtin

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

Esempio 11-12. Forzare un log-off


#!/bin/bash Terminare ppp per forzare uno scollegamento. Lo script deve essere eseguito da root. terminappp="eval kill -9 ps ax | awk /ppp/ { print $1 }" # ----- ID di processo di ppp -----$terminappp # La variabile diventata un comando.

# 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

Capitolo 11. Comandi interni e builtin


rm /var/lock/LCK..ttyS3 # Cancella il lock file della porta seriale. Perch? exit 0 # Esercizi: # -------# 1) Lo script deve verificare se stato root ad invocarlo. # 2) Effettuate un controllo per verificare che, prima di tentarne la chiusura, #+ il processo che deve essere terminato sia effettivamente in esecuzione. # 3) Scrivete una versione alternativa dello script basata su fuser: #+ if [ fuser -s /dev/modem ]; then . . .

Esempio 11-13. Una versione di rot13


#!/bin/bash # Una versione di "rot13" usando eval. # Confrontatelo con lesempio "rot13.sh". impvar_rot_13() # Codifica "rot13" { local nomevar=$1 valoreval=$2 eval $nomevar=$(echo "$valoreval" | tr a-z n-za-m) }

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

# Esempio di Stephane Chazelas. # Modificato dallautore del documento. exit 0

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

Capitolo 11. Comandi interni e builtin

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;

### Correzioni allesempio originale eseguite da Paulo Marcel Coelho Aragao.

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

"Parametri "Argomento "Argomento "Argomento

posizionali prima di set nr.1 da riga di comando = nr.2 da riga di comando = nr.3 da riga di comando =

\uname -a\ :" $1" $2" $3"

set uname -a

# Imposta i parametri posizionali alloutput # del comando uname -a # Sconosciuto

echo $_

205

Capitolo 11. Comandi interni e builtin


# Opzioni impostate nello script. echo "Parametri posizionali dopo set \uname -a\ :" # $1, $2, $3, ecc. reinizializzati col risultato di uname -a echo "Campo nr.1 di uname -a = $1" echo "Campo nr.2 di uname -a = $2" echo "Campo nr.3 di uname -a = $3" echo --echo $_ # --echo exit 0

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"

# uno # due # tre quattro cinque

206

Capitolo 11. Comandi interni e builtin


echo; echo # Ancora. set -- $variabile primo_param=$1 secondo_param=$2 echo "primo parametro = $primo_param" echo "secondo parametro = $secondo_param"

# 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

# (valore nullo) # (valore nullo)

Vedi anche Esempio 10-2 e Esempio 12-51.

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$ unset PATH bash$ echo $PATH

bash$

Esempio 11-17. Annullare una variabile


#!/bin/bash # unset.sh: Annullare una variabile. variabile=ciao echo "variabile = $variabile" unset variabile echo "variabile (annullata) = $variabile" exit 0 # Inizializzata.

# Annullata. # Stesso effetto di: variabile= # $variabile nulla.

207

Capitolo 11. Comandi interni e builtin

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

Capitolo 11. Comandi interni e builtin


Suggerimento: possibile inizializzare ed esportare variabili con ununica operazione, come export var1=xxx. Tuttavia, come ha sottolineato Greg Keraunen, in certe situazioni questo pu avere un effetto diverso da quello che si avrebbe impostando prima la variabile ed esportandola successivamente.
bash$ export var=(a b); echo ${var[0]} (a b)

bash$ var=(a b); export var; echo ${var[0]} a

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

Capitolo 11. Comandi interni e builtin


2. La struttura di getopts differisce leggermente da un normale ciclo while perch non presente la condizione di verica. 3. Il costrutto getopts sostituisce il deprecato comando esterno getopt.

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

Capitolo 11. Comandi interni e builtin


# # Se unopzione si aspetta un argomento ("flag:"), viene presa qualunque cosa si trovi vicino.

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."

Comportamento dello Script


source . (comando punto ) Questa istruzione, se invocata da riga di comando, esegue uno script. Allinterno di uno script, source nome-file carica il le nome-file. Caricando un le (comando-punto) si importa

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

Capitolo 11. Comandi interni e builtin


# Errore, se largomento assente. fi echo until [ -z "$1" ] do # Scorre gli argomenti passati alla funzione. echo -n "$1" # Visualizza gli argomenti uno alla volta, eliminando i ritorni a capo. echo -n " " # Inserisce degli spazi tra le parole. shift # Successivo. done echo return 0 }

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

Capitolo 11. Comandi interni e builtin


#+ (e perfino pericolosa). # Sarebbe meglio impostare $conta_passi a 0 prima che venga incrementata. while [ "$conta_passi" -le $MAXPASSCNT ] do . $0 # Lo script "esegue" se stesso, non chiama se stesso. # ./$0 (che sarebbe la vera ricorsivit) in questo caso non funziona. # Perch? done # #+ #+ #+ # # #+ Quello che avviene in questo script non una vera ricorsivit, perch lo script in realt "espande" se stesso, vale a dire genera una nuova sezione di codice ad ogni passaggio attraverso il ciclo while, con ogni source che si trova alla riga 20. Naturalmente, lo script interpreta ogni succesiva esecuzione della riga con "#!" come un commento e non come linizio di un nuovo script.

echo exit 0 # Il risultato finale un conteggio da 1 a 100. # Molto impressionante.

# 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

Capitolo 11. Comandi interni e builtin Esempio 11-22. Effetti di exec


#!/bin/bash exec echo "Uscita da \"$0\"." # Esce dallo script in questo punto. # -------------------------------------------# Le righe seguenti non verranno mai eseguite. echo "Questo messaggio non verr mai visualizzato." exit 99 # # #+ # Questo script non termina qui. Verificate lexit status, dopo che lo script terminato, con echo $?. *Non* sar 99.

Esempio 11-23. Uno script che esegue se stesso con exec


#!/bin/bash # self-exec.sh echo echo "Sebbene questa riga compaia UNA SOLA VOLTA nello script, continuer" echo "ad essere visualizzata." echo "Il PID di questo script desempio ancora $$." # Dimostra che non viene generata una subshell. echo "==================== Premi Ctl-C per uscire ====================" sleep 1 exec $0 # Inizia unaltra istanza di questo stesso script #+ che sostituisce quella precedente. # Perch?

echo "Questa riga non verr mai visualizzata" exit 0

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.

# 9 main test.sh # ^ # ^^^^ # ^^^^^^^ caller 0

Numero della riga dove la funzione stata richiamata. Invocata dalla parte "main" dello script. Nome dello script chiamante.

# Non ha alcun effetto perch non in una funzione.

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

Capitolo 11. Comandi interni e builtin


# Ciclo infinito while true # alternativa a ":" do operazione-1 operazione-2 ... operazione-n # Occorre un sistema per uscire dal ciclo, altrimenti lo script si blocca. done

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.

bash$ type [ [ is a shell builtin bash$ type -a [ [ is a shell builtin [ is /usr/bin/[

217

Capitolo 11. Comandi interni e builtin

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.

11.1. Comandi di controllo dei job


Alcuni dei seguenti comandi di controllo di job possono avere come argomento un identicatore di job. Vedi la tabella alla ne del capitolo. jobs Elenca i job in esecuzione in background, fornendo il rispettivo numero. Non cos utile come ps.
Nota: facilissimo confondere job e processi . Alcuni builtin, quali kill, disown e wait, accettano come argomento sia il numero di job che quello di processo. I comandi fg, bg e jobs accettano solo il numero di job.
bash$ sleep 100 & [1] 1384 bash $ jobs [1]+ Running

sleep 100 &

218

Capitolo 11. Comandi interni e builtin


1 il numero di job (i job sono gestiti dalla shell corrente), mentre 1384 il numero di processo (i processi sono gestiti dal sistema operativo). Per terminare questo job/processo si pu utilizzare sia kill %1 che kill 1384. Grazie, S.C.

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

echo "Aggiornamento del database locate ..."

219

Capitolo 11. Comandi interni e builtin


echo "Questo richiede un po di tempo." updatedb /usr & # Deve essere eseguito come root. wait # Non viene eseguita la parte restante dello script finch updatedb non #+ ha terminato il proprio compito. # Si vuole che il database sia aggiornato prima di cercare un nome di file. locate $1 # Senza il comando wait, nellipotesi peggiore, lo script sarebbe uscito #+ mentre updatedb era ancora in esecuzione, trasformandolo in un processo #+ orfano. exit 0

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 _

34 Oct 11 15:09 test.sh

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

34 Oct 11 15:09 test.sh

220

Capitolo 11. Comandi interni e builtin


Un altro modo per far fronte a questo problema quello di redirigere loutput del comando in un le o anche in /dev/null.

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

Capitolo 11. Comandi interni e builtin


anche kill -15. Un processo zombie, vale a dire un processo il cui genitore stato terminato, non pu essere ucciso (non si pu uccidere qualcosa che gi morto). Comunque init presto o tardi, solitamente lo canceller.

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).

Tabella 11-1. Identicatori di job Notazione


%N %S

Signicato numero associato al job [N] Chiamata (da riga di comando) del job che inizia con la stringa S

222

Capitolo 11. Comandi interni e builtin Notazione


%?S %% %+ %$!

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.

7. Lo stesso risultato di autoload pu essere ottenuto con typeset -fu.

223

Capitolo 12. Filtri, programmi e comandi esterni


I comandi standard UNIX rendono gli script di shell pi versatili. La potenza degli script deriva dallabbinare, in semplici costrutti di programmazione, comandi di sistema e direttive di shell.

12.1. Comandi fondamentali


I primi comandi che il principiante deve conoscere
ls Il comando fondamentale per elencare i le. molto facile sottostimare la potenza di questo umile comando. Per esempio, luso dellopzione -R, ricorsivo, con ls provvede ad elencare la directory in forma di struttura ad albero. Altre utili opzioni sono: -S, per ordinare lelenco in base alla dimensione, -t, per ordinarlo in base alla data di modica e -i per mostrare gli inode dei le (vedi Esempio 12-4). Esempio 12-1. Utilizzare ls per creare un sommario da salvare in un CDR
#!/bin/bash # ex40.sh (burn-cd.sh) # Script per rendere automatica la registrazione di un CDR. VELOC=2 # Potete utilizzare una velocit pi elevata #+ se lhardware la supporta. FILEIMMAGINE=cdimage.iso CONTENUTIFILE=contenuti DISPOSITIVO=cdrom # DISPOSITIVO="0,0" Per le vecchie versioni di cdrecord DEFAULTDIR=/opt # Questa la directory contenente i dati da registrare. # Accertatevi che esista. # Esercizio: aggiungente un controllo che lo verifichi. # Viene usato il programma "cdrecord" di Joerg Schilling: # http://www.fokus.fhg.de/usr/schilling/cdrecord.html # Se questo script viene eseguito da un utente ordinario va impostato #+ il bit suid di cdrecord (chmod u+s /usr/bin/cdrecord, da root). # Naturalmente questo crea una falla nella sicurezza, anche se non rilevante. if [ -z "$1" ] then DIRECTORY_IMMAGINE=$DEFAULTDIR # Viene usata la directory predefinita se non ne viene specificata #+ alcuna da riga di comando. else DIRECTORY_IMMAGINE=$1 fi

224

Capitolo 12. Filtri, programmi e comandi esterni

# 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.

bash$ cat file1.txt Questa la riga 1. Questa la riga 2.

bash$ tac file1.txt Questa la riga 2. Questa la riga 1.

bash$ rev file1.txt .1 agir al atseuQ .2 agir al atseuQ

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

Capitolo 12. Filtri, programmi e comandi esterni


bash$ mv directory_iniziale directory_destinazione bash$ ls -lF directory_destinazione total 1 drwxrwxr-x 2 bozo bozo 1024 May 28 19:20 directory_iniziale/

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

Un metodo alternativo far predere il nome del le da -- .


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# chattr +i file1.txt

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

Capitolo 12. Filtri, programmi e comandi esterni

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

Capitolo 12. Filtri, programmi e comandi esterni

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.

12.2. Comandi complessi


Comandi per utenti avanzati
nd -exec COMANDO \; Esegue COMANDO su ogni le vericato da nd. La sintassi del comando termina con ; (il ; deve essere preceduto dal carattere di escape per essere certi che la shell lo passi a nd col suo signicato letterale, evitandone la reinterpretazione come carattere speciale).

bash$ find ~/ -name *.txt /home/bozo/.kde/share/apps/karm/karmdata.txt /home/bozo/misc/irmeyc.txt /home/bozo/test-scripts/1.txt

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

Capitolo 12. Filtri, programmi e comandi esterni


find "$DIR" -type f -atime +5 -exec rm {} \; # ^^ # Le parentesi graffe rappresentano il percorso completo prodotto da "find." # # Cancella tutti il file in "/home/bozo/junk_files" #+ a cui non si acceduto da almeno 5 giorni. # # "-type tipofile", dove # f = file regolare # d = directory, ecc. # (La pagina di manuale di find contiene lelenco completo.)

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

Capitolo 12. Filtri, programmi e comandi esterni

# 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.)

Esempio 12-4. Cancellare un le tramite il suo numero di inode


#!/bin/bash # idelete.sh: Cancellare un file per mezzo del suo numero di inode. # Questo si rivela utile quando il nome del file inizia con un #+ carattere scorretto, come ? o -. CONTA_ARG=1 E_ERR_ARG=70 E_FILE_NON_ESISTE=71 E_CAMBIO_IDEA=72 if [ $# -ne "$CONTA_ARG" ] then echo "Utilizzo: basename $0 nomefile" exit $E_ERR_ARG fi if [ ! -e "$1" ] then echo "Il file \""$1"\" non esiste." exit $E_FILE_NON_ESISTE fi inum=ls -i | grep "$1" | awk {print $1} # inum = numero di inode (index node) del file # --------------------------------------------------------------# Tutti i file posseggono un inode, la registrazione che contiene #+ informazioni sullindirizzo fisico del file stesso. # --------------------------------------------------------------echo; echo -n "Sei assolutamente sicuro di voler cancellare \"$1\"(s/n)?" # Anche rm con lopzione -v visualizza la stessa domanda. read risposta case "$risposta" in # Allo script deve essere passato come argomento #+ il nome del file.

233

Capitolo 12. Filtri, programmi e comandi esterni


[nN]) echo "Hai cambiato idea, vero?" exit $E_CAMBIO_IDEA ;; *) echo "Cancello il file \"$1\".";; esac find . -inum $inum -exec rm {} \; # ^^ # Le parentesi graffe sono il segnaposto #+ per il testo prodotto da "find." echo "Il file "\"$1"\" stato cancellato!" exit 0

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

0 Jan 29 23:58 file1 0 Jan 29 23:58 file2

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

Capitolo 12. Filtri, programmi e comandi esterni


# --------# Modificate lo script in modo che registri i cambiamenti avvenuti #+ in /var/log/messages ad intervalli di venti minuti. # Suggerimento: usate il comando "watch".

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

Esempio 12-7. Terminare un processo usando il suo nome


#!/bin/bash # kill-byname.sh: Terminare i processi tramite i loro nomi. # Confrontate questo script con kill-process.sh. # Ad esempio, #+ provate "./kill-byname.sh xterm" -#+ e vedrete scomparire dal vostro desktop tutti gli xterm. # # Attenzione: ----------

236

Capitolo 12. Filtri, programmi e comandi esterni


# Si tratta di uno script veramente pericoloso. # Eseguirlo distrattamente (specialmente da root) #+ pu causare perdita di dati ed altri effetti indesiderati. E_NOARG=66 if test -z "$1" # Nessun argomento fornito da riga di comando? then echo "Utilizzo: basename $0 Processo(i)_da_terminare" exit $E_NOARG fi

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 $?

Esempio 12-8. Analisi di frequenza delle parole utilizzando xargs


#!/bin/bash # wf2.sh: Analisi sommaria della frequenza delle parole in un file di testo. # Usa xargs per scomporre le righe del testo in parole singole. # Confrontate questesempio con lo script "wf.sh" che viene dopo.

# 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

Capitolo 12. Filtri, programmi e comandi esterni

######################################################## 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

# Verifica luguaglianza. # 0 ( $x -ne $y )

239

Capitolo 12. Filtri, programmi e comandi esterni


a=3 b=expr $a \> 10 echo b=expr $a \> 10, quindi... echo "Se a > 10, b = 0 ((falso)" echo "b = $b" # 0 ( 3 ! -gt 10 ) echo b=expr $a \< 10 echo "Se a < 10, b = 1 (vero)" echo "b = $b" # 1 ( 3 -lt 10 ) echo # Notate luso dellescaping degli operatori. b=expr $a \<= 3 echo "Se a <= 3, b = 1 (vero)" echo "b = $b" # 1 ( 3 -le 3 ) # Esiste anche loperatore "\>=" (maggiore di o uguale a).

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

Capitolo 12. Filtri, programmi e comandi esterni


b=expr match "$a" [0-9]* # echo "Il numero di cifre allinizio b=expr match "$a" \([0-9]*\) # # == == #+ echo "Le cifre allinizio di \"$a\" echo exit 0 Conteggio numerico. di \"$a\" $b." Notate che le parentesi con lescape consentono la verifica della sottostringa. sono \"$b\"."

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

Capitolo 12. Filtri, programmi e comandi esterni

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.

12.3. Comandi per ora/data


Ora/data e calcolo del tempo
date La semplice invocazione di date visualizza la data e lora allo stdout. Linteresse per questo comando deriva dalluso delle sue opzioni di formato e verica. Esempio 12-10. Utilizzo di date
#!/bin/bash # Esercitarsi con il comando date echo "Il numero di giorni trascorsi dallinizio dellanno date +%j." # necessario il + per il formato delloutput. # %j fornisce i giorni dallinizio dellanno. echo "Il numero di secondi trascorsi dal 01/01/1970 date +%s." # %s contiene il numero di secondi dallinizio della "UNIX epoch", ma #+ quanto pu essere utile? prefisso=temp suffisso=$(date +%s) # Lopzione "+%s" di date una specifica GNU. nomefile=$prefisso.$suffisso

242

Capitolo 12. Filtri, programmi e comandi esterni


echo $nomefile # importantissima per creare nomi di file temporanei "univoci", persino #+ migliore delluso di $$. # Leggete la pagina di manuale di date per le altre opzioni di formato. exit 0

Lopzione -u fornisce il tempo UTC (Universal Coordinated Time).

bash$ date dom mag 11 17:55:55 CEST 2003

bash$ date -u dom mag 11 15:56:08 UTC 2003

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.

Esistono molte altre opzioni (eseguite man date).


date +%j # Visualizza i giorni dellanno (i giorni trascorsi dal 1 gennaio). date +%k%M # Visualizza lora e i minuti nel formato 24-ore, come unica stringa di cifre.

# 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

Capitolo 12. Filtri, programmi e comandi esterni

Vedi anche Esempio 3-4.

zdump Controllo dellora di zona: visualizza lora di una zona specicata.

bash$ zdump EST EST Sun May 11 11:01:53 2003 EST

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

Si veda anche il comando, molto simile, times nella sezione precedente.


Nota: Dalla versione 2.0 di Bash, time diventata una parola riservata di shell, con un comportamento leggermente diverso se usato con una pipe.

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.

bash$ at 2:30 am Friday < at-elenco-job job 2 at 2000-10-27 02:30

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

Capitolo 12. Filtri, programmi e comandi esterni

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.

Questo comando fa parte del pacchetto Red Hat initscripts / rc-scripts.

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.

12.4. Comandi per lelaborazione del testo


Comandi riguardanti il testo ed i le di testo
sort Classicatore di le, spesso usato come ltro in una pipe. Questo comando ordina un usso di testo, o un le, in senso crescente o decrescente, o secondo le diverse interpretazioni o posizioni dei caratteri. Usato con lopzione -m unisce, in un unico le, i le di input precedentemente ordinati. La sua pagina info ne elenca le funzionalit e le molteplici opzioni. Vedi Esempio 10-9, Esempio 10-10 e Esempio A-8.

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

una due due tre tre tre

sola volta. volte. volte. volte. volte. volte.

bash$ uniq -c 1 Questa 2 Questa 3 Questa

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

Capitolo 12. Filtri, programmi e comandi esterni

# 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

Capitolo 12. Filtri, programmi e comandi esterni


# + punteggiatura, come i punti e virgola. # 2) Modificatelo per filtrare anche gli spazi multipli e gli altri # + caratteri di spaziatura.

bash$ cat fileprova Questa riga presente Questa riga presente Questa riga presente Questa riga presente Questa riga presente Questa riga presente

una due due tre tre tre

sola volta. volte. volte. volte. volte. volte.

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

Uso di cut per visualizzare la versione del SO e del kernel:

249

Capitolo 12. Filtri, programmi e comandi esterni


uname -a | cut -d" " -f1,3,11,12

Usare cut per estrarre le intestazioni dei messaggi da una cartella e-mail:
bash$ grep ^Subject: read-messages | cut -c10-80 Re: Linux suitable for mission-critical apps? MAKE MILLIONS WORKING AT HOME!!! Spam complaint Re: Spam complaint

Usare cut per la verica di un le:


# Elenca tutti gli utenti presenti nel file /etc/passwd. FILE=/etc/passwd for utente in $(cut -d: -f1 $FILE) do echo $utente done # Grazie, Oleg Philon per il suggerimento.

cut -d -f2,3 nomefile equivale a awk -F[ ] { print $2, $3 } nomefile


Nota: anche possibile usare la_capo come delimitatore. Il trucco consiste nellinserire veramente un a_capo (INVIO) nella sequenza dei comandi.
bash$ cut -d

-f3,7,19 filetesto
Questa la riga 3 di filetesto. Questa la riga 7 di filetesto. Questa la riga 19 di filetesto.

Grazie a Jaka Kranjc per la precisazione.

Vedi anche Esempio 12-43.

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: 1.dat 100 Scarpe 200 Lacci 300 Calze

File: 2.dat 100 EUR 40.00 200 EUR 1.00 300 EUR 2.00

bash$ join 1.dat 2.dat File: 1.dat 2.dat 100 Scarpe EUR 40.00 200 Lacci EUR 1.00 300 Calze EUR 2.00

Nota: Il campo comune, nelloutput, compare una sola volta.

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.

Esempio 12-13. Generare numeri casuali di 10 cifre


#!/bin/bash # rnd.sh: Visualizza un numero casuale di 10 cifre # Script di Stephane Chazelas. head -c4 /dev/urandom | od -N4 -tu4 | sed -ne 1s/.* //p

# ===================================================================== # # Analisi # --------

252

Capitolo 12. Filtri, programmi e comandi esterni


# head: # lopzione -c4 considera solamente i primi 4 byte. # od: # Lopzione -N4 limita loutput a 4 byte. # Lopzione -tu4 seleziona, per loutput, il formato decimale senza segno. # sed: # Lopzione -n, in combinazione con lopzione "p" del comando "s", prende #+ in considerazione, per loutput, solo le righe verificate.

# 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

Capitolo 12. Filtri, programmi e comandi esterni


# # # # #+ #+ niente (verifica la riga) niente (verifica la riga) s/.* // q (quit)

In questo esempio, sed legge solo la sua prima riga di input. Esegue entrambi i comandi e visualizza la riga (con la sostituzione) prima di uscire (a causa del comando "q"), perch non gli stata passata 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

Capitolo 12. Filtri, programmi e comandi esterni

Vedi anche Esempio 12-5, Esempio 12-35 e Esempio 29-6.

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

Capitolo 12. Filtri, programmi e comandi esterni


6:The GPL governs the distribution of the Linux operating system.

Lopzione -v (o --invert-match) scarta le righe vericate.


grep modello1 *.txt | grep -v modello2 # Verifica tutte le righe dei file "*.txt" contenenti "modello1", # ma ***non*** quelle contenenti "modello2".

Lopzione -c (--count) fornisce il numero delle occorrenze, ma non le visualizza.


grep -c txt *.sgml # ((numero di occorrenze di "txt" nei file "*.sgml")

# grep -cz . # ^ punto # significa conteggio (-c) zero-diviso (-z) elementi da cercare "." # cio, quelli non vuoti (contenenti almeno 1 carattere). # printf a b\nc d\n\n\n\n\n\000\n\000e\000\000\nf | grep -cz . # 3 printf a b\nc d\n\n\n\n\n\000\n\000e\000\000\nf | grep -cz $ # 5 printf a b\nc d\n\n\n\n\n\000\n\000e\000\000\nf | grep -cz ^ # 5 # printf a b\nc d\n\n\n\n\n\000\n\000e\000\000\nf | grep -c $ # 9 # Per default, i caratteri di a capo (\n) separano gli elementi da cercare. # Notate che 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

Capitolo 12. Filtri, programmi e comandi esterni


osinfo.txt:The GPL governs the distribution of the Linux operating system.

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

Capitolo 12. Filtri, programmi e comandi esterni


echo $output # sed -ne "/$1/s|^|${file}: |p" equivale al precedente.

fi echo done echo exit 0

# Esercizi: # --------# 1) Aggiungete nuove righe di output nel caso ci sia pi di una #+ occorrenza per il file dato. # 2) Aggiungete altre funzionalit.

possibile far ricercare a grep due (o pi) differenti modelli? Cosa si pu fare se volessimo che grep visualizzi tutte le righe di un 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.

Ora cerchiamo nel le le righe contenenti entrambe le parole le e testo . . .


bash$ grep file tstfile # File: tstfile Questo un file desempio. Questo un file di testo ordinario. Questo file non contiene testo strano. Questo file non insolito.. bash$ grep file tstfile | grep testo Questo un file di testo ordinario. Questo file non contiene testo strano.

--

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

Capitolo 12. Filtri, programmi e comandi esterni


echo echo echo exit fi "la parte rimanente in minuscolo." "--------------------------------------------" "Esempi: Abandon, Dictionary, Marking, ecc." $E_ERR_ARG

if [ -z "$2" ]

# Potete specificare un dizionario #+ diverso come argomento #+ dello script.

then dictfile=$DEFAULT_DICTFILE else dictfile="$2" fi # --------------------------------------------------------Definizione=$(fgrep -A $MAXRIGHE "$1 \\" "$dictfile") # Definizioni nella forma "Voce \..." # # E, s, "fgrep" sufficientemente veloce #+ anche nella ricerca di un file di testo molto grande.

# Ora seleziona la parte inirente alla definizione. echo "$Definizione" | sed -n 1,/^[A-Z]/p | # Visualizza dalla prima riga della definizione #+ fino alla prima riga della voce successiva. sed $d | sed $d # Cancellando le ultime due righe #+ (la riga vuota e la prima riga della voce successiva). # --------------------------------------------------------exit 0 # # # # # # # # # # # # # Esercizi: -------1) Modificate lo script in modo che accetti un input alfabetico arbitrario + (lettere maiuscole, minuscole o alternate) che verr convertito + nel formato usato per 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

Capitolo 12. Filtri, programmi e comandi esterni


#+ precedente comando "exit".

# Stephane Chazelas propone la seguente, e pi concisa, alternativa: while read parola && [[ $parola != fine ]] do if look "$parola" > /dev/null then echo "\"$parola\" valida." else echo "\"$parola\" non valida." fi done <"$file" exit 0

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.

tr ltro per la sostituzione di caratteri.

263

Capitolo 12. Filtri, programmi e comandi esterni

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

tr -d 0-9 <nomefile # Cancella tutte le cifre dal file "nomefile".

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.

bash$ echo "acfdeb123" | tr -c b-d + +c+d+b++++

importante notare che tr riconosce le classi di caratteri POSIX. 3

bash$ echo "abcd2ef1" | tr [:alpha:] ----2--1

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 $?

# Controlla tutti i file della directory.

# #+ # #+

Cambia il nome del file in tutte lettere minuscole. Rinomina solo quei file che non sono gi in minuscolo.

265

Capitolo 12. Filtri, programmi e comandi esterni

# 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 $?

Esempio 12-20. Du: Conversione di le di testo DOS al formato UNIX


#!/bin/bash # Du.sh: converte i file di testo DOS in formato UNIX . E_ERR_ARG=65 if [ -z "$1" ] then echo "Utilizzo: basename $0 nomefile-da-convertire" exit $E_ERR_ARG fi NUOVONOMEFILE=$1.unx CR=\015 # # # # Ritorno a capo. 015 il codice ottale ASCII di CR Le righe dei file di testo DOS terminano con un CR-LF. Le righe dei file di testo UNIX terminano con il solo LF.

tr -d $CR < $1 > $NUOVONOMEFILE

266

Capitolo 12. Filtri, programmi e comandi esterni


# Cancella i CR e scrive il file nuovo. echo "Il file di testo originale DOS \"$1\"." echo "Il file di testo tradotto in formato UNIX \"$NOMENUOVOFILE\"." exit 0 # Esercizio: #----------# Modificate lo script per la conversione inversa (da UNIX a DOS).

Esempio 12-21. rot13: cifratura ultra-debole


#!/bin/bash # rot13.sh: Classico algoritmo rot13, cifratura che potrebbe beffare solo un # bambino di 3 anni. # Utilizzo: ./rot13.sh nomefile # o ./rot13.sh <nomefile # o ./rot13.sh e fornire linput da tastiera (stdin) cat "$@" | tr a-zA-Z n-za-mN-ZA-M # "a" corrisponde a "n", "b" a "o", ecc. # Il costrutto cat "$@" consente di gestire un input proveniente sia dallo #+ stdin che da un file. exit 0

Esempio 12-22. Generare Rompicapi Cifrati di frasi celebri


#!/bin/bash # crypto-quote.sh: Cifra citazioni # Cifra frasi famose mediante una semplice sostituzione monoalfabetica. # Il risultato simile ai rompicapo "Crypto Quote" delle pagine Op Ed #+ del Sunday.

chiave=ETAOINSHRDLUBCFGJMQPVWZYXK # La "chiave" non 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

Capitolo 12. Filtri, programmi e comandi esterni


# # # # # # # Provate lo script con qualcosa di simile a "Nothing so needs reforming as other peoples habits." --Mark Twain Il risultato : "CFPHRCS QF CIIOQ MINFMBRCS EQ FPHIM GIFGUIQ HETRPQ." --BEML PZERC

# Per decodificarlo: # cat "$@" | tr "$chiave" "A-Z"

# Questa semplice cifratura pu essere spezzata da un dodicenne con il #+ semplice uso di carta e penna. exit 0 # # # #+ Esercizio: --------Modificate lo script in modo che sia in grado sia di cifrare che di decifrare, in base al(i) argomento(i) passato(i) da riga di comando.

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

Capitolo 12. Filtri, programmi e comandi esterni

# Si sarebbe potuto fare anche con # echo $b | fold - -s -w $AMPIEZZA exit 0

Vedi anche Esempio 12-5.


Suggerimento: Una potente alternativa a fmt lutility par di Kamil Toman, disponibile presso http://www.cs.berkeley.edu/~amc/Par/.

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

Capitolo 12. Filtri, programmi e comandi esterni

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

Capitolo 12. Filtri, programmi e comandi esterni


# Dalla pagina di manuale di groff. # --------------------------# Se la pagina di manuale include tabelle e/o equazioni, #+ allora il precedente codice non funzioner. # La riga seguente in grado di gestire tali casi. # # gtbl < "$1" | geqn -Tlatin1 | groff -Tlatin1 -mtty-char -man # # Grazie, S.C. exit 0

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.

12.5. Comandi inerenti ai le e allarchiviazione


Archiviazione
tar lutility standard di archiviazione UNIX. 4 Dalloriginale programma per il salvataggio su nastro (Tape ARchiving), si trasformata in un pacchetto con funzionalit pi generali che pu gestire ogni genere di archiviazione con qualsiasi tipo di dispositivo di destinazione, dai dispositivi a nastro ai le regolari no allo stdout (vedi Esempio 3-4). Tar GNU stato implementato per accettare vari ltri di compressione, ad esempio tar czvf nome_archivio.tar.gz * che archivia ricorsivamente e comprime con gzip tutti i le, tranne quelli il cui nome inizia con un punto (dotle), della directory di lavoro corrente ($PWD). 5 Alcune utili opzioni di tar: 1. -c crea (un nuovo archivio) 2. -x estrae (le da un archivio esistente) 3. --delete cancella (le da un archivio esistente)

273

Capitolo 12. Filtri, programmi e comandi esterni

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-utils docbook-utils-0.6.9-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

# File temporaneo con nome "univoco". # $$ lID di processo dello script.

276

Capitolo 12. Filtri, programmi e comandi esterni


rpm2cpio < $1 > $TEMPFILE cpio --make-directories -F $TEMPFILE -i rm -f $TEMPFILE exit 0 # # #+ #+ # Esercizio: Aggiungete dei controlli per verificare se 1) "file_archivio" esiste e 2) veramente un archivio rpm. Suggerimento: verificate loutput del comando file. # Converte larchivio rpm in #+ un archivio cpio. # Decomprime larchivio cpio. # Cancella larchivio cpio.

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.

Vedi anche Esempio 7-7.

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

Capitolo 12. Filtri, programmi e comandi esterni

# 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

Esempio 12-29. Togliere i commenti da sorgenti C


#!/bin/bash # strip-comment.sh: Toglie i commenti (/* COMMENTO */) in un programma C. E_NOARG=0 E_ERR_ARG=66 E_TIPO_FILE_ERRATO=67 if [ $# -eq "$E_NOARG" ] then echo "Utilizzo: basename $0 file-C" >&2 # exit $E_ERR_ARG fi

Messaggio derrore allo stderr.

# 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

Capitolo 12. Filtri, programmi e comandi esterni


# Script sed piuttosto criptico: #-------sed /^\/\*/d /.*\*\//d $1 #-------# Facile da capire, se dedicate diverse ore ad imparare i fondamenti di sed.

# 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

Esempio 12-30. Esplorare /usr/X11R6/bin


#!/bin/bash # Cosa sono tutti quei misteriosi eseguibili in /usr/X11R6/bin? DIRECTORY="/usr/X11R6/bin" # Provate anche "/bin", "/usr/bin", "/usr/local/bin", ecc. for file in $DIRECTORY/* do whatis basename $file # Visualizza le informazione sugli eseguibili. done exit 0 # Potreste desiderare di redirigere loutput di questo script, cos: # ./what.sh >>whatis.db # o visualizzarne una pagina alla volta allo stdout,

281

Capitolo 12. Filtri, programmi e comandi esterni


# ./what.sh | less

Vedi anche Esempio 10-3.

vdir Visualizza lelenco dettagliato delle directory. Leffetto simile a ls -l. Questa una delle leutils GNU.

bash$ vdir total 10 -rw-r--r--rw-r--r--rw-r--r-bash ls -l total 10 -rw-r--r--rw-r--r--rw-r--r--

1 bozo 1 bozo 1 bozo

bozo bozo bozo

4034 Jul 18 22:04 data1.xrolo 4602 May 25 13:58 data1.xrolo.bak 877 Dec 17 2000 employment.xrolo

1 bozo 1 bozo 1 bozo

bozo bozo bozo

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

readlink Rivela il le a cui punta un link simbolico.

bash$ readlink /usr/bin/awk ../../bin/gawk

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

Capitolo 12. Filtri, programmi e comandi esterni


#+ # #+ #+ # #+ # # #+ # riducendo ad una sola le Z multiple consecutive. "tr -s \173-\377 Z" trasforma tutti i caratteri oltre la z in Z, riducendo ad una sola le Z multiple consecutive, liberandoci cos di tutti i caratteri strani che la precedente istruzione non riuscita a trattare. Infine, "tr Z " trasforma tutte queste Z in spazi, che saranno considerati separatori di parole dal ciclo che segue. ********************************************************** Notate la tecnica di concatenare diversi tr, ma con argomenti e/o opzioni differenti ad ogni passaggio. **********************************************************

for parola in $elenco

# # # #

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.

Patch del kernel:

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

Suggerimento: Si usa zdiff per confrontare le compressi con gzip.

285

Capitolo 12. Filtri, programmi e comandi esterni

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.

bash$ diff3 ==== 1:1c Questa 2:1c Questa 3:1c Questa

file-1 file-2 file-3

la riga 1 di "file-1". la riga 1 di "file-2". la riga 1 di "file-3"

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.

Esempio 12-32. Utilizzare cmp in uno script per confrontare due le


#!/bin/bash ARG=2 # Lo script si aspetta due argomenti. E_ERR_ARG=65 E_NONLEGGIBILE=66 if [ $# -ne "$ARG" ] then echo "Utilizzo: basename $0 file1 file2" exit $E_ERR_ARG fi if [[ ! -r "$1" || ! -r "$2" ]] then echo "Entrambi i file, per essere confrontati, devono esistere"

286

Capitolo 12. Filtri, programmi e comandi esterni


echo "ed avere i permessi di lettura." exit $E_NONLEGGIBILE fi cmp $1 $2 &> /dev/null # /dev/null elimina la visualizzazione del #+ risultato del comando"cmp". cmp -s $1 $2 ottiene lo stesso risultato (opzione "-s" di "cmp") Grazie Anders Gustavsson per averlo evidenziato.

# # # # Funziona anche con diff, vale a dire, diff $1 $2 &> /dev/null

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

Suggerimento: Si usa zcmp per i le compressi con gzip.

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.

Alcune opzioni consentono la soppressione di una o pi colonne di output.


-1 -2 -3

sopprime la colonna 1 sopprime la colonna 2 sopprime la colonna 3 sopprime entrambe le colonne 1 e 2, ecc.

-12

287

Capitolo 12. Filtri, programmi e comandi esterni

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).

Esempio 12-33. basename e dirname


#!/bin/bash a=/home/bozo/daily-journal.txt echo "Basename di /home/bozo/daily-journal.txt = basename $a" echo "Dirname di /home/bozo/daily-journal.txt = dirname $a" echo echo "La mia cartella personale basename ~/." # funziona anche basename ~. echo "La directory della mia cartella personale dirname ~/." # funziona anche dirname ~. exit 0

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.

Esempio 12-34. Vericare lintegrit dei le


#!/bin/bash # file-integrity.sh: Verifica se i file di una data directory # sono stati modificati senza autorizzazione. E_DIR_ERRATA=70 E_ERR_DBFILE=71 dbfile=File_record.md5 # Nome del file che contiene le registrazioni (file database).

crea_database () { echo ""$directory"" > "$dbfile"

289

Capitolo 12. Filtri, programmi e comandi esterni


# Scrive il nome della directory come prima riga di dbfile. md5sum "$directory"/* >> "$dbfile" # Accoda le checksum md5 e i nomi dei file. } verifica_database () { local n=0 local nomefile local checksum # --------------------------------------------------------- # # Questa verifica potrebbe anche non essere necessaria, ma #+ meglio essere pignoli che rischiare. if [ ! -r "$dbfile" ] then echo "Impossibile leggere il database delle checksum!" exit $E_ERR_DBFILE fi # --------------------------------------------------------- # while read record[n] do directory_verificata="${record[0]}" if [ "$directory_verificata" != "$directory" ] then echo "Le directory non corrispondono!" # E stato indicato un nome di directory sbagliato. exit $E_DIR_ERRATA fi if [ "$n" -gt 0 ] # Non il nome della directory. then nomefile[n]=$( echo ${record[$n]} | awk { print $2 } ) # md5sum scrive nel primo campo la checksum, nel #+ secondo il nome del file. checksum[n]=$( md5sum "${nomefile[n]}" )

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

Capitolo 12. Filtri, programmi e comandi esterni


then echo "${nomefile[n]} : CHECKSUM ERRATA!" # Il file stato modificato dallultima verifica. fi fi

let "n+=1" done <"$dbfile" }

# Legge il database delle checksum.

# ============================================================= # # main () if [ -z "$1" ] then directory="$PWD" else directory="$1" fi

# Se non altrimenti specificata, usa la #+ directory di lavoro corrente.

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

Capitolo 12. Filtri, programmi e comandi esterni


Nota: Essendoci state delle notizie che segnalano che la codica md5sum a 128 bit stata spezzata, non si pu che dare il benvenuto, tra gli strumenti per il calcolo della checksum, al pi sicuro sha1sum a 160 bit. Alcuni consulenti per la sicurezza sono dellopinione che anche sha1sum possa essere compromessa. Per cui, a quando la prossima utility a 512 bit?
bash$ md5sum filetesto e181e2c8720c60522c4c4c981108e367 filetesto

bash$ sha1sum filetesto 5d7425a9c08a66c3177f1e31286fa40986ffc996

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

Capitolo 12. Filtri, programmi e comandi esterni


do ricerca1=head -$righe $File | ricerca2=tail -$righe $File | # Decodifica i file che hanno #+ in quella finale. if [ "$ricerca1" -gt 0 ] then if [ "$ricerca2" -gt 0 ] then echo "Decodifico con uudecode $File fi fi done grep begin | wc -w grep end | wc -w un "begin" nella parte iniziale e un "end"

uudecode - $File -"

# 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

Capitolo 12. Filtri, programmi e comandi esterni

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.

bash$ mktemp /tmp/tmp.zzsvql3154

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 . . . ^^

12.6. Comandi per comunicazioni


Alcuni dei comandi che seguono vengono utilizzati per la caccia agli spammer, cos come per il trasferimento di dati e per lanalisi della rete.

Informazioni e statistiche
host Cerca informazioni su un host Internet per mezzo del nome o dellindirizzo IP usando il DNS.

bash$ host surfacemail.com surfacemail.com. has address 202.92.42.236

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.

bash$ ipcalc -h 202.92.42.236 HOSTNAME=surfacemail.com

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

Capitolo 12. Filtri, programmi e comandi esterni


;; QUESTION SECTION: ;2.6.9.81.in-addr.arpa. ;; AUTHORITY SECTION: 6.9.81.in-addr.arpa. 3600 2002031705 900 600 86400 3600 ;; ;; ;; ;;

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

Esempio 12-36. Scoprire dove effetuare una segnalazione di uno spammer


#!/bin/bash # spam-lookup.sh: ricerca il contatto per segnalare uno spammer. # Grazie a Michael Zick. # Verifica degli argomenti da riga di comando. CONTOARG=1 E_ERR_ARG=65 if [ $# -ne "$CONTOARG" ] then echo "Utilizzo: basename $0 nome-dominio" exit $E_ERR_ARG fi

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".

exit $? # spam-lookup.sh chinatietong.com

297

Capitolo 12. Filtri, programmi e comandi esterni


# Un noto dominio di spam.

# "[email protected]" # "[email protected]" # "[email protected]"

# 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.

Esempio 12-37. Analizzare un dominio di spam


#! /bin/bash # is-spammer.sh: Identificare i domini di spam # $Id: is-spammer, v 1.4 2004/09/01 19:37:52 mszick Exp $ # La riga precedente indica lID del RCS. # # la versione semplificata dello script "is_spammer.bash #+ presente nellappendice Script Aggiuntivi. # is-spammer <nome.dominio> # Viene usato il programma esterno dig # Provato con la versione 9.2.4rc5 # Uso di funzioni. # Utilizzo di IFS per il controllo delle stringhe da assegnare agli array. # Fa persino qualcosa di utile: controlla le blacklist dei server e-mail. # Si usa il nome.dominio presente nellURL: # http://www.veramente_ottimo.spammer.biz/tutto_il_resto_ignorato # ^^^^^^^^^^^ # Oppure il nome.domainio dellindirizzo e-mail: # [email protected] # # come unico argomento dello script. #(PS: occorre essere connessi ad internet) # # Concludendo, in base ai due esempi precedenti, questo script si invoca con: # is-spammer.sh spammer.biz

# Spaziatura == :Spazio:Tabulazione:Line Feed:A_capo: SPZ_IFS=$\x20$\x09$\x0A$\x0D # Nessuna spaziatura == Line Feed:A_capo

298

Capitolo 12. Filtri, programmi e comandi esterni


No_SPZ=$\x0A$\x0D # Separatore di campo per gli indirizzi ip puntati IND_IFS=${No_SPZ}. # Recupera la registrazione del testo del dns. # rec_testo <codice_errore> <server> rec_testo() { # verifica $1 per lassegnamento delle stringhe delimitate dai punti. local -a dns IFS=$IND_IFS dns=( $1 ) IFS=$SPZ_IFS if [ "${dns[0]}" == 127 ] then # Controlla se vi una spiegazione. echo $(dig +short $2 -t txt) fi } # Recupera lindirizzo dns. # rec_idr <dns_inv> <server> rec_idr() { local risposta local server local causa server=${1}${2} risposta=$( dig +short ${server} ) # Se la risposta contiene un codice derrore . . . if [ ${#risposta} -gt 6 ] then causa=$(rec_testo ${risposta} ${server} ) causa=${causa:-${risposta}} fi echo ${causa:- non in blacklist.} } # Si deve risalire allindirizzo IP partendo dal nome di dominio. echo "Recupero lindirizzo di: "$1 ip_idr=$(dig +short $1) risposta_dns=${ip_idr:- nessuna risposta } echo Indirizzo: ${risposta_dns} # Una risposta valida deve essere formata da almeno 4 cifre e 3 punti. if [ ${#ip_idr} -gt 6 ] then echo declare richiesta # Controllo per lassegnamento delle stringhe tra i punti.

299

Capitolo 12. Filtri, programmi e comandi esterni


declare -a dns IFS=$IND_IFS dns=( ${ip_idr} ) IFS=$SPZ_IFS # Riordina gli ottetti nella sequenza adatta ad una interrogazione dns. dns_inv="${dns[3]}"."${dns[2]}"."${dns[1]}"."${dns[0]}". # Controlla su: http://www.spamhaus.org (Tradizionale, ben mantenuto) echo -n spamhaus.org dice: echo $(rec_idr ${dns_inv} sbl-xbl.spamhaus.org) # Controlla su: http://ordb.org (Server aperti di istradamento e-mail) echo -n ordb.org dice: echo $(rec_idr ${dns_inv} relays.ordb.org) # Controlla su: http://www.spamcop.net/ (Qui si possono segnalare gli spammer) echo -n spamcop.net dice: echo $(rec_idr ${dns_inv} bl.spamcop.net) # # # altre operazioni di blacklist # # # # Controlla su: http://cbl.abuseat.org. echo -n abuseat.org dice: echo $(rec_idr ${dns_inv} cbl.abuseat.org) # Controlla su: http://dsbl.org/usage (Server vari di istradamento e-mail) echo echo Elenchi di server distibuiti echo -n list.dsbl.org dice: echo $(rec_idr ${dns_inv} list.dsbl.org) echo -n multihop.dsbl.org dice: echo $(rec_idr ${dns_inv} multihop.dsbl.org) echo -n unconfirmed.dsbl.org dice: echo $(rec_idr ${dns_inv} unconfirmed.dsbl.org) else echo echo Indirizzo inutilizzabile. fi exit 0 # Esercizi: # -------# 1) Verificate gli argomenti passati allo script, in caso derrore # lesecuzione deve terminare con un messaggio appropriato. # 2) Controllate lavvenuta connessione internet prima dellinvocazione dello # script, in caso contrario terminate con un appropriato messaggio derrore.

300

Capitolo 12. Filtri, programmi e comandi esterni

# 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

Tty tty1 ttyp0 ttyp1

Idle Login 8 Jun 25 Jun 25 Jun 25

Time 16:59 16:59 17:07

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.

Accesso ad host remoto


sx rx La serie di comandi sx e rx serve a trasferire le a e da un host remoto utilizzando il protocollo xmodem. Generalmente sono compresi in un pacchetto comunicazioni, come minicom.

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.

telnet Utility e protocollo di connessione ad host remoto.

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.

Esempio 12-38. Ottenere una quotazione di borsa


#!/bin/bash # quote-fetch.sh: Scarica una quotazione di borsa.

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

Capitolo 12. Filtri, programmi e comandi esterni


# # # # 2) #+ lavvenuto collegamento. (Suggerimento: confrontate loutput di ps -ax con "ppp" o "connect." Modificate lo script per scaricare il bollettino metereologico locale, fornendo come argomento il codice di avviamento postale.

Vedi anche Esempio A-29 e Esempio A-30.

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.

bash$ rsync -a ~/dirsorgente/*txt /node1/sottodirectory/

Esempio 12-39. Aggiornare FC4


#!/bin/bash # fc4upd.sh # Autore dello script: Frank Wang. # Con piccole modifiche di stile effettuate dallautore de Guida ASB.

305

Capitolo 12. Filtri, programmi e comandi esterni


# Utilizzato in Guida ASB con il permesso dellautore dello script.

# 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

Capitolo 12. Filtri, programmi e comandi esterni


# # ) "/4/i386/kernel-xen*" "/4/i386/xen-*"

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.

# Cancella il file temporaneo #+ alluscita.

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 }

# Recupera e perfeziona lelenco di aggiornamento rsync. crea_lista () { echo $$ > $PID_FILE || {

307

Capitolo 12. Filtri, programmi e comandi esterni


echo "Non posso scrivere nel file $PID_FILE" exit $E_RETURN } echo -n "Recupero e perfezionamento della lista di aggiornamento . . ." # Recupera la lista -- eval necessario per far eseguire rsync #+ come comando unico. # $3 e $4 sono la data e lora di creazione del file. # $5 il nome completo del pacchetto. precedente= pre_file= pre_data=0 eval /bin/nice /usr/bin/rsync \ -r $include $esclude $URL | \ egrep ^dr.x|^-r | \ awk {print $3, $4, $5} | \ sort -k3 | \ { while read riga; do # Calcola i secondi a partire da epoch per scartare i #+ pacchetti obsoleti. cor_data=$(date -d "$(echo $riga | awk {print $1, $2})" +%s) # echo $cor_data # Recupera il nome del file. cor_file=$(echo $riga | awk {print $3}) # echo $cor_file # Recupera il nome del pacchetto rpm dal nome del file, #+ se possibile. if [[ $cor_file == *rpm ]]; then nome_pkg=$(echo $cor_file | sed -r -e \ s/(^([^_-]+[_-])+)[[:digit:]]+\..*[_-].*$/\1/) else nome_pkg= fi # echo $nome_pkg if [ -z "$nome_pkg" ]; then # Se non un file rpm, echo $cor_file >> $TMP #+ lo accoda alla lista di download. elif [ "$nome_pkg" != "$precedente" ]; then # Trovato un nuovo #+ pacchetto. echo $pre_file >> $TMP # Accoda il #+ precedente. precedente=$nome_pkg # Salva quello #+ corrente. pre_data=$cor_data pre_file=$cor_file elif [ "$cor_data" -gt "$pre_data" ]; then # Stesso pacchetto, #+ ma pi recente, pre_data=$cor_data #+ aggiorna il #+ puntatore precedente. pre_file=$cor_file

308

Capitolo 12. Filtri, programmi e comandi esterni


fi done echo $pre_file >> $TMP

# TMP ora contiene la #+ lista COMPLETA e #+ aggiornata.

# 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

Capitolo 12. Filtri, programmi e comandi esterni


scarica_file RIT=$? # ------if [ "$RIT" -eq 0 ]; then /usr/bin/logger -t ${0##*/} "Fedora aggiornata con successo." else /usr/bin/logger -t ${0##*/} "Aggiornamento di Fedora fallito con codice: $RIT" fi exit $RIT

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.

# Provate ssh come utente root: # # $ ssh -l root $HOSTNAME

310

Capitolo 12. Filtri, programmi e comandi esterni


# # # # # #+ # #+ Quando vi verr chiesta la password, inserite quella di root, non la vostra. Last login: Tue Aug 10 20:25:49 2004 from localhost.localdomain Dopo di che exit. I comandi precedenti forniscono una shell interattiva. possibile impostare sshd in modalit comando singolo, ma questo va oltre lo scopo dellesempio. Lunica cosa da notare che quello che segue funziona in modalit comando singolo.

# 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

Capitolo 12. Filtri, programmi e comandi esterni

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.

Grazie a Jason Bechtel per la precisazione.

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

Capitolo 12. Filtri, programmi e comandi esterni


# #+ # #+ # # #+ Digitando self-mailer.sh [email protected] questo script viene inviato a quel destinatario. Il solo self-mailer.sh (senza argomento) invia lo script alla persona che lha invocato, per esempio, [email protected] Per i dettagli sul costrutto ${parametro:-default}, vedi la sezione "Sostituzione di Parametro" del capitolo "Variabili Riviste".

# ========================================================================= 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.

12.7. Comandi per il controllo del terminale


Comandi riguardanti la console o il terminale
tput Inizializza un terminale e/o ne recupera le informazioni dal database terminfo. Diverse opzioni consentono particolari operazioni sul terminale. tput clear lequivalente di clear, vedi oltre. tput reset lequivalente di reset, vedi oltre. tput sgr0 annulla le impostazioni di un terminale, ma senza pulire lo schermo.

bash$ tput longname xterm terminal emulator (XFree86 4.0 Window System)

313

Capitolo 12. Filtri, programmi e comandi esterni

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

Capitolo 12. Filtri, programmi e comandi esterni

12.8. Comandi per le operazioni matematiche


Calcoli matematici
factor Scompone un intero in fattori primi.

bash$ factor 27417 27417: 3 13 19 37

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.

variabile=$(echo "OPZIONI; OPERAZIONI" | bc)

Esempio 12-42. Rata mensile di un mutuo


#!/bin/bash # monthlypmt.sh: Calcola la rata mensile di un mutuo (prestito).

# #+ #+ #

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

Capitolo 12. Filtri, programmi e comandi esterni


echo "Dato il capitale, il tasso dinteresse e la durata del mutuo," echo "calcola la rata di rimborso mensile."