PROGRAMMAZIONE IN SHELL The Unix Programming Environment Kernigham - Pike 1 Osservazioni generali • I vari interpreti dei comandi che si possono trovare su un sistema Unix (sh, csh, tcsh, ksh, bsh) sono molto simili fra loro • Tutti funzionano anche come linguaggi di programmazione • Tutti derivano dalla sh (shell) il primo interprete scritto per il sistema Unix • Il programma sh si trova su qualsiasi macchina Unix, nella directory /bin 2 Osservazioni generali: • Una sequenza di comandi shell puó essere data direttamente al terminale una riga dopo l’altra: prompt> ls -l <CR> prompt> date <CR> prompt> cp fileA fileB <CR> • oppure scritta su un unica riga: prompt> ls -l; date; cp fileA fileB <CR> • oppure inserita in un file di comandi, che viene detto: un Shell SCRIPT 3 Shell script • un shell script puó essere eseguito con: prompt> sh scriptfile • oppure puó essere trasformato in un nuovo comando del sistema rendendolo eseguibile: prompt> chmod a+x scriptfile prompt> scriptfile • In ogni caso, il contenuto del file scriptfile viene interpretato usando la shell. Se si vuole che sia interpretato usando la csh bisogna aggiungere, come prima riga del file: #!/bin/csh 4 Shell Script: • La stessa cosa vale se si usa la shell come linguaggio di programmazione. Si puó lanciare un programma shell in modo interattivo: prompt> sh /* entra in ambiente shell */ $ for i in fileA fileB > do > ls -l $i > done rwxr-xr-x st909090 573 fileA rwx-----st909090 200 fileB $ exit /* esce dalla shell e torna in csh */ prompt> 5 Shell Script: • oppure scrivere il programma in un shell script ed eseguirlo: myscript for i in fileA fileB do ls -l $i done prompt> chmod a+x myscript prompt> myscript rwxr-xr-x st909090 573 fileA rwx-----st909090 200 fileB prompt> 6 Una directory di comandi personali • La shell usa la variabile path settata nel file .cshrc per stabilire in quali directory cercare i comandi lanciati dall’utente. • Ogni utente puó definire una propria directory di comandi, e poi modificare la variabile path in modo da lanciare i propri comandi senza specificare ogni volta il loro pathname (cioé proprio come se fossero un normale comando unix) 7 Una directory di comandi personali • al fondo del file .cshrc inserire il comando: set path=($path ~myloginname/mycommanddir) /* aggiunge a path il nuovo pathname */ • Eseguire poi il comando: source .cshrc /* rilegge il file .cshrc */ • Ora i comandi dentro mycommanddir possono essere lanciati direttamente • Attenzione: se aggiungete un nuovo file, perché sia immediatamente attivo date il comando: rehash 8 Lavorare in ambiente shell • ATTENZIONE: tutto quello che vedremo funziona e si riferisce all’ambiente shell. • Quando vi collegate al vostro account e usate un qualsiasi terminale virtuale state usando la cshell • Per usare la shell in modalitá interattiva dovete entrare nell’ambiente shell lanciando: sh • Per uscire dalla shell basta digitare dare il comando exit e ritornate alla cshell • Il problema non si pone se usate shell script, che 9 sono automaticamente interpretati usando la shell Le variabili di shell • La shell conosce un solo tipo di variabili: stringhe di caratteri • Il nome di una variabile shell puó essere una qualsiasi sequenza di caratteri senza spazio e metacaratteri non quotati • Lo stesso ambiente shell definisce ed usa un insieme di variabili per gestire correttamente la connessione dell’utente 10 Le variabili di shell • dando il comando env si possono vedere tutte le variabili correntemente definite nell’ambiente di lavoro. • Per definire una nuova variabile shell basta assegnarle un valore: prompt> myvar=pippo /* ricordate: niente spazi! */ • per usare il valore di una variabile definita occorre anteporle il simbolo $ prompt> echo $myvar pippo prompt> 11 Assegnamento dell’output di un comando ad una variabile shell • L’output di un comando puó essere assegnato ad una variabile shell, e poi usato: apici >pathname=`pwd` >echo $pathame >/docsrv/gunetti `pwd` 12 assegnazione di variabili da terminale echo “inserire nome e cognome:” read nome cognome echo “nome inserito = “ $nome echo “cognome inserito = “ $cognome myscript prompt> myscript inserire nome e cognome: Mario Rossi nome inserito = Mario cognome inserito = Rossi prompt> 13 Le variabili di shell • Il valore di una variabile si usa direttamente: prompt> myvar1=/etc/passwd prompt> ls -l $myvar1 r-xr-xr-x root 4570 /etc/passwd • Si puó concatenare ad un altro: prompt> myvar2=.old prompt> ls -l $myvar1$myvar2 r-xr-xr-x root 2620 /etc/passwd.old 14 Le variabili di shell • Si puó concatenare una variabile ad una stringa qualsiasi racchiudendo il nome della variabile tra graffe o terminandolo con il back slash \: prompt> myvar1=/etc/pass prompt> ls -l ${myvar1}wd r-xr-xr-x root 4570 /etc/passwd • oppure: prompt> ls -l $myvar1\wd.old r-xr-xr-x root 2620 /etc/passwd.old 15 Le variabili di shell • Peró non si puó concatenare una variabile ad una stringa qualsiasi direttamente, perché quello diventa un nuovo nome di variabile non definita: prompt> myvar1=/etc/pass prompt> more $myvar1wd usage: more [...] [...] /*ERRORE nell’uso del more */ • Le variabili non definite hanno il valore della stringa Null. 16 Alcune variabili predefinite: • • • • • • • $$: PID della shell corrispondente $#: numero di argomenti di uno script shell $0: nome dello script shell $n: valore dell’ennesimo argomento dello script $HOME: home directory dell’utente $PWD: current working directory $path: elenco delle directory in cui cercare i comandi eseguiti 17 Script di uso delle variabili predefinite: file myscript: echo “questo script si chiama “ $0 “ ed ha PID = “ $$ echo “é stato chiamato con “ $# “argomenti” echo “il secondo argomento é “ $2 echo “é stato lanciato nella directory “ $PWD sh myscript qui quo qua questo script si chiama myscript ed ha PID 16123 é stato chiamato con 3 argomenti il secondo argomento é quo é stato lanciato nella directory /docsrv/gunetti/tmp 18 Il costrutto FOR for var in val_1 val_2 ... val_N do comandi shell done la variabile var assume in successione ad ogni ciclo, i valori val_1, val_2, ..., val_N 19 Il costrutto FOR for var in * do comandi shell done • la variabile var assume come valore, in successione ad ogni ciclo, il nome degli oggetti (file, sottodirectory) della directory corrente. 20 Il costrutto FOR for var do comandi shell done myscript • la variabile var assume come valore, in successione ad ogni ciclo, il nome degli argomenti passati in input. prompt> myscript pippo pluto 21 Il costrutto FOR: esempio for i do grep $i elenco_telefonico done cerca_elenco prompt> cerca_elenco rossi bianchi 22 il comando TEST: • test [-flag] espressione • Restituisce true o false a seconda del tipo di test e dell’espressione coinvolta. • Viene usato in tutti i costrutti condizionali: – if-then-else – while-do – until-do • puó essere usato per testare variabili shell e file 23 il comando TEST: • l’elenco completo dei flag é sul manuale. Esempi: – – – – – test s1 = s2 /* vero se s1=s2 - attenzione al blank */ test s1 != s2 /* vero se s1 diverso da s2 */ test s /* vero se s non é la stringa vuota */ test -r file /* vero se file esiste ed é leggibile */ test -f file && test -s file /* vero se file é regolare e non ha dimensione 0 -- || (doppia pipe) é l’or */ – test ! -s file /* vero se file e’vuoto -- ! é la negazione */ – test n1 -gt n2 /*vero se n1 é maggiore di n2 */ – test n1 -le n2 /* vero se n1 é minore o uguale a n2 */24 if-then-else: if <lista1> then <lista2> [else <lista3>] fi • il ramo else é opzionale • piú if-then-else possono essere nidificati 25 if-then-else - esempio: var_A=ciao var_B=hello if test $1 = $var_A then echo “Ciao! Come stai?” else if test $1 = $var_B then echo “hello! Come stai?” else echo “non mi saluti?” fi fi 26 case • Esecuzione in condizionale di piú alternative case <var> in <pattern_1>) <command_list_1>;; ......... <pattern_n>) <command_list_n>;; [*) <otherwise_command_list>;;] esac 27 case - esempio: read saluto case $saluto in ciao) echo “CIAO!”; echo “Come stai?”;; hello) echo “Hello!”; echo “Come stai?”;; *) echo “EMBÉ?”;; esac 28 while - do / until - do while <command> do <command_list> done • Esegue il loop se <command> restituisce true until <command> do <command_list> done • Esegue il loop se <command> restituisce false 29 while - do - Esempio: who | grep $1 > tmpfile while test -s tmpfile /* tmpfile non é vuoto */ do echo “l’utente “ $1 “ é collegato” sleep 60 who | grep $1 > tmpfile done echo “l’utente “ $1 “ non é collegato” 30 until - do - Esempio: who | grep $1 > tmpfile occhio alla negazione! until test ! -s tmpfile do echo “l’utente “ $1 “ é collegato” sleep 60 who | grep $1 > tmpfile done echo “l’utente “ $1 “ non é collegato” 31 Selezione di porzioni di stringhe: • cut -bX-Y filename – taglia verticalmente filename prelevando per ogni riga solo i caratteri dall’X-esimo all’Y-esimo • echo “dipartimento” | cut -b3-7 – restituisce la stringa parti • a=`who am i | cut -b1-4``hostname | cut -b1-4` – setta la variabile a come la concatenazione dei primi 4 caratteri del login e del nome dell’host. 32 Raggruppamento di comandi: • ( lista di comandi ) – particolarmente utile per eseguire comandi in una directory particolare specificata all’interno delle parentesi: (cd subdir; rm tmpfile) al termine la directory corrente non é cambiata 33 Calcoli aritmetici: “expr” expr <espressione aritmetica> • expr premette di usare le variabili shell con significato numerico, ed eseguire semplici calcoli: >div=2 >result=`expr 10 / $div` >echo $result 5 > • Occhio agli spazi! 34 expr - Esempi: Cont=10 while test $cont -gt 0 do echo “cont = “ $cont cont=`expr $cont - 1` done • l’uso di espressioni complesse è un pó goffo: result=`expr “(“ 10 \* 5 “)” / 2` 35