Bash scripting - istruzioni composte: differenze tra le versioni

m
correzione
mNessun oggetto della modifica
m (correzione)
 
(15 versioni intermedie di 2 utenti non mostrate)
Riga 1: Riga 1:
{{Bash scripting}}
{{Bash scripting}}
=Istruzioni composte=
__TOC__
Sono istruzioni composte tutte le istruzioni che possono contenere al loro interno altre istruzioni. Per esempio <code>if</code> e <code>for</code> sono due istruzioni composte:
Sono istruzioni composte tutte le istruzioni che possono contenere al loro interno altre istruzioni. Tipicamente sono introdotte e terminate da parole chiave (''keywords''), in quanto definiscono regole speciali al loro interno, spesso facendo uso anche di parole riservate che agiscono come istruzioni.
 
Per esempio <code>if</code> e <code>for</code> sono due istruzioni composte (e due ''keywords''):
* <code>if</code> è già stata trattata assieme all'uso di <code>[ ... ]</code> nei [[Bash scripting - comandi essenziali#Condizioni | comandi essenziali]], utile anche per le condizioni del ciclo <code>while</code>;
* <code>if</code> è già stata trattata assieme all'uso di <code>[ ... ]</code> nei [[Bash scripting - comandi essenziali#Condizioni | comandi essenziali]], utile anche per le condizioni del ciclo <code>while</code>;
* <code>for</code> è già stato trattato nell'[[Bash scripting - variabili - stringhe#Assegnazione con ciclo | assegnazione con ciclo]] nella sezione delle Variabili, con la sintassi tipica di ''for each'' di alcuni linguaggi di programmazione e l'unica prevista da ''POSIX''.
* <code>for</code> è già stato trattato nell'[[Bash scripting - variabili - stringhe#Assegnazione con ciclo | assegnazione con ciclo]], con la sintassi tipica di ''for each'' di alcuni linguaggi di programmazione e l'unica prevista da [[POSIX]].


==Cicli==
== Cicli ==
Con <code>while</code> è possibile eseguire un blocco di comandi se un comando ha successo e ripeterne l'esecuzione fintanto che il comando continua ad avere successo. Proprio come <code>if</code>, <code>while</code> è un'istruzione accompagnata spesso da <code>[...]</code>, utilizzato come comando di cui valutare l'exit status.
Con <code>while</code> è possibile eseguire un blocco di comandi se un comando ha successo e ripeterne l'esecuzione fintanto che il comando continua ad avere successo. Proprio come <code>if</code>, <code>while</code> è un'istruzione accompagnata spesso da <code>[...]</code>, utilizzato come comando di cui valutare l'exit status.


Riga 24: Riga 26:
   printf %s "Inserisci intero > 0 e premi INVIO: "
   printf %s "Inserisci intero > 0 e premi INVIO: "
   # assegna l'input dell'utente a $num
   # assegna l'input dell'utente a $num
   read num
   read -r num
done
done
printf %s\\n "Hai inserito: ${num}"
printf %s\\n "Hai inserito: ${num}"
Riga 32: Riga 34:
<pre>
<pre>
i=1
i=1
while [ $i -le $n ]
while [ "$i" -le "$n" ]
do
do
     ...
     ...
Riga 54: Riga 56:
<pre>
<pre>
i=1
i=1
while [ $i -le $n ]
while [ "$i" -le "$n" ]
do
do
     if [ espressione-booleana ]; then
     if [ espressione-booleana ]; then
Riga 74: Riga 76:
E non è una svista la mancanza di '''$''' davanti al nome delle variabili all'interno di <code>((...))</code>. Tale sintassi è supportata anche nell'espansione aritmetica intera, ma non è prevista da ''POSIX'', e quindi non è mai stata usata in questa guida.
E non è una svista la mancanza di '''$''' davanti al nome delle variabili all'interno di <code>((...))</code>. Tale sintassi è supportata anche nell'espansione aritmetica intera, ma non è prevista da ''POSIX'', e quindi non è mai stata usata in questa guida.


==Composizione di comandi con pipe==
== Composizione di comandi con pipe ==
Una ''pipe'' collega due comandi, trattando l'output generato dal primo come input per il secondo, in modo da svolgere funzioni più complesse tramite la composizione comandi che si occupano di funzioni più limitate. Ed è una delle caratteristiche di ''Unix'' che ogni comando debba svolgere un'unica funzione nel modo migliore possibile, permettendo la comunicazione con l'utente o un altro comando attraverso un flusso di testo, considerato un'interfaccia universale.
Una ''pipe'' collega due comandi, trattando l'output generato dal primo come input per il secondo, in modo da svolgere funzioni più complesse tramite la composizione di comandi che si occupano di funzioni più limitate. Ed è una delle caratteristiche di ''Unix'' che ogni comando debba svolgere un'unica funzione nel modo migliore possibile, permettendo la comunicazione con l'utente o un altro comando attraverso un flusso di testo, considerato un'interfaccia universale.


L'exit status di una serie di pipe è dato dall'ultimo comando. L'exit status può essere invertito come per i comandi semplici mettendo <code>!</code> davanti al primo comando della serie.
L'exit status di una serie di pipe è dato dall'ultimo comando. L'exit status può essere invertito come per i comandi semplici mettendo <code>!</code> davanti al primo comando della serie.
Riga 97: Riga 99:
<pre>
<pre>
# filtra le occorrenze di video nel ring buffer del kernel
# filtra le occorrenze di video nel ring buffer del kernel
dmesg | grep -i video   # -i per ignore-case (ignora maiuscole/minuscole)
dmesg | grep -i video   # -i per ignore-case (ignora maiuscole/minuscole)


# stampa il gateway utilizzato
# stampa il gateway utilizzato
Riga 105: Riga 107:
</pre>
</pre>


===Output dei comandi e carattere ASCII n. 0===
=== Output dei comandi e carattere ASCII n. 0 ===
Una variabile non può contenere il carattere ASCII n. 0, che è usato per indicare la fine della stringa. Non potendo gestire direttamente il carattere ASCII n. 0, questo non può essere presente nell'espansione di un comando, ma dev'essere riservato ai soli comandi mediante l'uso di una o più pipe.
Una variabile non può contenere il carattere ASCII n. 0, che è usato per indicare la fine della stringa. Non potendo gestire direttamente il carattere ASCII n. 0, questo non può essere presente nell'espansione di un comando, ma dev'essere riservato ai soli comandi mediante l'uso di una o più pipe.


Riga 116: Riga 118:
Si noti che mentre è rimosso dalle espansioni, nelle assegnazioni normali viene interpretato come carattere di fine stringa, con il risultato che anche tutto ciò che segue viene tralasciato.
Si noti che mentre è rimosso dalle espansioni, nelle assegnazioni normali viene interpretato come carattere di fine stringa, con il risultato che anche tutto ciò che segue viene tralasciato.


====Nomi di file con caratteri non comuni====
Una possibilità è memorizzare l'output in una codifica alternativa, per esempio base64, esadecimale o ottale, richiamando un opportuno comando esterno, così da utilizzare soltanto caratteri ammissibili:
<pre>
# comando con output arbitrario
output_base64=$(comando | base64) # trasformo in base64 e assegno alla variabile
# e lo posso riaccedere riusando base64
printf %s\\n "$output_base64" |  # lo invio a base64 per ritrasformarlo
    base64 -d |                  # decodifico nel formato originale
    altro_comando                # lo invio come input a un altro comando
</pre>
È ovviamente un metodo molto inefficiente, ma l'unico permesso per memorizzare in una variabile. In alcuni casi potrebbe essere preferibile una codifica esadecimale o ottale, ancora più inefficiente quanto a spazio occupato, ma più semplici da manipolare dalla shell.
 
==== Nomi di file con caratteri non comuni ====
Questo carattere è utile perché nemmeno i file possono averlo nel proprio nome, mentre invece permettono caratteri jolly (*, ?, ...), come già visto con le espansioni di percorso, e potrebbero contenere perfino il carattere "a capo".
Questo carattere è utile perché nemmeno i file possono averlo nel proprio nome, mentre invece permettono caratteri jolly (*, ?, ...), come già visto con le espansioni di percorso, e potrebbero contenere perfino il carattere "a capo".


L'espansione di percorso funziona normalmente, anche in presenza di "a capo", ma potrebbero sorgere problemi sfruttando l'espansione di comando. Per esempio con il comando esterno <code>find</code>, utilizzato per effettuare ricerche in modo ricorsivo, e che di default restituisce i file trovati stampandoli uno per riga, assumendo implicitamente che non contengano il carattere "a capo".
L'espansione di percorso funziona normalmente, anche in presenza di "a capo", ma potrebbero sorgere problemi sfruttando l'espansione di comando. Per esempio con il comando esterno <code>find</code>, utilizzato per effettuare ricerche in modo ricorsivo, che di default restituisce i file trovati stampandoli uno per riga, assumendo implicitamente che non contengano il carattere "a capo".


Uno script a titolo esemplificativo:
Uno script a titolo esemplificativo:
Riga 145: Riga 158:
</pre>
</pre>


====Comando find e carattere ASCII n. 0====
==== Comando find e carattere ASCII n. 0 ====
Un abbinamento comune al comando esterno GNU <code>find</code> (dotato dell'opzione -print0) è il comando esterno GNU <code>xargs</code> (dotato dell'opzione -0):
Un abbinamento comune al comando esterno GNU <code>find</code> (dotato dell'opzione -print0) è il comando esterno GNU <code>xargs</code> (dotato dell'opzione -0):
<pre>
<pre>
Riga 156: Riga 169:
<pre>
<pre>
find /percorso -opzione1 ... -opzioneN -print0 |
find /percorso -opzione1 ... -opzioneN -print0 |
     while read -d $'\000' file
     while read -r -d $'\000' file
     do
     do
         # posso usare "$file"
         # posso usare "$file"
Riga 169: Riga 182:
</pre>
</pre>


==Concatenazione e blocchi di comandi==
== Concatenazione e blocchi di comandi ==
Più comandi, anche contenenti redirezioni, possono essere concatenati per formarne di più complessi. L'operatore di concatenazione può essere anche l'ultimo di una riga, per facilitare la leggibilità del codice.
Più comandi, anche contenenti redirezioni, possono essere concatenati per formarne di più complessi. L'operatore di concatenazione può essere anche l'ultimo di una riga, per facilitare la leggibilità del codice.


Riga 204: Riga 217:
; <code>( ... )</code> : esegue il blocco di comandi in una subshell (le variabili vengono ripristinate al loro valore precedente, alla fine del blocco).
; <code>( ... )</code> : esegue il blocco di comandi in una subshell (le variabili vengono ripristinate al loro valore precedente, alla fine del blocco).


===Catturare l'exit status===
=== Catturare l'exit status ===
Per catturare lo stato d'uscita di un comando appena eseguito è sufficiente espandere la variabile speciale <code>$?</code>, come già visto. Tuttavia in caso di fallimento del comando, il controllo effettuato via <code>$?</code> avverrebbe soltanto '''dopo''' un blocco con errore (si veda la parte introduttiva sul debug).
Per catturare lo stato d'uscita di un comando appena eseguito è sufficiente espandere la variabile speciale <code>$?</code>, come già visto. Tuttavia in caso di fallimento del comando, il controllo effettuato via <code>$?</code> avverrebbe soltanto '''dopo''' un blocco con errore (si veda la parte introduttiva sul [[Bash scripting - introduzione#Debug integrato | debug]]).


Per evitare che un blocco abbia un exit status diverso da zero, si possono usare le concatenazioni <code>&&</code> e <code>||</code> (oppure un <code>if</code>):
Per evitare che un blocco abbia un exit status diverso da zero, si possono usare le concatenazioni <code>&&</code> e <code>||</code> (oppure un <code>if</code>):
Riga 214: Riga 227:
</pre>
</pre>


===Condizioni complesse===
=== Condizioni complesse ===
Anche se '''bash''' dispone di altri metodi per unire condizioni più semplici, è possibile utilizzare la sintassi ''POSIX'', combinando il comando condizionale <code>[...]</code>, le concatenazioni con ''AND'' e ''OR'' logici (<code>&&</code> e <code>||</code>, rispettivamente), e il raggruppamento <code>{ ... ; }</code>. Inoltre gli operatori logici sono più efficienti, in quanto verrebbero eseguiti soltanto i comandi necessari a valutare la condizione, e si possono usare per evitare errori.
Anche se '''bash''' dispone di altri metodi per unire condizioni più semplici, è possibile utilizzare la sintassi [[POSIX]], concatenando più comandi <code>[...]</code> tramite gli operatori logici ''AND'' e ''OR'' logici (<code>&&</code> e <code>||</code>, rispettivamente) e raggruppandoli con <code>{ ... ; }</code>. Inoltre gli operatori logici sono più efficienti, in quanto verrebbero eseguiti soltanto i comandi necessari a valutare la condizione, e si possono usare per evitare errori.


Per esempio:
Per esempio:
Riga 231: Riga 244:
</pre>
</pre>


Verifica che una variabile contenga un intero (maggiore o minore di zero):
Esempi di condizioni complesse sull'esistenza dei file, in genere utilizzate dall'istruzione <code>if</code>:
<pre>
<pre>
if [ "$numero" -ne 0 ] 2> /dev/null || # fallisce solo se $numero è 0 oppuer se non è intero
# Controlla che il file regolare esista e sia leggibile
  [ "$numero" = 0 ]                   # copre il caso in cui $numero è 0
[ -f "$var" ] && [ -r "$var" ]
then
 
    printf %s\\n "Il numero inserito (${numero}) è intero"
# Controlla che il file regolare esista e sia eseguibile
else
# (permesso di lettura + esecuzione)
    printf %s\\n "ERRORE: il numero inserito non (${numero}) è intero!" >&2
[ -f "$var" ] && [ -r "$var" ] && [ -x "$var" ]
fi
 
# Controlla che la directory esista e sia possibile ottenere una lista del suo contenuto
# (per sapere se un file esiste nella directory)
[ -d "$var" ] && [ -r "$var" ]
 
# Controlla che la directory esista e sia possibile accedere al suo contenuto
# (leggendo o scrivendo i file, se i loro permessi lo consentono)
[ -d "$var" ] && [ -x "$var" ]
 
# Controlla che la directory esista e sia possibile ottenere una lista e accedere al suo contenuto
[ -d "$var" ] && [ -r "$var" ] && [ -x "$var" ]
 
# Controlla che la directory esista e sia possibile creare e cancellare dei file noti
# (permesso di accesso + scrittura)
[ -d "$var" ] && [ -x "$var" ] && [ -w "$var" ]
 
# Controlla che la directory esista e sia possibile cancellarne il contenuto (non noto)
# (permesso di lettura, per ottenere una lista del suo contenuto, + permessi del caso precedente)
[ -d "$var" ] && [ -r "$var" ] && [ -x "$var" ] && [ -w "$var" ]
</pre>
</pre>


Condizione più complessa, in cui si controlla che una variabile sia vuota, oppure che contenga un file regolare esistente e con permessi di lettura (<code>[ -r "$file" ]</code>):
Uso di una condizione più complessa, in cui si controlla che una variabile sia vuota, oppure che contenga un file regolare esistente e con permessi di scrittura:
<pre>
<pre>
if [ -z "$file" ] || { [ -f "$file" ] && [ -r "$file" ] ; }; then
if [ -z "$file" ] || { [ -f "$file" ] && [ -w "$file" ] ; }; then
     fai_qualcosa "${file:-/dev/null}" # se $file è vuota, usa /dev/null
     fai_qualcosa "${file:-/dev/null}" # se $file è vuota, usa /dev/null
                                       # (espansione di parametro con valore di default)
                                       # (espansione di parametro con valore di default)
else
else
     printf %s\\n "File \"${file}\" non esistente o non leggibile!" >&2
     printf %s\\n "File \"${file}\" non esistente o non modificabile!" >&2
     exit 1
     exit 1
fi
fi
Riga 255: Riga 286:
Per farne la negazione, se si è interessati al solo ramo <code>else</code>, bisogna aggiungere un altro raggruppamento, visto che <code>!</code> si riferisce a un comando semplice o a una concatenazione con pipe:
Per farne la negazione, se si è interessati al solo ramo <code>else</code>, bisogna aggiungere un altro raggruppamento, visto che <code>!</code> si riferisce a un comando semplice o a una concatenazione con pipe:
<pre>
<pre>
if ! { [ -z "$file" ] || { [ -f "$file" ] && [ -r "$file" ] ; } ; }; then
if ! { [ -z "$file" ] || { [ -f "$file" ] && [ -w "$file" ] ; } ; }; then
     printf %s\\n "File \"${file}\" non esistente o non leggibile!" >&2
     printf %s\\n "File \"${file}\" non esistente o non modificabile!" >&2
     exit 1
     exit 1
fi
fi
</pre>
</pre>
ma è in genere preferibile ripensare la condizione:
ma è in genere preferibile ripensare la condizione:
if [ -n "$file" ] && { ! [ -f "$file" ] || ! [ -r "$file" ] ; }; then
<pre>
     printf %s\\n "File \"${file}\" non esistente o non leggibile!" >&2
if [ -n "$file" ] && { ! [ -f "$file" ] || ! [ -w "$file" ] ; }; then
     printf %s\\n "File \"${file}\" non esistente o non modificabile!" >&2
     exit 1
     exit 1
fi
fi
</pre>


==Condizioni avanzate con pattern matching==
== Condizioni avanzate con pattern matching ==
Anche se si possono costruire condizioni arbitrarie sfruttando le sole istruzioni <code>[...]</code> con concatenazioni, raggruppamenti ed espansioni di parametro, tramite l'istruzione composta <code>case</code> è possibile scrivere confronti complessi molto più facilmente.
Anche se si possono costruire condizioni arbitrarie sfruttando le sole istruzioni <code>[...]</code> con concatenazioni, raggruppamenti ed espansioni di parametro, tramite l'istruzione composta <code>case</code> è possibile scrivere confronti complessi molto più facilmente.


Viene selezioanta per i confronti una stringa, di solito una variabile quotata, e diversi pattern con la stessa sintassi ammessa per l'espansione di percorso (''globbing''), eseguendo soltanto il blocco di comandi relativo alla prima corrispondenza trovata, e ignorando gli altri.
Viene selezionata per i confronti una stringa, di solito una variabile quotata, e diversi pattern con la stessa sintassi ammessa per l'espansione di percorso (''globbing''), eseguendo soltanto il blocco di comandi relativo alla prima corrispondenza trovata, e ignorando gli altri.


La shell '''bash''' ha diverse espansioni rispetto a ''POSIX'', e anche l'uso della parola riservata <code>select</code>, ma questa sezione si limita alla sintassi ''POSIX'', che è anche più comprensibile per chi è abituato ad altri linguaggi di programmazione.
La shell '''bash''' ha diverse espansioni rispetto a ''POSIX'', e anche l'uso della parola riservata <code>select</code>, ma questa sezione si limita alla sintassi ''POSIX'', che è anche più comprensibile per chi è abituato ad altri linguaggi di programmazione.


I pattern devono consistere di un'unica stringa, o più stringhe separate dal carattere <code>|</code>, e a eccezione dei caratteri speciali per il globbing vanno quotati per evitare espansioni accidentali o pattern non voluti.
I pattern devono consistere di un'unica stringa, o più stringhe separate dal carattere <code>|</code> e, a eccezione dei caratteri speciali per il globbing, vanno quotati per evitare espansioni accidentali o pattern non voluti.


Sintassi:
Sintassi:
Riga 306: Riga 339:
Va solo ricordato che nel pattern ci sono cinque caratteri speciali, il separatore di pattern e i quattro del ''globbing'' (<code>| * ? [ ]</code>), che come per l'espansione di percorso non possono essere quotati. È bene invece che tutto il resto sia quotato, se si usano variabili o si intende usare questi caratteri per il loro valore letterale.
Va solo ricordato che nel pattern ci sono cinque caratteri speciali, il separatore di pattern e i quattro del ''globbing'' (<code>| * ? [ ]</code>), che come per l'espansione di percorso non possono essere quotati. È bene invece che tutto il resto sia quotato, se si usano variabili o si intende usare questi caratteri per il loro valore letterale.


È un'istruzione molto usata per implementare un menù e per la lettura (parsing) degli argomenti passati allo script.
'''bash''' permette anche l'uso di condizioni avanzate con le parole chiave (''keywords'') <code>[[</code><code> ... </code><code>]]</code> (non ''POSIX'').<br/>
Supportano la sintassi base del comando interno <code>[ ... ]</code>, quella che è stata qui presentata, ma al loro interno permettono:
* concatenazioni con <code>&&</code> e <code>||</code>;
* l'uso di <code>!</code> per invertire il valore della condizione;
* di cambiare la precedenza degli operatori con <code>(...)</code>;
* pattern matching con <code>==</code> e <code>!=</code> per la stringa a destra dell'operatore tramite i caratteri speciali del globbing (<code>* ? [ ]</code>), al solito se non quotati e non preceduti dal carattere di escape <code>\</code>;
* confronti lessicografici tra stringhe con <code><</code> e <code>></code> (NOTA: non esistono "<=" e ">="). In '''bash''' (non ''POSIX'') esistono anche con <code>[...]</code>, ma con quel comando vanno quotate o precedute dal carattere di escape <code>\</code> (ossia: <code>\<</code> e <code>\></code>) perché sono caratteri speciali e soltanto le parole chiave possono permettere regole speciali al loro interno;
* l'uso di espressioni regolari estese con <code>=~</code> nel pattern matching per la stringa alla destra dell'operatore.
Si noti che soltanto gli ultimi due operatori non hanno un equivalente ''POSIX'', e l'ordine lessicografico dipende dalle impostazioni locali, e potrebbe restituire risultati diversi su sistemi con impostazioni diverse. Va ricordato che, anche se si applicano regole speciali per le ''keywords'', è '''sbagliato''' non quotare le stringhe o le variabili, salvo si voglia utilizzare il pattern matching su tutta la parte non quotata:
<pre>
[[ "$var" == "${prefisso}"* ]]  # successo se $var inizia con $prefisso
[ -z "${var##${prefisso}*}" ]  # equivalente (POSIX)


===Esempio: controllo sul percorso di un file===
[[ "$var" == *"${stringa}"* ]]  # successo se $var contiene $stringa
[ -z "${var##*${stringa}*}" ]  # equivalente (POSIX)
 
[[ "$var" != *[!A-Za-z0-9]* ]]  # successo se $var contiene solo caratteri alfanumerici (solo lettere base e numeri)
[ -n "${var##*[!A-Za-z0-9]*}" ] # equivalente (POSIX)
</pre>
 
=== Esempio: controllo sul percorso di un file ===
Controllare il percorso di un file, e aggiungere quello relativo se manca:
Controllare il percorso di un file, e aggiungere quello relativo se manca:
<pre>
<pre>
Riga 333: Riga 384:
</pre>
</pre>


===Parsing degli argomenti===
=== Parsing degli argomenti ===
Si effettua con il comando '''getopts''' (da non confondersi con <code>getopt</code>, un comando esterno più avanzato non ''POSIX'' utilizzata allo stesso scopo).
Si effettua con il comando '''getopts''' (da non confondersi con <code>getopt</code>, un comando esterno più avanzato non ''POSIX'' utilizzato allo stesso scopo).


Ogni opzione deve consistere di una sola lettera, e può ammettere un solo argomento. Supporta inoltre l'uso di <code>--</code> che permette di separare le opzioni dall'eventuale lista di altri argomenti.
Ogni opzione deve consistere di una sola lettera, e può ammettere un solo argomento. Supporta inoltre l'uso di <code>--</code> che permette di separare le opzioni dall'eventuale lista di altri argomenti.
Riga 341: Riga 392:


Regole:
Regole:
* nomevariabile è un nome di variabile non preceduto da '''$''' a cui sarà assegnata la prima opzione trovata oppure <code>?</code> in presenza di errori;
* ''nomevariabile'' è un nome di variabile non preceduto da '''$''' a cui sarà assegnata la prima opzione trovata oppure <code>?</code> in presenza di errori;
* l'exit status del comando è negativo solo se non ci sono altre opzioni da assegnare;
* l'exit status del comando è negativo solo se non ci sono altre opzioni da assegnare;
* se la stringa delle opzioni inizia con il carattere <code>:</code> non vengono stampati messaggi di errore, se un argomento non è trovato;
* se la stringa delle opzioni inizia con il carattere <code>:</code> non vengono stampati messaggi di errore, se un argomento non è trovato;
Riga 347: Riga 398:
* il carattere delle opzioni che richiede un argomento ausiliario va seguito dal carattere <code>:</code> e l'argomento sarà assegnato alla variabile <code>$OPTARG</code>, se trovato, altrimenti verrà stampato un errore (se non disabilitati) e la variabile scelta conterrà il carattere <code>?</code>;
* il carattere delle opzioni che richiede un argomento ausiliario va seguito dal carattere <code>:</code> e l'argomento sarà assegnato alla variabile <code>$OPTARG</code>, se trovato, altrimenti verrà stampato un errore (se non disabilitati) e la variabile scelta conterrà il carattere <code>?</code>;
* la variabile <code>$OPTIND</code> è aggiornata a ogni esecuzione di <code>getopts</code> per tenere traccia dell'indice dell'ultimo parametro letto partendo da 1 (per esempio $OPTIND a 5 significa che sono state lette le variabili speciali <code>$1 ... $4</code>, infatti <code>$4</code> è in quinta posizione);
* la variabile <code>$OPTIND</code> è aggiornata a ogni esecuzione di <code>getopts</code> per tenere traccia dell'indice dell'ultimo parametro letto partendo da 1 (per esempio $OPTIND a 5 significa che sono state lette le variabili speciali <code>$1 ... $4</code>, infatti <code>$4</code> è in quinta posizione);
* tutto ciò che non è un'opzione o un argomento di un'opzione, e tutto ciò che segue <code>--</code>, è considerato una lista di argomenti. Per accederli è necessario effettuare lo shift (con il comando <code>shift</code>) di tutte le stringhe già lette, ossia: $OPTIND - 1.
* tutto ciò che non è un'opzione o un argomento di un'opzione, e tutto ciò che segue <code>--</code>, è considerato una lista di argomenti. Per accedervi è necessario effettuare lo shift (con il comando <code>shift</code>) di tutte le stringhe già lette, ossia: $OPTIND - 1.


====Esempio: script con parsing degli argomenti====
==== Esempio: script con parsing degli argomenti ====
<pre>
<pre>
#! /bin/sh
#! /bin/bash


# Argomenti:
# Argomenti:
Riga 362: Riga 413:
azione=c      # per rendere implicita l'azione, la definisco prima
azione=c      # per rendere implicita l'azione, la definisco prima


# Risultato delle tre opzioni: "cef:"  (per getopts)
# Risultato delle tre opzioni: "cxf:"  (per getopts)
# Per disabilitare gli errori: ":cef:" (disabilita la stampa degli errori)
# Per disabilitare gli errori: ":cxf:" (disabilita la stampa degli errori)


# ripeti finché trova opzioni
# ripeti finché trova opzioni
while getopts ":cef:" opzione  # opzione è un nome qualunque di variabile
while getopts ":cxf:" opzione  # opzione è un nome qualunque di variabile
do
do
     case "$opzione" in
     case "$opzione" in
Riga 382: Riga 433:
# quando non trova più opzioni o incontra la stringa -- esce dal ciclo
# quando non trova più opzioni o incontra la stringa -- esce dal ciclo


# verifico l'esistenza dell'opzione, che considero obbligatoria (ma che non lo è per getopts!)
# verifico l'esistenza dell'opzione che considero obbligatoria (ma che non lo è per getopts!)
if [ -z "$archivio" ]; then  
# e per l'azione di estrazione che l'archivio esista e sia un file regolare
if [ -z "$archivio" ] || { [ "$azione" = "x" ] && ! [ -f "$archivio" ] ; }; then  
     printf %s\\n "Archivio mancante!" >&2
     printf %s\\n "Archivio mancante!" >&2
     exit 1
     exit 1
Riga 389: Riga 441:


# ricavo la lista dei file, rimuovendo le $OPTIND - 1 opzioni lette
# ricavo la lista dei file, rimuovendo le $OPTIND - 1 opzioni lette
shift $(($OPTARG - 1))
shift $(($OPTIND - 1))
# ora "$@" contiene la lista delle stringhe non corrispondenti alle opzioni (o tutte quelle dopo --)
# ora "$@" contiene la lista delle stringhe non corrispondenti alle opzioni (o tutte quelle dopo --)


# eseguo qualcosa, per esempio banalmente tar
# eseguo qualcosa, per esempio banalmente tar
tar "${azione}f" "$archivio" -- "$@"
tar "${azione}f" "$archivio" -- "$@" || {
    printf %s\\n "Comando fallito!" >&2
    exit 2  # per distinguerlo dall'exit status di opzione sbagliata/mancante
}
exit 0
</pre>
</pre>


3 581

contributi