Bash scripting: differenze tra le versioni

possibili problemi con le espansioni di comando
(possibili problemi con le espansioni di comando)
Riga 5: Riga 5:
Per l'uso interattivo si rimanda invece a [[Bash tips]].
Per l'uso interattivo si rimanda invece a [[Bash tips]].


== Variabili e stringhe ==
== Variabili ==


In Bash ogni variabile (di default) è trattata come una stringa. E il contenuto di una variabile si accede con <code>${nome}</code> oppure con la forma abbreviata <code>$nome</code>.
In Bash ogni variabile (di default) è trattata come una stringa. E il contenuto di una variabile si accede con <code>${nome}</code> oppure con la forma abbreviata <code>$nome</code>.
Riga 72: Riga 72:
Si noti invece che usando <code>"$ARGUMENT"</code> (quotata) per una variabile contenente la stringa vuota, il comando leggerebbe lo stesso un argomento e potrebbe fallire.
Si noti invece che usando <code>"$ARGUMENT"</code> (quotata) per una variabile contenente la stringa vuota, il comando leggerebbe lo stesso un argomento e potrebbe fallire.


==Variabili speciali==
===Variabili speciali===


; <code>$?</code> : contiene il valore di uscita dell'ultimo comando o funzione. Il comando ha successo se restituisce zero, qualsiasi altro valore indica invece un codice di errore;
; <code>$?</code> : contiene il valore di uscita dell'ultimo comando o funzione. Il comando ha successo se restituisce zero, qualsiasi altro valore indica invece un codice di errore;
Riga 212: Riga 212:
===Quotare (racchiudere tra virgolette)===
===Quotare (racchiudere tra virgolette)===


Racchiudere tra virgolette (ossia quotare) ogni stringa è in genere raccomandabile e la scelta consigliata, così da ridurre drasticamente il numero di caratteri speciali, permettendo allo stesso tempo l'espansione delle variabili e dei comandi. I soli caratteri speciali sono <code>$</code>, <code>`</code> (ma non l'apice), <code>"</code> e <code>\</code> devono essere preceduti dal carattere di escape <code>\</code>. Le espansioni di percorso e tilda (*, ?, [...], ~, ... ) non sono possibili; mentre lo sono quelle di variabile e parametro (attraverso un <code>$</code> senza <code>\</code>), e quelle di comando (attraverso un <code>`</code> o un <code>$(</code> senza <code>\</code>).
Racchiudere tra virgolette (ossia quotare) ogni stringa è in genere raccomandabile e la scelta consigliata, così da ridurre drasticamente il numero di caratteri speciali, permettendo allo stesso tempo l'espansione delle variabili e dei comandi. I soli caratteri speciali sono <code>$</code>, <code>`</code> (ma non l'apice), <code>"</code> e <code>\</code>, che devono essere preceduti dal carattere di escape <code>\</code>. Le espansioni di percorso e tilda (*, ?, [...], ~, ... ) non sono possibili; mentre lo sono quelle di variabile e parametro (attraverso un <code>$</code> senza <code>\</code>), e quelle di comando (attraverso un <code>`</code> o un <code>$(</code> senza <code>\</code>).


Riprendendo l'esempio e considerandolo per parti:
Riprendendo l'esempio e considerandolo per parti:
Riga 225: Riga 225:
==Espansione di comando==
==Espansione di comando==


Si effettua racchiudendo un comando tra <code>$(...)</code>, oppure tra due apici gravi <code>`...`</code> (su tastiera con layout italiano: <code>Alt Gr + '</code>):
Consiste nel trasformare l'output di un comando qualsiasi (interno della shell, esterno, una funzione e anche forme composte) in argomenti per un altro comando, oppure nel valore da assegnare a una variabile. Si effettua racchiudendo un comando tra <code>$(...)</code>, oppure tra due apici gravi <code>`...`</code> (su tastiera con layout italiano: <code>Alt Gr + '</code>):
<pre>$(comando)</pre>
<pre>$(comando)</pre>
oppure, meno leggibile e sconsigliata:
oppure, meno leggibile e sconsigliata:
Riga 232: Riga 232:
L'output del comando consiste in zero, una o più stringhe, in base agli spazi presenti nell'output prodotto e in maniera analoga all'espansione a cui sono soggette le variabili. Per trasformare l'output di un comando in una singola stringa è necessario che l'espansione di comando sia quotata, con l'eccezione dell'assegnazione a una variabile.
L'output del comando consiste in zero, una o più stringhe, in base agli spazi presenti nell'output prodotto e in maniera analoga all'espansione a cui sono soggette le variabili. Per trasformare l'output di un comando in una singola stringa è necessario che l'espansione di comando sia quotata, con l'eccezione dell'assegnazione a una variabile.


Per esempio:
Esempi di assegnazione:
<pre>
<pre>
oggi=$(date +%F) # assegna alla variabile $oggi la data in formato YYYY_MM_DD
# assegna alla variabile $oggi la data in formato YYYY_MM_DD
oggi=$(date +%F)   # senza virgolette
oggi="$(date +%F)" # equivalente a sopra (non servono nelle assegnazioni)
 
# associo a testo il contenuto del file indicato da $file
testo=$(cat $file)    # SBAGLIATO! (se la variabile $file contiene spazi o caratteri speciali)
testo=$(cat "$file")  # le virgolette attorno alla variabile sono necessarie
testo="$(cat "$file")" # equivalente a sopra
</pre>


Esecuzione dei comandi e passaggio come argomento del loro output:
<pre>
# stampa stati
# stampa stati
echo "Login name: $(logname); Name: $(whoami); UID: $(id -ur); EUID: $(id -u); Groups: $(groups)"
echo "Login name: $(logname); Name: $(whoami); UID: $(id -ur); EUID: $(id -u); Groups: $(groups)"
echo "OS: Debian GNU/Linux $(cat /etc/debian_version) ($(lsb_release -sc))" # uguale a $(lsb_release -sd)
echo "OS: Debian GNU/Linux $(cat /etc/debian_version) ($(lsb_release -sc))" # uguale a $(lsb_release -sd)
echo "Kernel: $(uname) $(uname -r) ($(uname -v))"
echo "Kernel: $(uname) $(uname -r) ($(uname -v))"
</pre>
===Output con a capo finali===
Si noti che l'espansione di comando, come anche in '''sh''' (''POSIX'') non espande il comando a tutto l'output prodotto, ma omette sempre gli "a capo" finali. Se da una parte è utile nella maggior parte delle situazioni, può talvolta avere effetti collaterali difficili da prevedere. Si consideri per esempio:
<pre>echo ciao > file_prova # scrive ciao (più un "a capo") nel file_prova
testo=$(cat file_prova)    # associa il contenuto (senza "a capo") a $testo
echo "Lunghezza del file: ${#testo}" # non è la dimensione esatta del file
echo "$testo"              # stampa il contenuto di $testo (più un "a capo")
</pre>
{{Warningbox | Una variabile non può contenere il carattere ASCII numero 0, quindi per i file binari è sempre sbagliato accederli in questo modo. È sconsigliabile anche se non si è certi della loro dimensione, e se si è interessati soltanto alla prima riga si può utilizzare <code>read riga < file_prova</code>}}
Un altro esempio, vogliamo associare il carattere "a capo" a una variabile:
<pre>
nuova_riga="
"                          # funziona, ma è brutto esteticamente e non permette l'indentazione
nuova_riga=$'\n'          # funziona (con Bash) ed è il modo consigliato


# comando più complesso con pipe che conta il numero di subdirectory
# a titolo esemplicativo con l'espansione di comando
numero_directory=$(find . -type d -print0 | tr -dc '\000' | wc -c)
nuova_riga=$(echo)        # SBAGLIATO, $nuova_riga è vuota
nuova_riga="$(echo)"      # SBAGLIATO, come sopra
nuova_riga=$(printf "\n")  # SBAGLIATO, $nuova_riga è sempre vuota
# una possibile soluzione...
nuova_riga=$(printf "\nX") # $nuova_riga contiene "a capo" seguito da X
nuova_riga=${nuova_riga%X} # $nuova_riga contiene "a capo" (la X è rimossa)
# Attenzione che il carattere aggiunto dev'essere nell'output del comando
nuova_riga=$(printf "\n")X # SBAGLIATO, $nuova_riga contiene solo X
</pre>
</pre>


Riga 386: Riga 421:
Cancella contenuto di un file:
Cancella contenuto di un file:
  $ :> xterm.txt
  $ :> xterm.txt
===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 lasciato a comandi esterni mediante l'uso di una o più pipe.
Infatti non esiste un modo di contenere il carattere ASCII n. 0 in nessuna posizione:
<pre>
var=$(printf '\000')    # SBAGLIATO: $var è vuota
var=$(printf '\000X')    # SBAGLIATO: $var contiene solo X
var=$(printf 'X\000X')  # SBAGLIATO: $var contiene XX
var="$(printf 'X\000X')" # SBAGLIATO: equivalente a sopra
</pre>
L'utilità di questo carattere si deve in particolare al fatto che nemmeno i file possono averlo nel proprio nome, mentre invece permettono caratteri jolly (*, ?, ...) come abbiamo già visto con le espansioni di percorso, e potrebbero contenere perfino il carattere "a capo".
Uno script a titolo esemplificativo:
<pre>
# creo una directory e un file con "a capo" nel nome
mkdir ./prova
touch ./prova/"file contenente"$'\n'"a capo nel nome"
# SBAGLIATO! (per via del nome del file particolare)
num_file=$(find ./prova -type f | # stampo i file regolari nella directory, uno per riga
          wc -l)                # conto il numero di righe
echo "La directory prova contiene ${num_file} file" # restituisce 2 invece di 1
# forma corretta
num_file=$(find ./prova -type f -print0 | # stampo il carattere ASCII n. 0 dopo ogni file
  tr -dc '\000' |                # rimuovo tutti i caratteri ASCII diversi dal n. 0
          wc -c)                        # conto il numero di caratteri
echo "La directory prova contiene ${num_file} file" # restituisce 1
# pulizia
rm -- ./prova/file*nome  # elimino il file
rmdir -- ./prova        # elimino la directory (se vuota)
</pre>
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>
find /percorso -opzione1 ... -opzioneN -print0 | # trova file che soddisfano le condizioni date
xargs -0 comando [ argomenti ... ]              # passa questi file al comando
</pre>
Si leggano i rispettivi manuali per maggiori informazioni (<code>man find</code> e <code>man xargs</code>).


== Debug integrato ==
== Debug integrato ==
Riga 414: Riga 494:
|Verificata_da=
|Verificata_da=
:[[Utente:S3v|S3v]] (versione in Bash tips)
:[[Utente:S3v|S3v]] (versione in Bash tips)
:[[Utente:HAL 9000|HAL 9000]] 14:28, 4 lug 2014 (CEST)
:[[Utente:HAL 9000|HAL 9000]] 21:46, 4 lug 2014 (CEST)
|Estesa_da=
|Estesa_da=
:[[Utente:S3v|S3v]] (versione in Bash tips)
:[[Utente:S3v|S3v]] (versione in Bash tips)
3 581

contributi