Introduzione allo Shell Scripting |
In questo capitolo saranno raccolti degli script più complicati (niente paura, nulla di difficle), vicini alla realtà di un utente e pertanto utili. Eventualmente, si troveranno contributi da parte degli utenti, i quali possono inviare i propri script con un breve commento.
Gli esempi saranno divisi per argomento ed i commenti saranno, per la
maggior parte, all'interno del codice stesso.
In questa sezione verranno riportati alcuni script utili nell'uso quotidiano.
Negli ultimi anni sono state sviluppate nuove tecnologie e nuovi linguaggi per semplificare la vita a chi scrive cgi (Common Gateway Interface, volgarmente: pagine web a contenuto dinamico). Creare un cgi con Bash può pertanto sembrare una forzatura, dato che questa non è la scelta tecnica migliore, ciò nonostante tratteremo questo argomento perché ricco dal punto di vista didattico.
Per semplicità, tratteremo in dettaglio soltanto il metodo
GET del protocollo HTTP, POST sarà
accennato verso la fine.
Per poter eseguire con successo lo script, occorrerà avere un server
web (ad esempio Apache,
http://httpd.apache.org) funzionante,
configurato per supportare l'esecuzione di cgi.
Utilizzando il metodo GET, le ``coppie''
vengono passate attraverso l'url. Ad esempio
spesso vediamo url del tipo :
Vediamo dunque che le coppie variabili=valore sono separate
l'una dall'altra attraverso il carattere
, mentre sono
separate dal percorso dello script attraverso
.
Vediamo nella pratica come realizzare lo script.
#!/bin/bash # # Esempio di cgi con bash EXIT_SUCCESS=0 # dichiariamo un vettore in cui salvare i valori delle variabili # passate con GET declare -a QUERY # Una funzione attraverso la quale impostare la parte di testa # della pagina. La stringa "Content-type: text/html" serve ad istruire # il browser ad interpretare quanto segue come html. Se avessimo, # invece scritto "Content-type: text/plain" otterremmo la pagina # senza che il codice html venga interpretato. # # La funzione accetta un argomento, il titolo della pagina. sopra () { cat <<EOF Content-type: text/html <HTML> <HEAD> <TITLE>$1</TITLE> </HEAD> <BODY> <!-- fine parte superiore --> EOF } # Con questa funzione, invece, definiamo gli elementi comuni della # parte inferiore della pagina, in modo da chiuderla correttamente. sotto () { cat <<EOF <!-- inizio parte inferiore --> </BODY> </HTML> EOF } # La funzione che segue è il "cuore" dello script, grazie a questa # possiamo analizzare il contenuto di $QUERY_STRING, ottenere il # valore di ogni variabile e salvarlo in un elemento del vettore # $QUERY. # # Si noti il particolare modo in cui abbiamo iterato con il ciclo # while; abbiamo dovuto "sprecare" il primo elemento del vettore. Se # non avessimo agito in questo modo, infatti, la condizione valutata # dal ciclo sarebbe stata falsa alla prima esecuzione e non avremmo # mai ottenuto le variabili che ci interessano. Questo artificio # simula in qualche modo il ciclo "do ... while" presente in altri # linguaggi quali ad esempio il C. analizza_query () { i=0 QUERY[0]="OK" while [ -n "${QUERY[${i}]}" ]; do # Aumentiamo subito il valore di $i ((i=$i+1)) # Otteniamo il valore delle variabili QUERY[${i}]="$(echo $QUERY_STRING | cut -d \& -f ${i} \ | cut -d = -f 2 | sed 's/+/ /g')" done } # Questa funzione ci consente di ottenere una form attraverso la quale # ottenere un input dall'esterno. prima_pagina () { cat <<EOF <P> Inserisci nome e cognome: </P> <FORM method="GET" action="$(basename $0)"> <P> <B>Nome</B>:<BR> <INPUT type="TEXT" name="nome"> </P> <P> <B>Cognome</B>:<BR> <INPUT type="TEXT" name="cognome"> </P> <INPUT type="SUBMIT"> </FORM> EOF } # Con questa funzione mostriamo i valori ottenuti dall'esterno. seconda_pagina () { cat <<EOF <H2> Ciao ${QUERY[1]} ${QUERY[2]}! </H2> <P> Hai visto quanto è divertente fare cgi con bash? </P> <P> <A href="$(basename $0)">Torna alla pagina precedente</A> </P> EOF } # FINE delle funzioni analizza_query if [ -z "${QUERY[1]}" ]; then sopra "Inserisci nome e cognome" prima_pagina else sopra "Risultato" seconda_pagina fi sotto exit $EXIT_SUCCESS
Analizzeremo principalmente parte dellla funzione analizza_query() ed il codice al di sotto di # FINE delle funzioni.
Procediamo con ordine ed occupiamoci in particolare della stringa di
codice
QUERY[${i}]="$(echo $QUERY_STRING | cut -d \& -f ${i} \ | cut -d = -f 2 | sed 's/+/ /g')"presente all'interto di analizza_query(). QUERY[${i}] è un elemento di un vettore, mentre la stringa di desta è il risultato di una serie di pipe di alcuni comandi,
echo $QUERY_STRING | cut -d \& -f ${i} \ | cut -d = -f 2 | sed 's/+/ /g'}Analizziamoli uno ad uno. Il primo, echo $QUERY_STRING, stampa sullo standard output il contenuto della variabile $QUERY_STRING che, grazie alla pipe, diventa standard input di cut -d & -f ${i}. In generale, cut è un comando che serve ad eseguire operazioni avanzate di estrazione di testo da una stringa; in particolare, con la sintassi utilizzata, diciamo a cut di dividere la stringa in campi (Utilizzando come separatore &, che deve essere preceduto da un backslash per evitare che la shell lo interpreti come istruzione) ed estrarne quello di posto ${i}. In questo modo, otteniamo la coppia variabile=valore di posto ${i}.
Il secondo cut è del tutto analogo al primo, tuttavia questa
volta si usa come separatore di campi il carattere = (Non ha
bisogno di alcun escape) e se ne ritorna il campo di posto 2; così
facendo, otteniamo il valore della variabile in esame.
In ultimo, dobbiamo tener presente il fatto che ogni spazio presente
nel valore di una variabile viene sostituito con un +,
pertanto, quando ritorniamo i valori dobbiamo ricordarci di
risostituire ogni occorrenza di + con uno spazio. Questa
operazione viene effettuata attraverso il comando
.
Passiamo ora ad analizzare il resto. Dopo aver dichiarato tutte le
funzioni necessarie, inizia la parte dello script che prende le
decisioni. Innanzi tutto, lanciamo la funzione
analizza_query() e decidiamo cosa fare a seconda del suo
operato, infatti controlliamo l'elemento ${QUERY[1]}, se il suo
valore non è impostato, allora lo script è stato richiamato senza
passare alcun paramentro nell'url, dunque eseguiamo le funzioni
sopra() (Passandole come argomento la stringa ``Inserisci
nome e cognome'') e prima_pagina() per raccogliere i dati.
Se, invece, il valore di ${QUERY[1]} fosse impostato, allora lo
script sarebbe stato richiamato passandogli dei parametri, quindi si
richiamerebbero le funzioni sopra() (Argomento ``Risultato'')
e seconda_pagina() in modo da mostrare i valori raccolti.
In ultimo, viene richiamata la funzione sotto() per chiudere
correttamente il codice html della pagina e si ritorna un valore di
successo.
Con one-line intendiamo dei semplici script che si sviluppano tutti su una sola linea di codice, utili spesso per portare a termine in un batter d'occhio i lavori sporchi.
Può capitare, ad esempio, di avere a che fare con programmi che non rispondono più ai comandi (raramente) ed occorre dunque terminarli. Andare alla ricerca del PID (Process ID) del processo per poi mandare al programma un segnale di KILL può essere fastidioso, sarebbe più bello se, magari, potessimo fare tutto fornendo il solo nome del programma. Siamo fortunati, leggendo la pagina manuale di ps, possiamo notare l'esistenza dell'opzione -C (Controllate!) che fa proprio al caso nostro. Vediamo, dunque, come fare.
$ kill -9 $(ps -C nome_comando -o pid=) o $ kill -KILL $(ps -C nome_comando -o pid=)
Analizziamo passo passo ciò che abbiamo scritto. Il comando kill serve ad inviare segnali ai processi; nel nostro caso, stiamo inviando il segnale di KILL (terminazione) attraverso l'opzione -9 5.1. Tale comando necessita come input del PID del processo in questione, per questo motivo abbiamo utilizzato l'espanzione di comandi $( ... ) su , con le opzioni specificate, infatti, ps dà come output il pid5.2del processo lanciato da nome_comando5.3. Per concludere, se volessimo terminare il processo lanciato dal programma gabber5.4 dovremmo digitare i comandi:
$ kill -9 $(ps -C gabber -o pid=)
Introduzione allo Shell Scripting |