Bash scripting - istruzioni composte: differenze tra le versioni

Vai alla navigazione Vai alla ricerca
(spostamento condizioni complesse, aggiunto pattern matching con ... e base64 per trattare output qualsiasi)
Riga 1: Riga 1:
{{Bash scripting}}
{{Bash scripting}}
=Istruzioni composte=
=Istruzioni composte=
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==
Riga 115: Riga 117:
</pre>
</pre>
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.
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====
====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 215: Riga 228:


===Condizioni complesse===
===Condizioni complesse===
Esempi di condizioni, equivalenti, per controllare se una variabile contiene un intero:
<pre>
[ "$var" -ge 0 ] 2> /dev/null || [ "$var" -lt 0 ] 2> /dev/null
[ "$var" -ne 0 ] 2> /dev/null || [ "$var" -eq 0 ] 2> /dev/null # equivalente
[ "$var" -ne 0 ] 2> /dev/null || [ "$var" = 0 ]                # equivalente
</pre>
E la condizione può essere usata con <code>if</code>:
<pre>
if [ "$numero" -ne 0 ] 2> /dev/null || # fallisce solo se $numero è 0 oppuer se non è intero
  [ "$numero" = 0 ]                  # copre il caso in cui $numero è 0
then
    printf %s\\n "Il numero inserito (${numero}) è intero"
else
    printf %s\\n "ERRORE: il numero inserito non (${numero}) è intero!" >&2
fi
</pre>
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'', 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.


Riga 231: Riga 262:
</pre>
</pre>


Verifica che una variabile contenga un intero (maggiore o minore di zero):
Esempi di condizioni complesse sull'esistenza dei file:
<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 listarne il contenuto
# (per sapere se un file esiste nella directory)
[ -d "$var" ] && [ -r "$var" ]
 
# Controlla che la directory esista e sia possibile accederne il contenuto
# (leggendo o scrivendo i file, se i loro permessi lo consentono)
[ -d "$var" ] && [ -x "$var" ]
 
# Controlla che la directory esista e sia possibile listarne e accederne il contenuto
[ -d "$var" ] && [ -r "$var" ] && [ -x "$var" ]
 
# Controlla che la directory esista e sia possibile creare e cancellare dei file noti
# (permesso di attraversamento + scrittura)
[ -d "$var" ] && [ -x "$var" ] && [ -w "$var" ]
 
# Controlla che la directory esista e sia possibile cancellarne il contenuto (non noto)
# (permesso di lettura, per listarne il 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 scrittura:
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" ] && [ -w "$file" ] ; }; then
if [ -z "$file" ] || { [ -f "$file" ] && [ -w "$file" ] ; }; then
Riga 308: Riga 357:
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)
 
[[ "$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)
[ -z "${var##*[!A-Za-z0-9]*}" ] # equivalente (POSIX)
</pre>


===Esempio: controllo sul percorso di un file===
===Esempio: controllo sul percorso di un file===
Riga 336: Riga 403:


===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 343: Riga 410:


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;