<?xml version="1.0" encoding="iso-8859-15" standalone="no"?>
<!--
The FreeBSD Italian Documentation Project
$FreeBSD$
-->
<chapter id="shell-programming">
<title>Programmazione di shell</title>
<sect1 id="shell-programming-scripts">
<title>Script di shell</title>
<para>Si possono scrivere programmi di shell creando script contenenti
alcuni comandi di shell. La prima linea dello script deve iniziare con
#!, che indica al kernel che lo script è direttamente eseguibile.
Si fa immediatamente seguire a quel simbolo il nome della shell
o del programma da eseguire (gli spazi sono permessi), usando un path name
assoluto. Generalmente si possono avere fino a 32 caratteri, forse di
più su alcuni sistemi e si può includere qualche opzione.
Quindi per inizializzare uno script per la shell Bourne la prima linea
dovrà essere:</para>
<programlisting>#! /bin/sh</programlisting>
<para>e per la shell C:</para>
<programlisting>#! /bin/csh -f</programlisting>
<para>dove l'opzione <option>-f</option> indica che la shell non deve
leggere il file <filename>.cshrc</filename>. Alcuni spazi bianchi
seguenti il magico simbolo, #!, sono opzionali.</para>
<para>Inoltre si deve specificare che lo script è eseguibile,
settando gli opportuni bit sul file con il comando &man.chmod.1;,
esempio:</para>
<screen>&prompt.user; <userinput>chmod +x <replaceable>shell_script</replaceable></userinput></screen>
<para>All'interno degli script il simbolo # indica un commento da quel punto
fino alle fine della linea; #! è un caso speciale se trovato come
primo carattere del file.</para>
</sect1>
<sect1 id="shell-programming-sett-param-val">
<title>Settare i valori dei parametri</title>
<para>I valori di un parametro, ad esempio <literal>param</literal>, sono
assegnati così:</para>
<informaltable frame="none">
<tgroup cols="2">
<colspec colwidth="1.5in"/>
<thead>
<row>
<entry>Shell Bourne</entry>
<entry>Shell C</entry>
</row>
</thead>
<tbody>
<row>
<entry><command>param=<replaceable>valore</replaceable></command></entry>
<entry><command>set param =
<replaceable>valore</replaceable></command></entry>
</row>
</tbody>
</tgroup>
</informaltable>
<para>dove <replaceable>valore</replaceable> è una valida stringa che
può essere chiusa tra caratteri di quoting singoli
('<replaceable>valore</replaceable>') o doppi
("<replaceable>valore</replaceable>") per permettere alcuni spazi bianchi
all'interno del valore della stringa. Quando viene racchiusa con dei
caratteri backquote (`<replaceable>valore</replaceable>`) la stringa viene
prima valutata dalla shell e viene sostituita con il risultato ottenuto
dalla valutazione. Questo viene spesso usato per eseguire un comando,
sostituendo l'output del comando a <replaceable>valore</replaceable>,
esempio:</para>
<screen>$ <userinput>day=`date +%a`</userinput></screen>
<screen>$ <userinput>echo $day</userinput>
Wed</screen>
<para>Dopo che il valore del parametro è stato assegnato, si accede
al valore corrente del parametro usando la notazione
<literal>$param</literal> o <literal>${param}</literal>.</para>
</sect1>
<sect1 id="shell-programming-quoting">
<title>Quoting</title>
<para>Le stringhe possono essere quotate per controllare il modo in cui la
shell interpreta alcuni parametri o variabili all'interno della stringa.
Per delimitare le stringhe si possono usare i caratteri di quoting singoli
(') o doppi ("). I caratteri di quoting doppi definiscono la stringa e
permettono la sostituzione di variabile. I carattere di quoting singoli
definiscono la stringa ma impediscono la sostituzione di variabile.
Un backslash (\) prima di un carattere viene posto per effettuare un
escape su di esso, specificando che il sistema deve considerare il
carattere letteralmente, senza assegnarli alcun significato speciale.
Queste tecniche di quoting possono essere usate per separare una variabile
da una stringa fissa. Come esempio si consideri la variabile
<literal>var</literal>, a cui è stata assegnata il valore
<literal>bat</literal>, e la stringa costante <literal>man</literal>. Se
si vuole combinare queste stringhe per ottenere come risultato la stringa
<literal>batman</literal> si può sperimentare:</para>
<para><literal>$varman</literal></para>
<para>ma questo non funzionerà, poichè la shell tenta di
valutare una variabile chiamata <literal>varman</literal>, che non esiste.
Per ottenere il risultato desiderato si ha la necessità di separare
le stringhe tramite quoting o di isolare la variabile con delle parentesi
graffe ({}), in questo modo:</para>
<informaltable frame="none">
<tgroup cols="2">
<colspec colwidth="1.5in"/>
<tbody>
<row>
<entry><literal>"$var"man</literal></entry>
<entry>- quoting sulla variabile</entry>
</row>
<row>
<entry><literal>$var""man</literal></entry>
<entry>- separazione di parametri</entry>
</row>
<row>
<entry><literal>$var"man"</literal></entry>
<entry>- quoting sulla costante</entry>
</row>
<row>
<entry><literal>$var''man</literal></entry>
<entry>- separazione di parametri</entry>
</row>
<row>
<entry><literal>$var'man'</literal></entry>
<entry>- quoting sulla costante</entry>
</row>
<row>
<entry><literal>$var\man</literal></entry>
<entry>- separazione di parametri</entry>
</row>
<row>
<entry><literal>${var}man</literal></entry>
<entry>- si isola la variabile</entry>
</row>
</tbody>
</tgroup>
</informaltable>
<para>Queste funzionano tutte poichè ", ', \, { e } non sono validi
caratteri per un nome di variabile.</para>
<para>Non si può usare</para>
<para><literal>'$var'man</literal></para>
<para><literal>\$varman</literal></para>
<para>poichè impediscono che la sostituzione della variabile
prenda posto.</para>
<para>Quando si usano le parentesi graffe, queste devono circondare
solamente la variabile, senza includere il $, altrimenti saranno incluse
come parte del risultato della stringa, esempio:</para>
<screen>&prompt.user; <userinput>echo {$var}man</userinput>
{bat}man</screen>
</sect1>
<sect1 id="shell-programming-var">
<title>Variabili</title>
<para>Alcune variabili sono automaticamente inizializzate all'avvio della
shell. Queste variabili permettono di riferirsi agli argomenti su linea
di comando.</para>
<para>Queste <emphasis>variabili di shell</emphasis> sono:</para>
<table frame="all" id="shell-programming-table-shell-var">
<title>Variabili di shell</title>
<tgroup cols="4">
<colspec colwidth="1in"/>
<colspec colwidth="5in"/>
<colspec colwidth="0.3in"/>
<colspec colwidth="0.3in"/>
<thead>
<row>
<entry align="center">Variabile</entry>
<entry align="center">Uso</entry>
<entry align="center">sh</entry>
<entry align="center">csh</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>$#</literal></entry>
<entry>numero di argomenti su linea di comando</entry>
<entry align="center">x</entry>
<entry align="center"></entry>
</row>
<row>
<entry><literal>$-</literal></entry>
<entry>opzioni fornite alla shell</entry>
<entry align="center">x</entry>
<entry align="center"></entry>
</row>
<row>
<entry><literal>$?</literal></entry>
<entry>valore di uscita dell'ultimo comando eseguito</entry>
<entry align="center">x</entry>
<entry align="center"></entry>
</row>
<row>
<entry><literal>$$</literal></entry>
<entry>numero id del processo corrente</entry>
<entry align="center">x</entry>
<entry align="center">x</entry>
</row>
<row>
<entry><literal>$!</literal></entry>
<entry>numero di processo dell'ultimo comando messo in
background</entry>
<entry align="center">x</entry>
<entry align="center"></entry>
</row>
<row>
<entry><literal>$<replaceable>n</replaceable></literal></entry>
<entry>argomento su linea di comando, dove
<replaceable>n</replaceable> varia tra 1 e 9, leggendo da sinistra
a destra</entry>
<entry align="center">x</entry>
<entry align="center">x</entry>
</row>
<row>
<entry><literal>$0</literal></entry>
<entry>il nome della shell corrente o del programma corrente</entry>
<entry align="center">x</entry>
<entry align="center">x</entry>
</row>
<row>
<entry><literal>$*</literal></entry>
<entry>tutti gli argomenti su linea di comando ("$1 $2 ...
$9")</entry>
<entry align="center">x</entry>
<entry align="center">x</entry>
</row>
<row>
<entry><literal>$@</literal></entry>
<entry>tutti gli argomenti su linea di comando, ciascuno quotato
separatamente ("$1" "$2" ... "$9")</entry>
<entry align="center">x</entry>
<entry align="center"></entry>
</row>
<row>
<entry><literal>$argv[<replaceable>n</replaceable>]</literal></entry>
<entry>seleziona l'<replaceable>n-esima</replaceable> parola dalla
lista di input</entry>
<entry align="center"></entry>
<entry align="center">x</entry>
</row>
<row>
<entry><literal>${argv[<replaceable>n</replaceable>]}</literal></entry>
<entry>come sopra</entry>
<entry align="center"></entry>
<entry align="center">x</entry>
</row>
<row>
<entry><literal>$#argv</literal></entry>
<entry>riporta il numero di parole della lista di input</entry>
<entry align="center"></entry>
<entry align="center">x</entry>
</row>
</tbody>
</tgroup>
</table>
<para>L'uso di queste variabili può essere illustrato con alcuni
semplici script. Per la shell Bourne lo script potrebbe essere:</para>
<programlisting>#!/bin/sh
echo "$#:" $#
echo '$#:' $#
echo '$-:' $-
echo '$?:' $?
echo '$$:' $$
echo '$!:' $!
echo '$3:' $3
echo '$0:' $0
echo '$*:' $*
echo '$@:' $@</programlisting>
<para>Quando viene eseguito con alcuni argomenti, mostra i valori delle
variabili di shell, esempio:</para>
<screen>$ <userinput>./variables.sh one two three four five</userinput>
5: 5
$#: 5
$-:
$?: 0
$$: 12417
$!:
$3: three
$0: ./variables.sh
$*: one two three four five
$@: one two three four five</screen>
<para>Come si può notare, si ha la necessità di usare un
carattere di quoting singolo per impedire alla shell di assegnare
significati speciali a $. Il carattere di quoting doppio, come nella
prima struttura <command>echo</command>, permette di rimpiazzare il nome
della variabile con il suo valore.</para>
<para>Similmente, per le variabili della shell C si possono illustrare le
sostituzioni di variabili tramite il seguente script:</para>
<programlisting>#!/bin/csh -f
echo '$$:' $$
echo '$3:' $3
echo '$0:' $0
echo '$*:' $*
echo '$argv[2]:' $argv[2]
echo '${argv[4]}:' ${argv[4]}
echo '$#argv:' $#argv</programlisting>
<para>che quando eseguito con alcuni argomenti mostra il risultato
seguente:</para>
<screen>&prompt.user; <userinput>./variables.csh one two three four five
</userinput>
$$: 12419
$3: three
$0: ./variables.csh
$*: one two three four five
$argv[2]: two
${argv[4]}: four
$#argv: 5</screen>
</sect1>
<sect1 id="shell-programming-param-sost">
<title>Sostituzione di parametri</title>
<para>Si può riferirsi ai parametri in modo astratto e sostituire i
loro valori in base a delle condizioni, usando gli operatori definiti qui
sotto. Ancora una volta si possono usare le parentesi graffe ({}) per
isolare la variabile e il suo operatore.</para>
<informaltable frame="none">
<tgroup cols="2">
<colspec colwidth="2in"/>
<tbody>
<row>
<entry><literal>$<replaceable>parametro</replaceable></literal></entry>
<entry>sostituisce questa stringa con il valore di
<replaceable>parametro</replaceable></entry>
</row>
<row>
<entry><literal>${<replaceable>parametro</replaceable>}</literal></entry>
<entry>come sopra. Le parentesi sono d'aiuto se non c'è
separazione tra questo parametro e una stringa adiacente.</entry>
</row>
<row>
<entry><literal>$<replaceable>parametro</replaceable>=</literal></entry>
<entry>setta <replaceable>parametro</replaceable> a
<emphasis>null</emphasis>.</entry>
</row>
<row>
<entry><literal>${<replaceable>parametro</replaceable>-<replaceable>default</replaceable>}</literal></entry>
<entry>se <replaceable>parametro</replaceable> non è settato
allora si usa <replaceable>default</replaceable> come valore.
<replaceable>parametro</replaceable> non viene resettato.</entry>
</row>
<row>
<entry><literal>${<replaceable>parametro</replaceable>=<replaceable>default</replaceable>}</literal></entry>
<entry>se <replaceable>parametro</replaceable> non è settato
allora lo si setta a <replaceable>default</replaceable> e si usa
il nuovo valore</entry>
</row>
<row>
<entry><literal>${<replaceable>parametro</replaceable>+<replaceable>nuovo_valore</replaceable>}</literal></entry>
<entry>se <replaceable>parametro</replaceable> è settato
allora si usa <replaceable>nuovo_valore</replaceable> altrimenti
non si usa nulla. <replaceable>parametro</replaceable> non viene
resettato.</entry>
</row>
<row>
<entry><literal>${<replaceable>parametro</replaceable>?<replaceable>messaggio</replaceable>}</literal></entry>
<entry>se <replaceable>parametro</replaceable> non è settato
allora si visualizza il messaggio. Se
<replaceable>parametro</replaceable> è settato allora si
usa il valore corrente.</entry>
</row>
</tbody>
</tgroup>
</informaltable>
<para>Non ci sono spazi nei precedenti operatori. Se un due punti (:) viene
inserito prima di -, =, + o ? allora si effettua prima un test per vedere
se il parametro ha un settaggio <emphasis>non-nullo</emphasis>.</para>
<para>La shell C ha alcuni modi aggiuntivi per la sostituzione di
parametri:</para>
<informaltable frame="none">
<tgroup cols="2">
<colspec colwidth="1.5in"/>
<tbody>
<row>
<entry><literal>$lista[<replaceable>n</replaceable>]</literal></entry>
<entry>seleziona l'<replaceable>n-esima</replaceable> parola dalla
lista</entry>
</row>
<row>
<entry><literal>${lista[<replaceable>n</replaceable>]}</literal></entry>
<entry>come sopra</entry>
</row>
<row>
<entry><literal>$#lista</literal></entry>
<entry>riporta il numero di parole in lista</entry>
</row>
<row>
<entry><literal>$?<replaceable>parametro</replaceable></literal></entry>
<entry>ritorna 1 se il parametro è settato, 0
altrimenti</entry>
</row>
<row>
<entry><literal>${?<replaceable>parametro</replaceable>}</literal></entry>
<entry>come sopra</entry>
</row>
<row>
<entry><literal>$<</literal></entry>
<entry>legge una linea da &man.stdin.4;</entry>
</row>
</tbody>
</tgroup>
</informaltable>
<para>Inoltre la shell C definisce l'array
<literal>$argv[<replaceable>n</replaceable>]</literal> per
contenere gli <replaceable>n</replaceable> argomenti della linea di
comando e <literal>$#argv</literal> per il numero di argomenti, come
specificato in <link linkend="shell-programming-table-shell-var">Tabella
9.1</link>.</para>
<para>Per illustrare alcune di queste caratteristiche si userà il
seguente script di prova:</para>
<programlisting>#!/bin/sh
param0=$0
test -n "$1" && param1=$1
test -n "$2" && param2=$2
test -n "$3" && param3=$3
echo 0: $param0
echo "1: ${param1-1}: \c" ;echo $param1
echo "2: ${param2=2}: \c" ;echo $param2
echo "3: ${param3+3}: \c" ;echo $param3</programlisting>
<para>Inizialmente nello script si verifica con &man.test.1; se la
variabile esiste; in tal caso si setta il parametro al suo valore.
Dopo si riportano i valori, effettuando le sostituzioni.</para>
<para>Nella prima esecuzione dello script non vengono forniti
argomenti:</para>
<screen>$ <userinput>./parameter.sh</userinput>
0: ./parameter.sh # trova sempre $0
1: 1: # sostituisce 1, ma non assegna questo valore
2: 2: 2 # sostituisce 2 e assegna questo valore
3: : # non sostituisce</screen>
<para>In questa seconda esecuzione dello script si forniscono alcuni
argomenti:</para>
<screen>$ <userinput>./parameter one two three</userinput>
0: ./parameter.sh # trova sempre $0
1: one: one # non sostituisce, ha già un valore
2: two: two # non sostituisce, ha già un valore
3: 3: three # sostituisce 3 ma non assegna questo valore</screen>
</sect1>
<sect1 id="shell-programming-here-doc">
<title>Here document</title>
<para>Un <emphasis>here document</emphasis> è una forma di quoting
che permette alle variabili di shell di essere sostituite. È una
forma speciale di redirezione che inizia con una linea contenente
solamente <emphasis><<<replaceable>PAROLA</replaceable></emphasis>
e finisce con una linea contenete solamente
<emphasis><replaceable>PAROLA</replaceable></emphasis>. Nella shell
Bourne si può impedire la sostituzione di shell effettuando un
escape su <emphasis><replaceable>PAROLA</replaceable></emphasis>, mettendo
un \ davanti a <emphasis><replaceable>PAROLA</replaceable></emphasis>
sulla linea di redirezione, esempio
<emphasis><<\<replaceable>PAROLA</replaceable></emphasis>, ma non
sulla linea finale. Per avere lo stesso effetto con la shell C si mette
il \ davanti a <emphasis><replaceable>PAROLA</replaceable></emphasis> in
entrambi i posti.</para>
<para>Gli script che seguono illustrano questo meccanismo:</para>
<informaltable frame="none">
<tgroup cols="2">
<colspec colwidth="3.3in"/>
<tbody>
<row>
<entry>per la <emphasis>shell Bourne</emphasis>:</entry>
<entry>e per la <emphasis>shell C:</emphasis></entry>
</row>
</tbody>
</tgroup>
</informaltable>
<programlisting>#!/bin/sh #!/bin/csh -f
fa=fa set fa = fa
non="" set non = ""
cat << EOF cat << EOF
Questo here document Questo here document
$non $fa $non $fa
sostituzione di variabile sostituzione di variabile
EOF EOF
cat << \EOF cat << \EOF
Questo here document Questo here document
$non $fa $non $fa
sostituzione di variabile sostituzione di variabile
EOF \EOF</programlisting>
<para>Entrambi gli output producono:</para>
<screen>Questo here document
fa
sostituzione di variabile
Questo here document
$non $fa
sostituzione di variabile</screen>
<para>Nella parte superiore dell'esempio le variabili di shell
<literal>$non</literal> e <literal>$fa</literal> sono sostituite.
Nella parte inferiore queste variabili vengono trattate come delle
semplici stringhe di testo senza effettuare la sostituzione.</para>
</sect1>
<sect1 id="shell-programming-int-input">
<title>Input interattivo</title>
<para>Gli script di shell possono accettare input interattivo per
inizializzare parametri all'interno dello script stesso.</para>
<sect2 id="shell-programming-int-input-sh">
<title>Sh</title>
<para>&man.sh.1; utilizza il comando built-in <command>read</command> per
leggere una linea di input, esempio:</para>
<para><command>read param</command></para>
<para>Questo può essere illustrato con un semplice script:</para>
<programlisting>#!/bin/sh
echo "Inserisci una frase \c" # /bin/echo che richiede "\c" per
# impedire un newline
read param
echo param=$param</programlisting>
<para>Quando questo script viene eseguito, viene richiesto l'input, che
viene poi mostrato nel risultato:</para>
<screen>$ <userinput>./read.sh</userinput>
<prompt>Inserisci una frase</prompt> <userinput>hello frank</userinput> # E' stato digitato hello frank <return>
param=hello frank</screen>
</sect2>
<sect2 id="shell-programming-int-input-csh">
<title>Csh</title>
<para>&man.csh.1; usa il simbolo <literal>$<</literal> per leggere una
linea da &man.stdin.4;, esempio:</para>
<para><command>set param = $<</command></para>
<para>Gli spazi bianchi intorno al segno di uguale sono importanti.
Il seguente script illustra come usarlo:</para>
<programlisting>#!/bin/csh -f
echo -n "Inserisci una frase" # Questo echo built-in richiede -n
# per impedire un newline
set param = $<
echo param=$param</programlisting>
<para>Quindi chiede l'input e lo mostra nel risultato:</para>
<screen>&prompt.user; <userinput>./read.csh</userinput>
<prompt>Inserisci una frase</prompt> <userinput>hello frank</userinput> # E' stato digitato hello frank <return>
param=hello frank</screen>
</sect2>
</sect1>
<sect1 id="shell-programming-functions">
<title>Funzioni</title>
<para>La shell Bourne permette di definire funzioni. Queste sono molto
simili agli alias della shell C, ma permettono più
flessibilità. Una funzione ha la forma:</para>
<para><command><replaceable>funzione</replaceable> () {
<replaceable>comando</replaceable>; }</command></para>
<para>dove lo spazio dopo { e il punto e virgola (;) sono obbligatori;
il punto e virgola può essere omesso facendo precedere a } un
newline. Spazi e newline aggiuntivi sono permessi.
Alcuni esempi di funzioni possono essere visti nel semplice file
<filename>.profile</filename> discusso nei primi capitoli, dove si avevano
delle funzioni per <command>ls</command> e <command>ll</command>:</para>
<para><command>ls() { /bin/ls -sbF "$@";}</command></para>
<para><command>ll() { ls -al "$@";}</command></para>
<para>La prima funzione ridefinisce &man.ls.1; affinchè le opzioni
<option>-sbF</option> siano sempre fornite dal comando standard
<command>/bin/ls</command> e in modo da agire in base all'input fornito,
<literal>$@</literal>. La seconda di queste funzioni prende il valore
corrente di <command>ls</command> (la funzione precedente) e aggiunge le
opzioni <option>-al</option>.</para>
<para>Le funzioni sono molto utili negli script di shell. Il seguente
script è una versione semplificata di uno script utilizzato per
effettuare automaticamente il backup su nastro delle partizioni di
sistema.</para>
<programlisting>#!/bin/sh
# Script cron per un completo backup del sistema
HOST=`/bin/uname -n`
admin=frank
Mt=/bin/mt
Dump=/usr/sbin/ufsdump
Mail=/bin/mailx
device=/dev/rmt/0n
Rewind="$Mt -f $device rewind"
Offline="$Mt -f $device rewoffl"
# Failure - exit
failure () {
$Mail -s "Backup fallito - $HOST" $admin << EOF_failure
$HOST
Script cron backup fallito. A quanto pare non c'è il nastro nel dispositivo.
EOF_failure
exit 1
}
# Dump failure - exit
dumpfail () {
$Mail -s "Backup fallito - $HOST" $admin << EOF_dumpfail
$HOST
Script cron backup fallito. A quanto pare non c'è il nastro nel dispositivo.
EOF_dumpfail
exit 1
}
# Success
success () {
$Mail -s "Backup completato con successo - $HOST" $admin << EOF_success
$HOST
Script cron backup apparentemente riuscito. Il file /etc/dumpdates è:
`/bin/cat /etc/dumpdates`
EOF_success
}
# Conferma nastro nel device
$Rewind || failure
$Dump 0uf $device / || dumpfail
$Dump 0uf $device /usr || dumpfail
$Dump 0uf $device /home || dumpfail
$Dump 0uf $device /var || dumpfail
($Dump 0uf $device /var/spool/mail || dumpfail) && success
$Offline</programlisting>
<para>Questo script illustra alcuni argomenti che sono stati trattati in
questo documento. Lo script inizia settando i valori di alcuni parametri.
<literal>HOST</literal> viene inizializzato dall'output di un comando,
<literal>admin</literal> è l'amministratore di sistema,
<literal>Mt</literal>, <literal>Dump</literal> e <literal>Mail</literal>
sono nomi di programmi, <literal>device</literal> è il dispositivo
speciale usato per accedere al nastro, <literal>Rewind</literal> e
<literal>Offline</literal> contengono i comandi rispettivamente per
riavvolgere e scaricare il nastro usando il riferimento
<literal>Mt</literal> e le necessarie opzioni. Vengono definite
tre funzioni: <command>failure</command>, <command>dumpfail</command> e
<command>success</command>. Tutte le funzioni in questo script utilizzano
la forma <emphasis>here document</emphasis> per realizzare il contenuto
della funzione stessa. Si introducono ora gli operatori logici
<emphasis>OR</emphasis> (<emphasis>||</emphasis>) e
<emphasis>AND</emphasis> (<emphasis>&&</emphasis>); ciascuno è
posizionato tra una coppia di comandi. Per l'operatore
<emphasis>OR</emphasis>, il secondo comando viene eseguito solamente se
il primo comando non è stato completato con successo. Per
l'operatore <emphasis>AND</emphasis>, il secondo comando viene
eseguito solamente se il primo comando è stato completato con
successo.</para>
<para>Lo scopo principale dello script è realizzare i comandi
<command>Dump</command>, ad esempio copiando i file system specificati.
Prima si tenta di eseguire il riavvolgimento del nastro. Se questo
fallisce, <literal>|| failure</literal>, si esegue la funzione
<command>failure</command> e si esce dal programma. Se invece questo ha
successo si procede con il backup a turno di ogni partizione, ogni volta
verificando che questa operazione sia completamente riuscita
(<literal>|| dumpfail</literal>). Se questa operazione non viene eseguita
completamente con successo si esegue la procedura
<command>dumpfail</command> e si esce. Se l'ultimo backup ha successo si
procede con la funzione <command>success</command> (<literal>(...) &&
success</literal>). In fine si riavvolge il nastro e lo si manda fuori
linea affinchè altri utenti non possano accidentalmente scriverci
sopra.</para>
</sect1>
<sect1 id="shell-programming-control-comm">
<title>Comandi di controllo</title>
<sect2 id="shell-programming-control-comm-if">
<title>Condizionale if</title>
<para>L'espressione condizionale <command>if</command> è
disponibile in entrambe le shell, ma con una diversa sintassi.</para>
<sect3 id="shell-programming-control-comm-if-sh">
<title>Sh</title>
<programlisting><command>if</command> <replaceable>condizione1</replaceable>
<command>then</command>
lista di comandi se <replaceable>condizione1</replaceable> è vera (true)
[<command>elif</command> <replaceable>condizione2</replaceable>
<command>then</command> lista di comandi se <replaceable>condizione2</replaceable> è vera (true)]
[<command>else</command>
lista di comandi se <replaceable>condizione1</replaceable> è falsa (false)]
<command>fi</command></programlisting>
<para>Le condizioni sono sottoposte usualmente al comando &man.test.1; o
<command>[]</command> (Vedere la <link
linkend="shell-programming-control-comm-test">sezione 9.9.6</link>).
L'<command>if</command> e <command>then</command> devono essere
separati con un newline o un punto e virgola (;).</para>
<programlisting>#!/bin/sh
if [ $# -ge 2 ]
then
echo $2
elif [ $# -eq 1 ]; then
echo $1
else
echo Nessun input
fi</programlisting>
<para>Sono richiesti degli spazi nel formato della condizione di
&man.test.1;, uno dopo <command>[</command> e uno prima di
<command>]</command>. Questo script potrebbe comportarsi in modo
differente a seconda che ci siano zero, uno o più argomenti su
linea di comando. Iniziando con nessun argomento:</para>
<screen>$ <userinput>./if.sh</userinput>
Nessun input</screen>
<para>Ora con un argomento:</para>
<screen>$ <userinput>./if.sh one</userinput>
one</screen>
<para>E ora con due argomenti:</para>
<screen>$ <userinput>./if.sh one two</userinput>
two</screen>
</sect3>
<sect3 id="shell-programming-control-comm-if-csh">
<title>Csh</title>
<programlisting><command>if</command> (<replaceable>condizione</replaceable>) <command><replaceable>comando</replaceable></command>
-oppure-
<command>if</command> (<replaceable>condizione1</replaceable>) <command>then</command>
lista di comandi se <replaceable>condizione1</replaceable> è vera (true)
[<command>else</command> <command>if</command> (<replaceable>condizione2</replaceable>) <command>then</command>
lista di comandi se <replaceable>condizione2</replaceable> è vera (true)]
[<command>else</command>
lista di comandi se <replaceable>condizione1</replaceable> è falsa (false)]
<command>endif</command></programlisting>
<para>L'<command>if</command> e <command>then</command> devono stare
sulla stessa linea.</para>
<programlisting>#!/bin/csh -f
if ( $#argv >= 2 ) then
echo $2
else if ( $#argv == 1 ) then
echo $1
else
echo Nessun input
endif</programlisting>
<para>Di nuovo, questo script potrebbe comportarsi in modo differente a
seconda che ci siano zero, uno o più argomenti su linea di
comando. Iniziando con nessun argomento:</para>
<screen>&prompt.user; <userinput>./if.csh</userinput>
Nessun input</screen>
<para>Ora con un argomento:</para>
<screen>&prompt.user; <userinput>./if.csh one</userinput>
one</screen>
<para>E ora con due argomenti:</para>
<screen>&prompt.user; <userinput>./if.csh one two</userinput>
two</screen>
</sect3>
</sect2>
<sect2 id="shell-programming-control-comm-switch-case">
<title>Condizionale switch e case</title>
<para>Per scegliere tra una serie di valori di tipo stringa relativi a un
parametro si usa <command>case</command> nella shell Bourne e
<command>switch</command> nella shell C.</para>
<sect3 id="shell-programming-control-comm-switch-case-sh">
<title>Sh</title>
<programlisting><command>case</command> <replaceable>parametro</replaceable> <command>in</command>
<replaceable>schema1</replaceable>[|<replaceable>schema1a</replaceable>]) <replaceable>lista1 di comandi</replaceable>;;
<replaceable>schema2</replaceable>) <replaceable>lista2 di comandi</replaceable>
<replaceable>lista2a di comandi</replaceable>;;
<replaceable>schema3</replaceable>) <replaceable>lista3 di comandi</replaceable>;;
*) ;;
<command>esac</command></programlisting>
<para>Si possono usare validi nomi di file meta-caratteri all'interno
dello schema per il confronto. I ;; concludono ogni scelta e
possono essere sulla stessa linea della scelta o a seguito di un
newline, dopo l'ultimo comando per la scelta. Schemi alternativi
per la scelta di un particolare caso sono separati da una barra
verticale |, come nel primo schema dell'esempio precedente. I simboli
wild card ,? per indicare un generico carattere e * per far
corrispondere alcuni caratteri, possono essere usati singolarmente o
in modo adiacente per completare stringhe.</para>
<para>Questo semplice esempio illustra come usare l'espressione
condizionale <command>case</command>.</para>
<programlisting>#!/bin/sh
case $1 in
aa|ab) echo A
;;
b?) echo "B \c"
echo $1;;
c*) echo C;;
*) echo D;;
esac</programlisting>
<para>Quindi quando si esegue lo script con l'argomento posto sulla
colonna di sinistra, lo script risponde come sulla colonna di
destra:</para>
<informaltable frame="none">
<tgroup cols="2">
<colspec colwidth="0.5in"/>
<tbody>
<row>
<entry>aa</entry>
<entry>A</entry>
</row>
<row>
<entry>ab</entry>
<entry>A</entry>
</row>
<row>
<entry>ac</entry>
<entry>D</entry>
</row>
<row>
<entry>bb</entry>
<entry>B bb</entry>
</row>
<row>
<entry>bbb</entry>
<entry>D</entry>
</row>
<row>
<entry>c</entry>
<entry>C</entry>
</row>
<row>
<entry>cc</entry>
<entry>C</entry>
</row>
<row>
<entry>fff</entry>
<entry>D</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</sect3>
<sect3 id="shell-programming-control-comm-switch-case-csh">
<title>Csh</title>
<programlisting><command>switch</command> (<replaceable>parametro</replaceable>)
<command>case</command> <replaceable>schema1</replaceable>:
<replaceable>lista1 di comandi</replaceable>
[<command>breaksw</command>]
<command>case</command> <replaceable>schema2</replaceable>:
<replaceable>lista2 di comandi</replaceable>
[<command>breaksw</command>]
<command>default</command>:
lista di comandi per il comportamento di default
[<command>breaksw</command>]
<command>endsw</command></programlisting>
<para><command>breaksw</command> è opzionale e può essere
usato per interrompere lo switch dopo che si è verificata una
corrispondenza del valore di tipo stringa del parametro confrontato.
<command>Switch</command> non accetta | nella lista degli schemi, ma
permette di unire insieme diverse strutture <command>case</command>
per fornire un simile risultato. Il seguente script di shell C ha lo
stesso comportamento dell'esempio precedente, riferito al
<command>case</command> della shell Bourne.</para>
<programlisting>#!/bin/csh -f
switch ($1)
case aa:
case ab:
echo A
breaksw
case b?:
echo -n "B "
echo $1
breaksw
case c*:
echo C
breaksw
default:
echo D
endsw</programlisting>
</sect3>
</sect2>
<sect2 id="shell-programming-control-comm-for-foreach">
<title>for e foreach</title>
<para>Per effettuare un ciclo tra una lista di valori di tipo
stringa si possono usare i comandi <command>for</command> e
<command>foreach</command>.</para>
<sect3 id="shell-programming-control-comm-for-foreach-sh">
<title>Sh</title>
<programlisting><command>for</command> <replaceable>variabile</replaceable> [<command>in</command> <replaceable>lista_di_valori</replaceable>]
<command>do</command>
<replaceable>lista di comandi</replaceable>
<command>done</command></programlisting>
<para>La <replaceable>lista_di_valori</replaceable> è opzionale,
presupponendo <literal>$@</literal> se nulla viene specificato. Ogni
valore in questa lista viene sostituito sequenzialmente in
<replaceable>variabile</replaceable> fino a quando la lista risulta
vuota. Possono essere usati wild card, che vengono applicati ai
nomi dei file nella directory corrente. Di seguito si illustra il
ciclo <command>for</command> che copia tutti i file che finiscono con
<filename>.old</filename> negli stessi nomi che finiscono però
con <filename>.new</filename>. In questi esempi l'utility
&man.basename.1; estrae la parte base del nome affinchè
si possa modificarne l'estensione.</para>
<programlisting>#!/bin/sh
for file in *.old
do
newf=`basename $file .old`
cp $file $newf.new
done</programlisting>
</sect3>
<sect3 id="shell-programming-control-comm-for-foreach-csh">
<title>Csh</title>
<programlisting><command>foreach</command> <replaceable>variabile</replaceable> (<replaceable>lista_di_valori</replaceable>)
<replaceable>lista di comandi</replaceable>
<command>end</command></programlisting>
<para>L'equivalente script in shell C per copiare tutti i file con
estensione <filename>.old</filename> negli stessi file con estensione
<filename>.new</filename> è:</para>
<programlisting>#!/bin/csh -f
foreach file (*.old)
set newf = `basename $file .old`
cp $file $newf.new
end</programlisting>
</sect3>
</sect2>
<sect2 id="shell-programming-control-comm-while">
<title>while</title>
<para>Il comando <command>while</command> permette di effettuare il ciclo
sempre che la condizione sia vera.</para>
<sect3 id="shell-programming-control-comm-while-sh">
<title>Sh</title>
<programlisting><command>while</command> <replaceable>condizione</replaceable>
<command>do</command>
<replaceable>lista di comandi</replaceable>
[<command>break</command>]
[<command>continue</command>]
<command>done</command></programlisting>
<para>Un semplice script per illustrare il ciclo
<command>while</command> è:</para>
<programlisting>#!/bin/sh
while [ $# -gt 0 ]
do
echo $1
shift
done</programlisting>
<para>Questo script prende la lista degli argomenti, ne visualizza il
primo, quindi effettua uno shift nella lista verso sinistra, perdendo
il primo elemento originale. Il ciclo viene ripetuto fino a quando
tutti gli argomenti sono stati spostati fuori dalla lista.</para>
<screen>$ <userinput>./while.sh one two three</userinput>
one
two
three</screen>
</sect3>
<sect3 id="shell-programming-control-comm-while-csh">
<title>Csh</title>
<programlisting><command>while</command> (<replaceable>condizione</replaceable>)
<replaceable>lista di comandi</replaceable>
[<command>break</command>]
[<command>continue</command>]
<command>end</command></programlisting>
<para>Se si vuole che la condizione sia sempre vera si specifica 1
all'interno del test condizionale.</para>
<para>Lo script di shell C equivalente a quello precedente
è:</para>
<programlisting>#!/bin/csh -f
while ($#argv != 0 )
echo $argv[1]
shift
end</programlisting>
</sect3>
</sect2>
<sect2 id="shell-programming-control-comm-until">
<title>until</title>
<para>Questo costrutto di ciclo è solamente disponibile per la
shell Bourne.</para>
<programlisting><command>until</command> <replaceable>condizione</replaceable>
<command>do</command>
lista di comandi se la condizione è falsa
<command>done</command></programlisting>
<para>La condizione viene verificata all'inizio di ogni ciclo e il ciclo
termina quando la condizione è vera.</para>
<para>Uno script equivalente all'esempio del <command>while</command>
precedente è:</para>
<programlisting>#!/bin/sh
until [ $# -le 0 ]
do
echo $1
shift
done</programlisting>
<para>Si noti che qui si verifica per <emphasis>minore o
uguale</emphasis>, piuttosto che per <emphasis>maggiore</emphasis>,
poichè il ciclo <command>until</command> viene abilitato da una
condizione <emphasis>falsa</emphasis>.</para>
<para>Sia il ciclo <command>until</command> che il
<command>while</command> sono solamente eseguiti se la condizione
è soddisfatta. La condizione viene valutata prima
dell'esecuzione dei comandi.</para>
</sect2>
<sect2 id="shell-programming-control-comm-test">
<title>test</title>
<para>Le espressioni condizionali vengono valutate per valori
<emphasis>veri</emphasis> o <emphasis>falsi</emphasis>. Questo, di
solito, viene realizzato con &man.test.1; o equivalentemente con i suoi
operatori <command>[]</command>. Se la condizione viene valutata vera,
viene settato uno stato di uscita zero (<emphasis>TRUE</emphasis>),
altrimenti viene settato uno stato di uscita non-zero
(<emphasis>FALSE</emphasis>). Se non ci sono argomenti viene settato
uno stato di uscita non-zero. Gli operatori utilizzati nelle
espressioni condizionali della shell Bourne sono mostrati qui
sotto.</para>
<para>Per i <emphasis>nomi di file</emphasis> le opzioni per
&man.test.1; sono date con la sintassi seguente:</para>
<para><option>-<replaceable>opzione</replaceable></option>
<replaceable>filename</replaceable></para>
<para>Le opzioni di &man.test.1; disponibili per i
<emphasis>file</emphasis> includono:</para>
<informaltable frame="none">
<tgroup cols="2">
<colspec colwidth="1in"/>
<tbody>
<row>
<entry><option>-r</option></entry>
<entry>vero se il file esiste ed è leggibile</entry>
</row>
<row>
<entry><option>-w</option></entry>
<entry>vero se il file esiste ed è scrivibile</entry>
</row>
<row>
<entry><option>-x</option></entry>
<entry>vero se il file esiste ed è eseguibile</entry>
</row>
<row>
<entry><option>-f</option></entry>
<entry>vero se il file esiste ed è un file regolare (o per
&man.csh.1; esiste e non è una directory)</entry>
</row>
<row>
<entry><option>-d</option></entry>
<entry>vero se il file esiste ed è una directory</entry>
</row>
<row>
<entry><option>-h</option> o <option>-L</option></entry>
<entry>vero se il file esiste ed è un link
simbolico</entry>
</row>
<row>
<entry><option>-c</option></entry>
<entry>vero se il file esiste ed è un file speciale a
caratteri (ad esempio un dispositivo al quale si accede un
carattere alla volta)</entry>
</row>
<row>
<entry><option>-b</option></entry>
<entry>vero se il file esiste ed è un file speciale a
blocchi (ad esempio un dispositivo al quale si accede in blocchi
di dati)</entry>
</row>
<row>
<entry><option>-p</option></entry>
<entry>vero se il file esiste ed è un file pipe
(fifo)</entry>
</row>
<row>
<entry><option>-u</option></entry>
<entry>vero se il file esiste ed è setuid (ad esempio ha il
bit set-user-id settato a s o S nel terzo bit)</entry>
</row>
<row>
<entry><option>-g</option></entry>
<entry>vero se il file esiste ed è setgid (ad esempio ha il
bit set-group-id settato a s o S nel sesto bit)</entry>
</row>
<row>
<entry><option>-k</option></entry>
<entry>vero se il file esiste e ha lo sticky bit settato (una t
nel nono bit)</entry>
</row>
<row>
<entry><option>-s</option></entry>
<entry>vero se il file esiste ed ha una dimensione maggiore di
zero</entry>
</row>
</tbody>
</tgroup>
</informaltable>
<para>C'è un test per i <emphasis>descrittori di
file</emphasis>:</para>
<informaltable frame="none">
<tgroup cols="2">
<colspec colwidth="2in"/>
<tbody>
<row>
<entry><option>-t
[<replaceable>descrittore_file</replaceable>]</option></entry>
<entry>vero se l'aperto descrittore del file specificato (1,
&man.stdout.4;, di default) è associato ad un
terminale</entry>
</row>
</tbody>
</tgroup>
</informaltable>
<para>Ci sono test per le <emphasis>stringhe</emphasis>:</para>
<informaltable frame="none">
<tgroup cols="2">
<colspec colwidth="2in"/>
<tbody>
<row>
<entry><option>-z
<replaceable>stringa</replaceable></option></entry>
<entry>vero se la lunghezza della stringa è zero</entry>
</row>
<row>
<entry><option>-n
<replaceable>stringa</replaceable></option></entry>
<entry>vero se la lunghezza della stringa non è
zero</entry>
</row>
<row>
<entry><option><replaceable>stringa1</replaceable> =
<replaceable>stringa2</replaceable></option></entry>
<entry>vero se <replaceable>stringa1</replaceable> è
identica a <replaceable>stringa2</replaceable></entry>
</row>
<row>
<entry><option><replaceable>stringa1</replaceable> !=
<replaceable>stringa2</replaceable></option></entry>
<entry>vero se <replaceable>stringa1</replaceable> non è
identica a <replaceable>stringa2</replaceable></entry>
</row>
<row>
<entry><replaceable>stringa</replaceable></entry>
<entry>vero se la stringa non è nulla</entry>
</row>
</tbody>
</tgroup>
</informaltable>
<para>Ci sono dei confronti per gli interi:</para>
<informaltable frame="none">
<tgroup cols="2">
<colspec colwidth="1.5in"/>
<tbody>
<row>
<entry><option><replaceable>n1</replaceable> -eq
<replaceable>n2</replaceable></option></entry>
<entry>vero se gli interi <replaceable>n1</replaceable> e
<replaceable>n2</replaceable> sono uguali</entry>
</row>
<row>
<entry><option><replaceable>n1</replaceable> -ne
<replaceable>n2</replaceable></option></entry>
<entry>vero se gli interi <replaceable>n1</replaceable> e
<replaceable>n2</replaceable> non sono uguali</entry>
</row>
<row>
<entry><option><replaceable>n1</replaceable> -gt
<replaceable>n2</replaceable></option></entry>
<entry>vero se l'intero <replaceable>n1</replaceable> è
maggiore dell'intero <replaceable>n2</replaceable></entry>
</row>
<row>
<entry><option><replaceable>n1</replaceable> -ge
<replaceable>n2</replaceable></option></entry>
<entry>vero se l'intero <replaceable>n1</replaceable> è
maggiore o uguale dell'intero
<replaceable>n2</replaceable></entry>
</row>
<row>
<entry><option><replaceable>n1</replaceable> -lt
<replaceable>n2</replaceable></option></entry>
<entry>vero se l'intero <replaceable>n1</replaceable> è
minore dell'intero <replaceable>n2</replaceable></entry>
</row>
<row>
<entry><option><replaceable>n1</replaceable> -le
<replaceable>n2</replaceable></option></entry>
<entry>vero se l'intero <replaceable>n1</replaceable> è
minore o uguale dell'intero
<replaceable>n2</replaceable></entry>
</row>
</tbody>
</tgroup>
</informaltable>
<para>Sono disponibili i seguenti <emphasis>operatori
logici</emphasis>:</para>
<informaltable frame="none">
<tgroup cols="2">
<colspec colwidth="0.5in"/>
<tbody>
<row>
<entry><option>!</option></entry>
<entry>negazione (unaria)</entry>
</row>
<row>
<entry><option>-a</option></entry>
<entry>and (binario)</entry>
</row>
<row>
<entry><option>-o</option></entry>
<entry>or (binario)</entry>
</row>
<row>
<entry><option>()</option></entry>
<entry>le espressioni all'interno di ( ) vengono raggruppate
insieme. Può essere necessario quotare le parentesi ( )
per impedire alla shell di interpretarle.</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</sect2>
<sect2 id="shell-programming-control-comm-csh-log-rel-op">
<title>Operatori relazionali e logici della shell C</title>
<para>La shell C possiede un suo set di operatori logici e relazionali
built-in. In ordine decrescente di priorità questi sono:</para>
<informaltable frame="none">
<tgroup cols="2">
<colspec colwidth="1.5in"/>
<tbody>
<row>
<entry><command>(...)</command></entry>
<entry>raggruppa espressioni con ( )</entry>
</row>
<row>
<entry><command>~</command></entry>
<entry>inverso (il suo complemento)</entry>
</row>
<row>
<entry><command>!</command></entry>
<entry>negazione logica</entry>
</row>
<row>
<entry><command>*, /, %</command></entry>
<entry>moltiplicazione, divisione, modulo</entry>
</row>
<row>
<entry><command>+, -</command></entry>
<entry>addizione, sottrazione</entry>
</row>
<row>
<entry><command><<, >></command></entry>
<entry>shift a sinistra di bit, shift a destra di bit</entry>
</row>
<row>
<entry><command><=</command></entry>
<entry>minore o uguale</entry>
</row>
<row>
<entry><command>>=</command></entry>
<entry>maggiore o uguale</entry>
</row>
<row>
<entry><command><</command></entry>
<entry>minore</entry>
</row>
<row>
<entry><command>></command></entry>
<entry>maggiore</entry>
</row>
<row>
<entry><command>= =</command></entry>
<entry>uguale</entry>
</row>
<row>
<entry><command>!=</command></entry>
<entry>non uguale</entry>
</row>
<row>
<entry><command>=~</command></entry>
<entry>uguale a stringa</entry>
</row>
<row>
<entry><command>!~</command></entry>
<entry>non uguale a stringa</entry>
</row>
<row>
<entry><command>&</command></entry>
<entry>AND bit</entry>
</row>
<row>
<entry><command>^</command></entry>
<entry>XOR bit (or esclusivo)</entry>
</row>
<row>
<entry><command>|</command></entry>
<entry>OR bit</entry>
</row>
<row>
<entry><command>&&</command></entry>
<entry>AND logico</entry>
</row>
<row>
<entry><command>||</command></entry>
<entry>OR logico</entry>
</row>
<row>
<entry><command>{<replaceable>comando</replaceable>}</command></entry>
<entry>vero (1) se il comando termina con uno stato di uscita 0,
falso (0) altrimenti.</entry>
</row>
</tbody>
</tgroup>
</informaltable>
<para>Inoltre la shell C permette richieste sul tipo e sui permessi dei
file con gli operatori seguenti:</para>
<informaltable frame="none">
<tgroup cols="2">
<colspec colwidth="1in"/>
<tbody>
<row>
<entry><option>-r</option></entry>
<entry>ritorna vero (1) se il file esiste ed è leggibile,
altrimenti ritorna falso (0)</entry>
</row>
<row>
<entry><option>-w</option></entry>
<entry>vero se il file esiste ed è scrivibile</entry>
</row>
<row>
<entry><option>-x</option></entry>
<entry>vero se il file esiste ed è eseguibile</entry>
</row>
<row>
<entry><option>-f</option></entry>
<entry>vero se il file esiste e non è una directory</entry>
</row>
<row>
<entry><option>-d</option></entry>
<entry>vero se il file esiste ed è una directory</entry>
</row>
<row>
<entry><option>-e</option></entry>
<entry>vero se il file esiste</entry>
</row>
<row>
<entry><option>-o</option></entry>
<entry>vero se l'utente corrente è il proprietario del
file</entry>
</row>
<row>
<entry><option>-z</option></entry>
<entry>vero se il file ha una lunghezza zero (file vuoto)</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</sect2>
</sect1>
</chapter>