Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
# Bash Scripting - Quoting
Chiunque abbia usato seriamente la Bash si è scontrato almeno una volta (e probabilmente più di una) con problemi legati al quoting.
Uno dei problemi più comuni è la difficoltà nello script che non riescono a gestire casi in teoria semplici come i file con uno spazio nel nome. Quasi come se fosse impossibile gestire dei file con nomi di quel tipo.
In realtà, anche se quasi nessuno sa come, con la Shell è possibile gestire file con nomi contenenti praticamente qualsiasi cosa. Per come è fatta Bash è possibile lavorare con qualsiasi nome di file che il kernel sia in grado di gestire. Quindi, ammesso di conoscere bene le regole del quoting, si può lavorare con problemi sia con i file con nomi contenenti spazi, sia pure con file con nomi contententi le più strane sequenze unicode (dagli emoji della birra a pure alle sequenze unicode non ancora definite nello standard).
Il quoting della shell, se usato correttamente, permette di gestire qualsiasi nome di file. Molti trovano difficoltà a capire il modo di usarlo correttamente a causa delle sue regole apparentemente contro-intuitive e contrarie a tutto quello che si trova negli altri linguaggi.
Nel resto del testo cerco di spiegare il modo più semplice di interpretare il quoting, una volta letto dovreste essere tra quei pochi utenti in grado di fare degli script che continuano a funzionare anche quando ci sono gli spazi.
__Fare delle prove__
Il punto di partenza per poter capire il quoting è poter fare delle prove. Il modo più semplice è avere a disposizione il programma `print-args`. print-args è un programmino che semplicemente stampa in output gli argomenti passati sulla sua linea di comando.
Aspetta a cercarlo sul tuo sistema perché sul tuo sistema `print-args` non c'è. Non è un programma che fa parte della tua distribuzione, e un programmino che poco più avanti ti farò vedere come crearlo da zero.
Prima di farti vedere come si crea ti voglio far vedere come si usa. Un esempio di output di `print-args` è il seguente:
$ ./print-args uno due tre
[1] ->uno<-
[2] ->due<-
[3] ->tre<-
`print-args` semplicemente stampa in output gli argomenti passati dalla linea di comando. In output ogni parametro è indicato tra parentsi quadre la posizione ed è "circondato" da una coppia di freccine. La presenza delle freccine sarà comoda più avanti per riuscire a rilevare l'eventuale presenza di __caratteri invisibili__ (come ad esempio lo spazio, il tab, o anche il ritorno a capo) che di solito sono quelli che sono più difficili da gestire.
__Come si crea print-args?__
Per creare print-args, se hai il compilatore gcc installato, dovrebbe bastarti copiare e incollare queste righe:
cat >| print-args.c << \'
#include <stdio.h>
int main(int argc, char**argv) {
int i;
for(i=1; i<argc; i++) {
printf("[%d] ->%s<-\n", i, argv[i]);
}
return 0;
}
'
gcc print-args.c -o print-args
Se tutto è andato bene dovresti essere in grado di usarlo:
$ ./print-args hello world
[1] ->hello<-
[2] ->world<-
__Non solo quoting__
Volendo guardare la pagina di manuale sembra (ed è) molto precisa nel definire come funziona il quoting. Per accorgesene basta cercare ^QUOTING in bash(1). La cosa che forse può non apparire chiara dalla pagina è che il Quoting si può capire solo considerandolo assieme ad altri concetti relativi a come Bash interpreta quello che gli diciamo.
Il quoting si riesce a capire solo in funzione delle altre operazioni che accadono durante la fase di EXPANSION (presente anch'essa nel manuale), in particolare la parte del __Word Splitting__.
Una volta data una veloce lettura al paragrafo del Word Splitting ti consiglio di cominciare subito a fare le prove.
__Le prove__
E provarlo e scoprire cosa succede nei casi particolari:
$ ./print-args #nessun argomento
$ ./print-args uno # un argomento
[1] ->uno<-
$ ./print-args "" # un argomento vuoto
[1] -><-
$ ./print-args '' # un argomento vuoto usando single quoting
[1] -><-
$ a="uno due"
$ ./print-args $a #una singola variabile che si trasforma in 2 argomenti perché c'è lo spazio
[1] ->uno<-
[2] ->due<-
$ ./print-args "$a" #una singola variabile che non si spezza in due argomenti perché stai usando il quoting
[1] ->uno due<-
$ b=(dipsy lala po "tinky winky") # una variabile che è però è un array di 4 elementi (di cui l'ultimo con spazio)
$ declare -p b
declare -a b='([0]="dipsy" [1]="lala" [2]="po" [3]="tinky winky")'
$ ./print-args $b # non va bene: stampa solo il primo elemento perché si comporta come se non fosse l'array
[1] ->dipsy<-
$ ./print-args ${b[@]} # non va bene: perché spezza l'ultimo teletubby come ce ne fossero 5
[1] ->dipsy<-
[2] ->lala<-
[3] ->po<-
[4] ->tinky<-
[5] ->winky<-
$ ./print-args "${b[@]}" # questo funziona
[1] ->dipsy<-
[2] ->lala<-
[3] ->po<-
[4] ->tinky winky<-
Nel paragrago Quoting della pagina di manuale è anche spiegato come fare escaping per i caratteri unicode.
$ echo $'\U1F37A' # <- stampa il carattere della boccale di birra, non tutti font lo supportano
🍺
Funziona solo con le Bash recenti, altrimenti l'unico modo è specificare la codifica UTF-8 in esadecimale:
$ echo $'\xF0\x9F\x8D\xBA'
🍺
Se invece ti interessa il meta-scripting e vuoi generare uno script che quoti correttamente devi usare: printf "%q".
Riprendendo i nostri amici teletubbies:
teletubbies=(dipsy lala po "tinky winky")
Puoi generare uno script che crea un file per ogni teletubbies così:
for i in "${teletubbies[@]}"; do
printf "touch %q\n" "$i"
done >| script.sh
Se fai a vedere il risultato puoi vedere che lo spazio in "tinky winky" è correttamente quotato:
$ cat script.sh
touch dipsy
touch lala
touch po
touch tinky\ winky
__Approndimenti__
Questo tutorial è un must-read riguardo alla Bash: http://www.tldp.org/LDP/abs/html/ è ha anche una sezione riguardo al quoting.
Ciao
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.