2012-02-10 13 views
22

Il mio collega, Ryan, è venuto da me con un bug nel suo script Bash, e ho identificato il problema con questo test:risultati Assegnare di globbing ad una variabile in Bash

$ mkdir ryan 
$ mkdir ryan/smells-bad 
$ FOO=ryan/smells-* 
$ echo $FOO 
ryan/smells-bad 
$ touch $FOO/rotten_eggs 
touch: cannot touch `ryan/smells-*/rotten_eggs': No such file or directory 

Da questo deduco che il il globbing avviene durante il comando echo, non quando viene creata la variabile FOO.

abbiamo un paio di soluzioni alternative, in ordine decrescente di ungracefulness:

touch `echo $FOO`/rotten_eggs 

Oppure:

pushd 
cd $FOO 
touch rotten_eggs 
popd 

Ma non è soddisfacente. Mi manca un trucco?

risposta

29

Il problema è che il glob si espanderà solo se il file "rotten_eggs" esiste, perché è incluso nel modello glob. Dovresti usare un array.

FOO=(ryan/smells-*) 
touch "${FOO[@]/%//rotten_eggs}" 

L'array FOO contiene tutto ciò che corrisponde al glob. L'espansione utilizzando% appends/rotten_eggs per ogni elemento.

+0

Grazie, questo lo spiega bene. –

+0

Questo non è affatto il problema. Il problema è che bash assegna il glob alla variabile prima dell'espansione. Anche se il file esistesse, un glob sarebbe assegnato alla variabile, non un nome di file; e quel glob sarebbe stato espanso sull'uso –

+0

@SamLiddicott L'esempio dell'OP avrebbe funzionato se esistessero "rotten_eggs", quindi era più o meno il problema. Il tempo in cui il glob è espanso non è rilevante nel breve esempio dell'OP. – jordanm

6

consideri

for dir in $FOO; do 
    touch "$dir/rotten_eggs" 
done 

Si noti che questo touch più file se il modello di glob corrisponde più di un percorso.

+0

Questo funzionerà, ma richiede che il tocco venga eseguito più volte, anziché una volta. – jordanm

+1

Questo è probabilmente più chiaro, a seconda della situazione. –

3

lo farei in questo modo:

for FOO in ryan/smells-*; do 
    touch "$FOO"/rotten_eggs 
done 

In questo modo $FOO contiene il nome della directory attuale, non il modello di glob. Se c'è più di una corrispondenza, tuttavia, conterrà solo l'ultima dopo il ciclo, quindi la soluzione dell'array potrebbe essere migliore per quel caso.

0

Il codice come previsto con il risultato del glob assegnato alla variabile sarebbe come questo:

$ mkdir ryan 
$ mkdir ryan/smells-bad 
$ FOO=(ryan/smells-*) 
$ echo "${FOO[@]}" 
ryan/smells-bad 
$ echo "$FOO" 
ryan/smells-bad 
$ touch "$FOO/rotten_eggs" 
$ ls -l "$FOO" 
total 0 
-rw-r--r-- 1 ryan ryan 0 Mar 1 11:17 rotten_eggs 

$FOO è in realtà una serie qui, ma $ FOO funziona anche per ottenere il primo elemento della matrice .

ma, vedere come il glob può corrispondere a più di un file (da qui la matrice è una buona idea)

$ mkdir ryan/clean 
$ FOO=(ryan/*) 
$ echo "$FOO" 
ryan/clean 
$ echo "${FOO[@]}" 
ryan/clean ryan/smells-bad 

In questi casi i risultati del glob è assegnato alla variabile a piacere, anziché la variabile viene espansa come glob nel punto di utilizzo.

Naturalmente questo significa che la variabile in realtà dovrebbe sempre essere usato in virgolette doppie "..." altrimenti se il nome del file stesso (l'espansione glob) aveva anche un * in esso, sarebbe glob di nuovo.

ad es.

$ touch ryan/'*ea*' 
$ FOO=(ryan/*ea*) 
$ echo "${FOO[@]}" 
ryan/clean ryan/*ea* 
$ echo ${FOO[@]} 
ryan/clean ryan/clean ryan/*ea* 
Problemi correlati