script della shell (con particolare riferimento a bash e ksh) Paolo Montalto set-2010 Paolo Montalto set-2010 http://www.xabaras.it http://www.xabaras.it v2.0
alias "Bourne" shell scritta da Steve Bourne degli AT&T Bell Labs per Unix V7 (1979) scritta da Steve Bourne degli AT&T Bell Labs per Unix V7 (1979) Semplice, e dotata (inizialmente) di pochissimi comandi interni, infatti chiamava programmi esternianche per Semplice, e dotata (inizialmente) di pochissimi comandi interni, infatti chiamava programmi esternianche per i task più semplici. i task più semplici. csh: ”C" shell csh: ”C" shell sviluppata da Bill Joy (inventore dell editor VI) per il sistema BSD sviluppata da Bill Joy (inventore dell editor VI) per il sistema BSD Simile alla Bourne shell ma con molti miglioramenti tesi all'interattività (es. history), i comandi usati negli Simile alla Bourne shell ma con molti miglioramenti tesi all'interattività (es. history), i comandi usati negli script sono molto simili alle istruzioni del linguaggio C. script sono molto simili alle istruzioni del linguaggio C. tcsh: ”TC” shell tcsh: ”TC” shell sviluppata alla fine degli anni 70 da Ken Greer alla Carnegie Mellon University per il sistema ”TENEX” sviluppata alla fine degli anni 70 da Ken Greer alla Carnegie Mellon University per il sistema ”TENEX” è un'estensione (completamente compatibile) della C shell che supporta il completamento automatico dei è un'estensione (completamente compatibile) della C shell che supporta il completamento automatico dei comandi e l'editing a linea di comando. comandi e l'editing a linea di comando. ksh: ”Korn” shell ksh: ”Korn” shell scritta da David Korn agli AT&T Bell Labs all'inizio degli anni 80 scritta da David Korn agli AT&T Bell Labs all'inizio degli anni 80 considerata come un'update della Bourne shell, è compatibile con quest'ultima ma incorpora anche molte considerata come un'update della Bourne shell, è compatibile con quest'ultima ma incorpora anche molte caratteristiche di interattività della tcshell. caratteristiche di interattività della tcshell. bash: ”Bourne again” shell bash: ”Bourne again” shell shell UNIX Open Source scritta dal progetto GNU (Default su Linux) shell UNIX Open Source scritta dal progetto GNU (Default su Linux) è un clone della sh ma con estensione dell'insieme di comandi, conforme allo standard POSIX e è un clone della sh ma con estensione dell'insieme di comandi, conforme allo standard POSIX e parzialmente compatibile con la Korn shell. parzialmente compatibile con la Korn shell. Shell Unix
delle Directory ls: mostra il contenuto delle Directory -l -l elenca un file per linea elenca un file per linea -a -a mostra i file nascost mostra i file nascosti i -t -t ordina per data ultima modifica ordina per data ultima modifica cd: ”cambia directory” cd: ”cambia directory” rm: cancella file e directory rm: cancella file e directory -R -R (ricorsivo) elimina anche le sottocartelle (ricorsivo) elimina anche le sottocartelle -f -f forza la cancellazione senza chiedere conferma forza la cancellazione senza chiedere conferma mv: sposta file e directory mv: sposta file e directory pwd: stampa la directory corrente pwd: stampa la directory corrente history: history: mostra l'elenco dei comandi eseguidi dalla shell mostra l'elenco dei comandi eseguidi dalla shell more: more: mette in pausa lo standard output consentendo di leggerlo, solitamente è usato in concomitanza con mette in pausa lo standard output consentendo di leggerlo, solitamente è usato in concomitanza con l'operatore ”|” (che vedremo più avanti) l'operatore ”|” (che vedremo più avanti) tail: tail: mostra le ultime righe di un file mostra le ultime righe di un file -n -n mostra le ultime ”n” righe mostra le ultime ”n” righe -f -f continua a rileggere il file nel caso vinga modificato continua a rileggere il file nel caso vinga modificato cat: cat: stampa uno o più file sullo standard output stampa uno o più file sullo standard output Shell Unix
ad un file ln: crea un link ad un file ln [OPZIONE] DEST NOME_COLL ln [OPZIONE] DEST NOME_COLL -s -s crea un link simbolico crea un link simbolico who: who: elenca gli utente attualmente loggati nel sistema elenca gli utente attualmente loggati nel sistema w: w: come ”who” ma aggiunge informazioni su cosa stanno eseguendo come ”who” ma aggiunge informazioni su cosa stanno eseguendo echo: echo: stampa una stringa e/o il valore di una variabile sullo standard output stampa una stringa e/o il valore di una variabile sullo standard output which / whence: which / whence: trova il path corrispondete ad un comando, es: trova il path corrispondete ad un comando, es: # which # which ls ls /bin/ls /bin/ls sort: sort: ordina le righe di un file di testo ordina le righe di un file di testo touch: touch: cambia la data di modifica di un file cambia la data di modifica di un file wc: wc: conta le parole presenti in un file di testo conta le parole presenti in un file di testo find: find: cerca un file nel percorso specificato come parametro cerca un file nel percorso specificato come parametro -name -name cerca i file per nome cerca i file per nome -exec -exec esegue un comando sui file trovati esegue un comando sui file trovati kill: kill: invia un segnale ad un processo (generalmente usato per terminare un programma) invia un segnale ad un processo (generalmente usato per terminare un programma) es. es. kill kill -9 61435 -9 61435 Shell Unix
file speciali Ogni programma eseguito dalla shell accede a tre file speciali Standard input (per convenzione descrittore di file numero Standard input (per convenzione descrittore di file numero 0 0) ) Standard output per convenzione descrittore di file numero Standard output per convenzione descrittore di file numero 1 1) ) Standard error (per convenzione descrittore di file numero Standard error (per convenzione descrittore di file numero 2 2) ) ciascuno di questi file può essere oggetto di ”reindirizzamento”. ciascuno di questi file può essere oggetto di ”reindirizzamento”. Cerchiamo di essere più chiari. Cerchiamo di essere più chiari. Supponiamo di lanciare il comando ”ls” nella directory corrente, questo scriverà una lista di file e directory Supponiamo di lanciare il comando ”ls” nella directory corrente, questo scriverà una lista di file e directory sullo schermo. sullo schermo. E se invece volessimo scrivere il risultato di ”ls” su un file risultato.txt? E se invece volessimo scrivere il risultato di ”ls” su un file risultato.txt? Detto fatto! Detto fatto! Basterà utilizzare l'operatore ”>” che ”devia” lo standard output di ”ls” su un file: Basterà utilizzare l'operatore ”>” che ”devia” lo standard output di ”ls” su un file: ls > risultato.txt ls > risultato.txt oppure oppure ls >> risultato.txt ls >> risultato.txt se vogliamo mantenere il contenuto preesistende del file se vogliamo mantenere il contenuto preesistende del file Shell Unix
comando su file basta anteporre un 2 all'operatore: Per deviare lo standard error di un comando su file basta anteporre un 2 all'operatore: comando 2> errore.txt comando 2> errore.txt Per deviare lo standard error sullo standard output lanciare: Per deviare lo standard error sullo standard output lanciare: comando 2>&1 comando 2>&1 dove ”&1” identifica lo standard output. dove ”&1” identifica lo standard output. Inoltre è possibile leggere l'input di un comando da file in questo modo: Inoltre è possibile leggere l'input di un comando da file in questo modo: comando < input.txt comando < input.txt o combinare i due tipi di reindirizzamento come di seguito: o combinare i due tipi di reindirizzamento come di seguito: comando < input.txt > output.txt comando < input.txt > output.txt Infine ogni comando di shell può leggere il suo input da una lista testuale come questa: Infine ogni comando di shell può leggere il suo input da una lista testuale come questa: comando <<[delimitatore] comando <<[delimitatore] input1.txt input1.txt input2.txt input2.txt delimitatore delimitatore dove il primo ”delimitarore” indica il carattere che termina la lista (per default è EOF). dove il primo ”delimitarore” indica il carattere che termina la lista (per default è EOF). Shell Unix
modo che l'output di un comando diventi l'input di quello successivo E' possibile eseguire comandi multipli in modo che l'output di un comando diventi l'input di quello successivo in modo automatico. in modo automatico. Ciò può essere fatto mediante l'operatore ”|” detto ”pipe” il quale collega in serie gli input/output dei vari Ciò può essere fatto mediante l'operatore ”|” detto ”pipe” il quale collega in serie gli input/output dei vari comandi che compongono la pipeline nel modo seguente: comandi che compongono la pipeline nel modo seguente: comando1 | comando2 [ | comando3 …] comando1 | comando2 [ | comando3 …] L'uso delle pipeline è molto comune quando si utilizza una shell Unix, ecco alcuni esempi: L'uso delle pipeline è molto comune quando si utilizza una shell Unix, ecco alcuni esempi: ls | more ls | more esegue il comando ls e passa il suo output al comando more paginando di fatto la lista esegue il comando ls e passa il suo output al comando more paginando di fatto la lista cat applicazione.log | more cat applicazione.log | more visualizza il contenuto del file di log una shermata alla volta visualizza il contenuto del file di log una shermata alla volta ls | grep ”foto” ls | grep ”foto” visualizza solo i file che contengono ”foto” nel nome visualizza solo i file che contengono ”foto” nel nome Shell Unix
ad effettuare trasformazioni su uno stream (che si esso un file o una pipeline) Uno stream editor serve ad effettuare trasformazioni su uno stream (che si esso un file o una pipeline) Ciò può essere molto utile, ad esempio, se si vogliono effettuare delle sostituzioni massive all'interno di un Ciò può essere molto utile, ad esempio, se si vogliono effettuare delle sostituzioni massive all'interno di un file. file. Es: sostituiamo tutte le occorrenze della parola ”giorno” con la parola ”notte” nel file pippo.txt: Es: sostituiamo tutte le occorrenze della parola ”giorno” con la parola ”notte” nel file pippo.txt: sed s/giorno/notte/ < pippo.txt > output.txt sed s/giorno/notte/ < pippo.txt > output.txt il risultato della sostituzione viene memorizzato nel file output.txt il risultato della sostituzione viene memorizzato nel file output.txt Cerchiamo ora di analizzare l'esempio descritto. Cerchiamo ora di analizzare l'esempio descritto. Il comando è cotituito da quattro parti: Il comando è cotituito da quattro parti: s s comando ”sostituisci” comando ”sostituisci” /../../ /../../ delimitatore delimitatore giorno giorno criterio di ricerca (pattern, è un'espressione regolare) criterio di ricerca (pattern, è un'espressione regolare) notte notte stringa da sostituire stringa da sostituire Poiché lavora sugli stream, ”sed” può essere eseguito anche sulle pipeline, ad esempio: Poiché lavora sugli stream, ”sed” può essere eseguito anche sulle pipeline, ad esempio: echo Buongiorno | sed 's/giorno/notte/' echo Buongiorno | sed 's/giorno/notte/' Il cui risultato sarà la stringa ”Buonnotte”. Il cui risultato sarà la stringa ”Buonnotte”. Shell Unix
un esempio delle potenzialità di sed. Quello appena mostrato è solo un esempio delle potenzialità di sed. E' ad esempio possibile: E' ad esempio possibile: cercare una stringa ed aggiungervi un prefisso e/o un suffisso cercare una stringa ed aggiungervi un prefisso e/o un suffisso sostituire soltanto una parte della stringa cercata sostituire soltanto una parte della stringa cercata --- --- Supponiamo di avere un articolo con dei riferimenti alle figure inserite al suo interno (Figura 1, Figura 2, Supponiamo di avere un articolo con dei riferimenti alle figure inserite al suo interno (Figura 1, Figura 2, Figura 3) e di voler aggiungere le parentesi intorno alla dicitura Figura n. Figura 3) e di voler aggiungere le parentesi intorno alla dicitura Figura n. Non potremo usare il metodo precedentemente descritto, poiché non conosciamo la stringa esatta da Non potremo usare il metodo precedentemente descritto, poiché non conosciamo la stringa esatta da cercare. cercare. La soluzione è semplice, sed prevede un carattere speciale che possiamo usare per indicare la stringa La soluzione è semplice, sed prevede un carattere speciale che possiamo usare per indicare la stringa trovata, questo carattere e la '&'. trovata, questo carattere e la '&'. Supponendo che il nostro testo sia ”La casa di Pippo Figura 1”, lanciando il seguente comando: Supponendo che il nostro testo sia ”La casa di Pippo Figura 1”, lanciando il seguente comando: echo "La casa di pippo Figura 1" | sed 's/Figura [0-9]*/(&)/' echo "La casa di pippo Figura 1" | sed 's/Figura [0-9]*/(&)/' otterremo la stringa: otterremo la stringa: La casa di Pippo (Figura 1) La casa di Pippo (Figura 1) Dove il criterio di ricerca ”Figura [0-9]*” indica la parola Figura seguita da uno spazio e da numero arbitrario Dove il criterio di ricerca ”Figura [0-9]*” indica la parola Figura seguita da uno spazio e da numero arbitrario di cifre. di cifre. Shell Unix
rivelarsi un comando molto ultile a chiunque si trovi a dover utilizzare una shell unix. Sostanzialmente il comando ”grep” cerca nel file di input le righe che corrispondono ad un certo criterio e le stampa. Partendo da questo semplice concetto è possibile ad esempio cercare tutti i file che contengono una determinata stringa o tutte le righe di un file che soddisfano una condizione. Tale condizione è esprimibile come ”espressione regolare”. Ricollegandoci all'esempio fatto per ”sed”, supponiamo di voler cercare tutte le righe del file pippo.txt che contengono il riferimento ad una Figura. Ci basterà lanciare il seguente comando grep: grep ”Figura [0-9]* Figura [0-9]*” pippo.txt che stampa tutte le righe di pippo.txt che soddisfano il criterio. Il comando così eseguito però non da informazioni sulla posizione delle righe nel file, inoltre il criterio di ricerca è ”case sensitive”. Modificando il comando in questo modo: grep -in ”Figura [0-9]* Figura [0-9]*” pippo.txt grep stamperà ogni riga preceduta dalla sua posizione nel file. Nellesempio appena descritto il parametro -i sta per ”ignore case” e -n per ”line number”. Shell Unix
è quindi molto utile per filtrare i risultati di altri comandi quali ad esempio cat cat Catalina.out | grep myApp.war oppure ls ls -l | grep -i ”Figura[0-9]*” Principali opzioni di grep: -i ignore case (non distingue tra minuscole e maiuscole) -v invert match (seleziona le righe che non soddisfano la condizione) -n visualizza numeri di riga -w parola intera (Forza MODELLO a corrispondere solo a parole intere) -x linea intera (Forza MODELLO a corrispondere solo a righe intere) Shell Unix
un comando di shell, ma un programma a se stante che consente ad un utente abilitato di eseguire un comando come superutente o come un altro utente. Normalmente i permessi e vincoli per l'esecuzione di sudo si trovano nel file /etc/sudoers che viene compilato dall'amministratore di sistema. La sintassi di sudo è la seguente: sudo [opzioni] [variabile1=valore1 …] [--] [comando [arg1 …]] dove Il parametro facoltativo comando indica il comando da eseguire, ed i parametri arg sono i suoi parametri. Inolte è possibile associare delle variabili d'ambiente all'esecuzione del comando facendolo precedere da parametri nella forma variabile=valore. Il doppio trattino -- (facoltativo) indica che i parametri successivi non sono da considerarsi opzioni o assegnazioni di variabili. Pricipali opzioni di sudo: -u assume l'identità dell'utente specificato invece che di superuser -l elenca i comandi che l'utente è abilitato a lanciare -s lancia una shell come superutente Shell Unix
in concomitanza con gli operatori di redirezione. Supponiamo di voler eseguire come superutente il seguente comando: echo "1" > /proc/sys/net/ipv4/ip_forward La sintassi più intuitiva sarebbe la seguente: sudo echo "1" > /proc/sys/net/ipv4/ip_forward però, sfortunatamente non è quella corretta, poiché stiamo dicendo alla shell di eseguire echo ”1” come superutente e la redirezione come utente normale. La soluzione corretta è invece la seguente: sudo sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward" In questo modo si esegue come superutente una shell che lancia il comando echo 1 > /proc/sys/net/ipv4/ip_forward Shell Unix
di testo contenente: Uno script della shell è un file di testo contenente: comandi di shell così come potrebbero essere scritti nel prompt comandi di shell così come potrebbero essere scritti nel prompt commenti (identificati dal simbolo # come primo carattere della riga) commenti (identificati dal simbolo # come primo carattere della riga) costrutti di controllo e variabili costrutti di controllo e variabili Qualsiasi shell può essere utilizzata per eseguire uno script, per questo è fondamentale che nella prima riga Qualsiasi shell può essere utilizzata per eseguire uno script, per questo è fondamentale che nella prima riga dello script sia specificato a quale shell è destinato per evitare errori di interpretazione. dello script sia specificato a quale shell è destinato per evitare errori di interpretazione. Ciò può essere fatto mediante la seguente sintassi: Ciò può essere fatto mediante la seguente sintassi: #!/percorso/della/shell #!/percorso/della/shell ad esempio ad esempio #!/bin/ksh #!/bin/ksh A questo punto il file può essere eseguito passandolo alla shell in questo modo: A questo punto il file può essere eseguito passandolo alla shell in questo modo: ksh mioscript.sh ksh mioscript.sh o direttamente aggiungendo i permessi di esecuzione al file: o direttamente aggiungendo i permessi di esecuzione al file: chmod +x mioscript.sh chmod +x mioscript.sh Shell Unix
accettare parametri a riga di comando. Uno script della shell può accettare parametri a riga di comando. Questi parametri possono essere acceduti Questi parametri possono essere acceduti in maniera posizionale attraverso le variabili $0, $1, $2, $3, $4... fino a $9 in maniera posizionale attraverso le variabili $0, $1, $2, $3, $4... fino a $9 (dove $0 contiene il nome dello script) (dove $0 contiene il nome dello script) Per accedere ai parametri successivi al 9 è possibile usare Per accedere ai parametri successivi al 9 è possibile usare ${10}, ${11} … ${10}, ${11} … Il costrutto ”shift n” che scarta i primi n argomenti e ri-numera i successivi Il costrutto ”shift n” che scarta i primi n argomenti e ri-numera i successivi $* che contiene tutti gli argomenti separati da spazi in una sola stringa $* che contiene tutti gli argomenti separati da spazi in una sola stringa $@ che contiene tutti gli argomenti tra virgolette e separati da spazi $@ che contiene tutti gli argomenti tra virgolette e separati da spazi Se ad esempio gli argomenti sono Se ad esempio gli argomenti sono a1 a2 "a3 con spazi" a4 a1 a2 "a3 con spazi" a4 allora allora $1=a1, $2=a2, $3=a3 con spazi, $4=a4 $1=a1, $2=a2, $3=a3 con spazi, $4=a4 $*=a1 a2 a3 con spazi a4 $*=a1 a2 a3 con spazi a4 ” ”$@"="a1" "a2" "a3 con spazi" "a4" $@"="a1" "a2" "a3 con spazi" "a4" $# $# contiene il numero di argomenti passati (escluso $0). contiene il numero di argomenti passati (escluso $0). Shell Unix
Per creare una variabile è sufficiente assegnarle un valore: nome=”Paolo” nome=”Paolo” eta=30 eta=30 Invece quando si usa una variabile la sitassi cambia ed è necessario anteporre al nome della variabile il Invece quando si usa una variabile la sitassi cambia ed è necessario anteporre al nome della variabile il simbolo $, ad esempio: simbolo $, ad esempio: echo $name echo $name E' anche possibile fare uso di array che possono essere dichiarati inizializzandone gli elementi: E' anche possibile fare uso di array che possono essere dichiarati inizializzandone gli elementi: nomi[0]=Mario nomi[0]=Mario nomi[1]=Marco nomi[1]=Marco Inoltre: Inoltre: ${nomi[*]} ${nomi[*]} ritorna tutti gli elementi dell'array ritorna tutti gli elementi dell'array ${#nomi[*]} ${#nomi[*]} ritorna il numero di elementi dell'array ritorna il numero di elementi dell'array Shell Unix
e leggere dati direttamente nelle variabili. Gli scipt della shell possono scrivere sullo standard output e leggere dati direttamente nelle variabili. Esistono due funzioni di output: Esistono due funzioni di output: echo echo stampa gli argomenti separati da spazi e terminati da newline sullo standard output stampa gli argomenti separati da spazi e terminati da newline sullo standard output (supporta le convenzioni escape del linguaggio C: \n, \t, …) (supporta le convenzioni escape del linguaggio C: \n, \t, …) -n -n sopprime i caratteri newline sopprime i caratteri newline print print (nativa della Korn shell) stampa gli argomenti separati da spazi e terminati da newline sullo (nativa della Korn shell) stampa gli argomenti separati da spazi e terminati da newline sullo standard output standard output -n -n sopprime i caratteri newline sopprime i caratteri newline -r -r raw mode – ignora le convenzioni \-escape raw mode – ignora le convenzioni \-escape -R -R raw mode – ignora le convenzioni \-escape e le altre opzioni (tranne -n) raw mode – ignora le convenzioni \-escape e le altre opzioni (tranne -n) Per la lettura dallo standard input è possibile usare la funzione Per la lettura dallo standard input è possibile usare la funzione read read var1 var2 var3 … var1 var2 var3 … che legge una riga dalla standard input ed inserisce le stringhe separate da spazi nelle relative variabili. che legge una riga dalla standard input ed inserisce le stringhe separate da spazi nelle relative variabili. Shell Unix
controllo di flusso richiedono l'utilizzo di test e confronti. La maggior parte dei costrutti per il controllo di flusso richiedono l'utilizzo di test e confronti. I confronti possono essere effettuati mediante: I confronti possono essere effettuati mediante: il comando il comando test test test test $nome=”Paolo” $nome=”Paolo” o i suoi equivalenti o i suoi equivalenti […] […] e e [[…]] [[…]] (per bash e ksh) (per bash e ksh) [ [ $nome=”Paolo” $nome=”Paolo” ] ] [[ [[ $nome=”Paolo” $nome=”Paolo” ]] ]] i confronti tra stringhe si effettuano mediante i confronti tra stringhe si effettuano mediante = = con ovvio significato con ovvio significato != != per diverso per diverso < < per minore per minore > > per maggiore per maggiore I confronti tra numeri si effettiano nel seguente modo: I confronti tra numeri si effettiano nel seguente modo: -eq -eq per uguale per uguale -ne -ne per diverso per diverso -lt -lt per minore per minore -gt -gt per maggiore per maggiore Tutte le condizioni possono essere combinate con && (indica l'AND) e || (indica l'OR) Tutte le condizioni possono essere combinate con && (indica l'AND) e || (indica l'OR)
una serie di costrutti per il controllo di flusso, condizioni di branch e loop. Infine ci sono tutta una serie di costrutti per il controllo di flusso, condizioni di branch e loop. Vediamone rapidamente la sintassi: Vediamone rapidamente la sintassi: if then else fi if then else fi if [[ $value -eq 7 ]] if [[ $value -eq 7 ]] then then print ”il valore è 7” print ”il valore è 7” else else print ”il valore è diverso da 7” print ”il valore è diverso da 7” fi fi if then elif then else fi if then elif then else fi if [[ $nome = "Paolo" ]]; then if [[ $nome = "Paolo" ]]; then print "Benvenuto, ${nome}." print "Benvenuto, ${nome}." elif [[ $nome = "Luca" ]];then elif [[ $nome = "Luca" ]];then print "Ciao, ${nome}, come va?" print "Ciao, ${nome}, come va?" else else print "Arrivederci, ${nome}!" print "Arrivederci, ${nome}!" fi fi N.B. Un comando può essere terminato andando a capo oppure con ”;” N.B. Un comando può essere terminato andando a capo oppure con ”;”
list do done for var in list do done for foo in $(ls);do for foo in $(ls);do if [[ -d $f ]];then if [[ -d $f ]];then print "$f è una directory" print "$f è una directory" else else print "$f non è una directory" print "$f non è una directory" fi fi done done E' possibile saltare un'iterazione in un ciclo usando il comando ”continue” E' possibile saltare un'iterazione in un ciclo usando il comando ”continue” E' possibile uscire da un ramo di uno dei costrutti di branch usando il comando ”break” E' possibile uscire da un ramo di uno dei costrutti di branch usando il comando ”break”