Nella precedente lezione sono stati definiti gli elementi base della sintassi Bash, con l'ausilio dei quali vengono manipolati i parametri e costruiti i comandi composti. Questi, a loro volta, si ottengono utilizzando, come costituenti, i comandi primitivi della shell assieme a quelli già definiti in precedenza.
Fra i comandi predefiniti quello di test riveste un ruolo fondamentale perchè attraverso di esso è possibile implementare la valutazione delle espressioni booleane. Si ricorda, a questo proposito, che la shell non esegue implicitamente alcuna espressione aritmetica o logica ma, soltanto su richiesta del comando test.
Nel seguito verrà trattato diffusamente tale comando e, successivamente, verranno discussi alcuni esempi significativi che riprendono e illustrano alcune delle caratteristiche della shell.
A completamento della lista di comandi predefiniti della shell Bash, si riporta nel seguito la descrizione dettagliata delle possibili opzioni utilizzabili con il comando test. Tuttavia, per un maggior approfondimento degli argomenti finora discussi si faccia riferimento al manuale della shell in formato PDF oppure interattivo ipertestuale.
Il comando test args può anche rappresentarsi con una coppia di parantesi quadre [ args ] con l'avvertenza di lasciare uno spazio bianco fra le parentesi quadre e l'espressione da valutare.
COSTRUTTO | BOURNE SHELL | C-LIKE SHELL |
Nuovo Input |
cmd << mark ......... .... ......... mark |
cmd << mark ......... .... ......... mark |
cosicchè durante l'esecuzione dello script quando si incontra la riga contenente il comando cmd, tutte le righe successive, fino a quella contenente mark vengono passate come input a cmd. Se la costante mark è quotata, allora le righe vengono prese letteralmente, altrimenti si procede alle usuali regole di espansione che la shell implementa sugli argomenti.
I tre esempi proposti nel seguito esemplificano la metodologia di implementazione degli shell script e, in particolare, il secondo dove vengono utilizzati tutti i costrutti standard della bash per la manipolazione di variabili e file.
Il seguente programma riordina le le righe di un file secondo i valori interi crescenti contenuti nel primo campo di ciascuna riga. Vengono utilizzato i programmi di utilità generale wc, cat, head e sort oltre, naturalmente, a sed e expr
#!/bin/bash # Usage: fsort file # if [ $# -ne 1 ] > /dev/null; then echo 'insufficient parameter' exit 1 fi NAME=$1 sort +0 $NAME > tmp$$ L=`wc -l $NAME | sed s/$NAME//` echo $L K=0 while [ $K -ne $L ] do K=`expr $K + 1` cat `echo -n $K` head -1 tmp$$ >> sort$$ done cat tmp$$ /bin/rm -f tmp$$ echo cat sort$$ /bin/rm -f sort$$ exit 0 |
Anche in quest'ultimo esempio l'implementazione si appoggia su comandi di shell già noti (wc, rm, sort, ecc...) ma ora si utilizzano file temporanei di supporto all'elaborazione che, per evitare sovrapposizioni non volute, sono indicizzati coll'identificatore di processo ($$).
L'interprete della shell, che nel caso di Linux è rappresentato dalla bash, viene attivato ogno qualvolta si apre un X terminal a partire dalla barra degli strumenti della console grafica o si apre una sessione telnet, sia localmente che in modalià remota.
Lo schema di funzionamento dell'interprete è abbastanza semplice. Dopo aver scritto una stringa di prompt sul terminale, l'interprete attende che l'utente introduca una riga di comando. A questo punto si passa alla sua lettura che prevede il riconoscimento del comando con tutti i suoi parametri ed opzioni. Nell'implementazione proposta di seguito, si utilizza il costrutto while per la gestione del ciclo esecutivo, affiancato dalla variabile di controllo di terminazione $HALT. Il comando predefinito read serve a leggere la riga di comando.
La gestione della storia dei comandi è affidata alla variabile $HISTORY che viene inizializzata in base al contenuto del file storico $STORIA e riscritto al termine della sessione. I comandi implementati sono !?, per la lettura dello storico, !del, per la sua cancellazione, !!, per eseguire l'ultimo comando e !n, per l'esecuzione dell'n-esimo comando già eseguito.
#!/bin/bash # Synopsis: minishell -flag # possible flags: c,d,e,s,x function Banner() { echo "Minishell based on Bash shell"; echo "version 1.0"; echo; if [ -f "$STORIA" ]; then HISTORY=$(sed 's/^$/\n/g' $STORIA); fi } function Execute() { # Execute(**char $1, char $2, **char $3) case "$2" in "d") echo "$3" ;; "x") $3 ;; "e") eval $3 ;; "s") ( $3 ) ;; "c") command $3 ;; esac HISTORY=$(echo -e "$1\n$3" | sed "/^$/d"); } function Help() { echo "quit exit"; echo "!del delete History log"; echo "!! execute last command"; echo "!? list all command"; echo "!n execute the n-th command"; echo; } function get_Args() { K=0 ; ARGS="" for arg in "$1" do if [ "$K" -eq 0 ] then CMD=$arg ; else ARGS="$ARGS $arg" ; fi K=`expr $K + 1` done } function Command() { # Command([TAG] char $1, [M] int $2) N=$(echo -e "$HISTORY" | wc -l); if [ "$2" -lt "$N" ]; then CMDLINE=$(echo -e "$HISTORY" | tail --lines=+$2 | head -1); echo "$PROMPT$CMDLINE" Execute "$HISTORY" $1 "$CMDLINE"; return 0 else return 1 fi } declare -i HALT=0 declare -x ARGS declare -x CMD if [ "$#" -lt 1 ]; then echo "Possible options are: d,e,x,s,c" echo "Try -help for more information" exit 1 fi TAG=`echo $1 | sed 's/-//'` if [ "$TAG" == "help" ]; then Help; exit 5 elif [ "$TAG" != "c" -a "$TAG" != "d" -a "$TAG" != "e" -a \ "$TAG" != "s" -a "$TAG" != "x" ]; then echo "no such an option" exit 6 fi PROMPT="msh> " if [ -d ".labos" ]; then mkdir ~/.labos fi STORIA=~/.labos/mshistory Banner; while [ "$HALT" -eq 0 ]; do read -p "$PROMPT" ; get_Args "$REPLY" ; case $CMD in quit) HALT=1 ;; help) Help;; !\?) echo -e "$HISTORY" | cat -b;; !del) HISTORY="";; !!) CMD=$(echo -e "$HISTORY" | tail -1); Execute "$HISTORY" $TAG "$CMD $ARGS";; ![0-9]*[a-z]*) HALT=2;; ![a-z]*) HALT=3;; ![0-9]*) M=$(echo "$CMD" | sed 's/!//'); Command $TAG $M if [ "$?" -ne 0 ]; then HALT=4 fi ;; *) Execute "$HISTORY" $TAG "$CMD $ARGS" ;; esac done echo "$HISTORY" > $STORIA case $HALT in 1) exit 0;; 2) echo "Syntax error"; exit 2 ;; 3) echo "Syntax error"; exit 3 ;; 4) echo "Syntax error"; exit 4 ;; esac |
Nel semplice esempio che segue viene utilizzato la ridirezione di input per fornire i parametri necessari al comando sendmail. Il template viene istanziato dai valori correnti associati ai paramentri posizionali durante la chiamata del comando msend la cui unica funzione è quella di preparare i parametri per sendmail.
#/bin/bash if [ "$#" -eq 0 ] then echo "Usage: msend recipient subject integer" exit 1 fi sendmail $1 << EOT subject: $2 questo e' il messaggio di prova n. $3 EOT exit 0 |