Sei sulla pagina 1di 29

Shell scripting

Enrico Tassi
(slides originali di Stefano Zacchiroli)

Dipartimento di Scienze dellinformazione, Universit` a di Bologna

March 9, 2009

Shell scripting

Per automatizzare lesecuzione di compiti lunghi e ripetitivi, ogni shell fornisce un proprio linguaggio di programmazione (shell script) che permette lesecuzione di programmi (script) memorizzati in le eseguibili testuali. Caratteristiche comuni di questi linguaggi: turing completi (while, if) approccio procedurale non tipati (lunico tipo disponibile ` e la stringa) interpretati Studieremo il linguaggio della shell POSIX (con qualche nota sulle funzionalit` a aggiuntive di bash, la shell pi` u diusa)

Script
Uno script (non necessariamente di shell) ` e un programma eseguibile da un interprete quando viene eseguito un le non binario, la shell legge i primi caratteri del le, cercando la shebang line: #!/path/to/the/interpreter arg1 ... argn per eseguire uno script la shell invoca linterprete appendendo alla sua lista di parametri posizionali il nome dello script esempio: script che stampa il proprio contenuto: #!/bin/cat contenuto dello script Uno shell script ` e uno script con interprete /bin/sh, ad esempio #!/bin/sh echo "Hello, world!" Se si fa uso di features non POSIX ` e bene usare #!/bin/bash

Interattivit` a: input da stdin


` possibile leggere da standard input utilizzando il comando E interno read sintassi: read <nome1> <nome2> ... <nomeN> legge una riga da standard input, la spezza ai caratteri di $IFS, ed associa ogni componente ad una delle variabili specicate nel caso vi siano pi` u componenti che variabili, lultima variabile contiene anche le rimanenti esempio: script inverti #!/bin/bash read nome cognome echo "Dott. $cognome $nome"

Interattivit` a: input da le, con prompt e timeout


Inoltre ` e possibile cambiare il separatore di campo via IFS e cambiare il prompt con -p #!/bin/sh IFS="|" read -p N e C separati da "|" : nome cognome echo "Dott. $cognome $nome" In bash ` e possibile leggere da le tramite read -u n dove n ` e il numero del le descriptor associato al le e dare un timeout (solo bash) con -t (se scade, torna 1 e non 0) #!/bin/bash exec 4<foo.txt read -u 4 -t 10 nome cognome test $? = 0 && echo "Dott. $cognome $nome"

Variabili predenite
La shell ore un vasto insieme di variabili locali e dambiente predenite, ne riportiamo alcune: Locali $* tutti i parametri $@ tutti i parametri, quotati $n n-esimo parametro $# numero dei parametri $$ PID della shell $- ag associati alla shell corrente Di ambiente $IFS Internal Field Separator $PS1, $PS2 prompt primario e secondario (multiline)

Matematica

` possibile eettuare computazioni aritmetiche in due modi E utilizzando leseguibile expr (attenzione al quoting!)
operatori aritmetici: + - / * % predicati: > >= = != < <= espressioni regolari: <string> : <regexp> (stampa il numero di caratteri matched o la capture se presente) lunghezza: length <string>

utilizzando lespansione aritmetica $(( ... )) Per eettuare computazioni matematiche in virgola mobile ` e necessario fare ricorso a programmi esterni (es. bc)

Predicati booleani test (1/2)


Il comando interno test permette di valutare predicati e varia il suo valore di ritorno in base alla loro veridicit` a. Alcuni predicati: Sui les: esistenza di un le regolare: test -f <nome> esistenza di una directory: test -d <nome> leggibilit` a di un le: test -r <nome> eseguibilit` a di un le: test -x <nome> pi` u nuovo di: test <file1> -nt <file2> Sulle stringhe: ` e la stringa vuota: test -z <stringa> uguaglianza di stringhe: test <stringa1> = <stringa2>

Predicati booleani test (2/2)

Sui numeri interi: sintassi generica: test <pred1> <op> <pred2> <op> ` e uno tra: -lt (<) -le () -eq (=) -ge () -gt (>) Predicati composti (connettivi logici): congiunzione: test <pred1> -a <pred2> disgiunzione: test <pred1> -o <pred2> negazione: test ! <pred> Sintassi concisa di test: [ predicato ] esempio: test $x -ge 10 equivale a [ $x -ge 10 ]

Controllo condizionale if then fi


La shell dispone dellusuale costrutto if ... then ... else la guardia ` e un comando C il ramo then viene eseguito se il return code di C ` e0 se il return code di C ` e diverso da 0 viene eseguito il ramo else La guardia ` e tipicamente (ma non necessariamente!) il comando test (o la sua forma compatta [): read -p "inserisci un numero: " number if test $number -lt 0; then echo negativo elif [ $number -eq 0 ]; then echo nullo else echo positivo fi

Pattern matching case esac


` disponibile un costrutto di pattern matching su stringhe, i E pattern sono simili a quelli disponibili per il globbing. Esempio: case "$1" in start) start-stop-daemon --start ... ;; reload | force-reload) start-stop-daemon --stop ... start-stop-daemon --start ... ;; *) exit 1 ;; esac

Cicli limitati for do done


` disponibile un costrutto di ciclo limitato che permette di iterare E il valore di una variabile su un insieme nito di stringhe. Esempio: for dirname in /tmp /var/tmp; do rm -f $dirname/*~ done Nota: se uno script ha come parametri "a" e "b c" i comandi for "$*"; do ... done e for "$@"; do ... done hanno comportamenti dierenti! Per generare la sequenza di interi "1" "2" "3" ... "n" si usa il programma seq e lespansione dei comandi: for i in seq 1 100; do echo "$i pecorelle" > sonno.txt done

Cicli illimitati while


` disponibile un costrutto di ciclo non limitato che permette di E ciclare ntanto che lesecuzione di un comando ha return code 0: while read -t 10 line; do echo $line done ` disponibile anche la versione until che permette di ciclare ntanto E che lesecuzione di un comando ha return code diverso da 0: until false; do echo "guru meditation" done

Gestione dei segnali


` possibile installare presso la shell in esecuzione dei comandi di E callback che verranno eseguiti al vericarsi di eventi. Sono considerati eventi: la ricezione di segnali la terminazione della shell Per installare callback si utilizza il comando interno trap: set -e tmp=tempfile trap "rm $tmp" EXIT ... Per avere una lista di segnali trap -l

Denitione di funzioni
` possibile denire funzioni utilizzando la sintassi: E <nome> () { <comandi> } Una volta denite, le funzioni possono essere invocate come normali comandi di shell, passando loro parametri posizionali Il loro corpo viene eseguito con un nuovo insieme di parametri posizionali hello () { echo "Hello, $1 World!" } hello "Functions"

Inclusione

. . . come evitare il mal di testa. . . per aumentare la mantenibilit` a di shell script di dimensioni considerevoli ` e possibile dividerli in pi` u le utilizzando il comando . (punto) o source (bash only), ` e possibile interpretare il contenuto di script esterni ` e quindi possibile organizzare gli script in librerie di funzioni invocabili esternamente

Debugging

Ad ogni shell ` e associato un insieme di ag, identicati da una singola lettera, modicabili attraverso il comando interno set con la sintassi set -ag attiva un ag set +ag disattiva un ag Due ag sono di particolare utilit` a per il debugging di script ag e, se settato impone alla shell di uscire non appena un comando ritorna un return code diverso da 0 ag x, se settato la shell stampa su stdout i comandi e i loro argomenti prima di eseguirli

Here document

Generare un le al volo ` e possibile con loperatore di redirezione << cat > foo << EOT contenuto dato in pasto a cat EOT Pu` o essere anche usato con un le descriptor exec 4<>out.txt 4<<EOT contenuto dato in pasto a cat EOT

Truccacci indirezione nei nomi

Variabili contenent nomi di variabili: A=3 B=A eval echo \$$B Funziona anche in scrittura A=3 foo=A eval $foo=4 echo $A

Truccacci hooks
Rendere uno script estensibile semplicemente copiando un le in una directory (plugin) denita una variabile globale CB_FOR_X denita una directory contenent plugins hooks.d/ ogni hook viene incluso con source: for h in hooks.d/*.sh; do . $h; done ogni hook denisce una funzione che desidera venga chiamata ogni volta che si verica X con nome proprio (es. lhook foo.sh denisce foo_callback) ogni hook termina con CB_FOR_X="$CB_FOR_X foo_callback" il programma principale, quando si verica X, esegue un for h in $CB_FOR_X; do $h params; done

Truccacci Espanzione variabili avanzata


Esistono vari modi di espandere una variabile ${FOO}BAR diverso da $FOOBAR ${FOO:-default value} usa "devault value" se FOO non ` e denita ${FOO:=default value} come sopra ma assegna anche FOO ${FOO:?[error]} assert FOO denita, stampa error altrimenti ${FOO%pat} rimuove il pi` u piccolo susso che fa match ${FOO%%pat} rimuove il pi` u grande susso che fa match ${FOO#pat} rimuove il pi` u piccolo presso che fa match ${FOO##pat} rimuove il pi` u grande presso che fa match

Appendice utility standard (1/4)


cal cat le cd nomedir Mostra il calendario della settimana. Pu` o essere seguito dallanno o dal mese e dallanno Scrive il contenuto del le. Senza argomento legge da standard input Ci si sposta nella directory specicata. Senza argomnti riporta nella home. .. ` e il nome della directory padre Copia il le specicato nella destinazione. Se la destinazione ` e una directory ci crea un le con lo stesso nome di quello copiato, se ` e il nome di un le allora ci scrive sopra Stampa la data corrente. Consultare il manuale per i vari formati di data supportati Stampa il messaggio

cp le destinazione

date echo saggio mes-

Appendice utility standard (2/4)


expr espressione grep espressione le head le Stampa il risultato. Controllare nel manuale gli operatori supportati. Esempio expr 3 + 5 stampa 8 Stampa le righe di le che contengono lespressione. Nel manuale di grep sono documentate le espressioni regolari che accetta Mostra le prime righe del le. -n numero sceglie il numero di righe da mostrare Mostra il contenuto della directory specicata. Senza argomenti mostra il contenuto della directory corrente Visualizza la pagina del manuale relativa al comando. Si esce con q e man man mostra la pagina del manuale relativa al manuale crea una directory col nome specicato

ls dir

man comando

mkdir nomedir

Appendice utility standard (3/4)


mv sorgente destinazione pwd rm le Sposta il le/directory sorgente nel le o nella directory di destinazione Stampa la directory corrente Rimuove il le. NON c` e il cestino. -i chiede conferma. -r cancella la directory e tutto quello che vi ` e contenuto Stampa una sequenza di numeri da inizio a ne. Utile nei for Attende il tempo specicato Ordina le righe di un le. Senza argomento legge da standard input. -k colonna seleziona la chiave di ordinamento Mostra le ultime righe del le. -n numero sceglie il numero di righe da mostrare

seq inizio ne sleep secondi sort le

tail le

Appendice utility standard (4/4)

test espressione uniq wc le

Ritorna 0 se lespressione ` e vera. Utile nell if. es. test -e pippo ritorna 0 se il le pippo esiste. test 3 -ge 2 ritorna 0 perch` e32 Elimina i duplicati da una sequenza di righe Conta il numero di caratteri/parole/linee del le. Senza argomento legge da standard input

Appendice moreutils
Pacchetto Debian con alcune utility molto comode sponge per il paradigma (non funzionante) cat foo | mangle > foo che pu` o essere implementato come cat foo | mangle | sponge foo zrun per il paradigma cp a.gz b.gz; gunzip b.gz; foo b; rm b che pu` o essere implementato come zrun foo a.gz ifdata per non parsare loutput di ifconfig o netstat. Ad esempio ifdata -pa eth0 stampa 130.136.32.45 combine combina le linee di due les. Ad esempio combine file1 not file2 Riferimenti: less /usr/share/doc/moreutils/README

Riferimenti

Riferimenti: Advanced Bash-Scripting Guide, An in-depth exploration of the art of shell scripting http://tldp.org/LDP/abs/html/index.html man bash man dash

Esercizi (1/2)

script junk.sh che prende in input un le e lo sposta in /.junk (creandola se non c` e). creare un lsjunk.sh che stampa i les in /.junk creare anche un unjunk.sh che prende in input il nome di un le e se c` e lo toglie dal /.junk e lo rimette nella dir corrente, se non c` e stampa un errore. modicare i 3 precedenti in modo che controllino se lutente gli passa un argomento (sugg. basta controllare con test se la lunghezza di $1 ` e zero, ma fate attenzione a metterlo tra doppi apici)

Esercizi (2/2)

Scrivere uno shell script monitor.sh che prende come parametri il nome di una directory da osservare, un intervallo di tempo in secondi I (ogni quanti secondi osservare la directory) e il nome di una directory contenente dei plugin. I plugin sono eseguiti una sola volta con source e devono registrare una callback in una variabile globale NOTIFY. Quando un le osservato viene modicato (hint: risulta pi` u nuovo di un le creato I secondi prima) tutte le callback devono essere chiamate passando loro come unico parametro il nome del le modicato. Scrivere almeno un plugin che stampa a video il nome del le modicato e uno che manda una mail utilizzando il comando mail.