3 581
contributi
m (→Esempio: script con parsing degli argomenti: sh invece di bash) |
(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]] | * <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, | 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> | ||
Esempi di condizioni complesse sull'esistenza dei file: | |||
<pre> | <pre> | ||
# Controlla che il file regolare esista e sia leggibile | |||
[ -f "$var" ] && [ -r "$var" ] | |||
# Controlla che il file regolare esista e sia eseguibile | |||
# (permesso di lettura + esecuzione) | |||
[ -f "$var" ] && [ -r "$var" ] && [ -x "$var" ] | |||
# 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> | ||
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. | ||
'''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'' | 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; |
contributi