2012-02-03 32 views
18

In bash, se si esegue questa operazione:C'è un modo semplice per impostare nullglob per un glob

mkdir /tmp/empty 
array=(/tmp/empty/*) 

si scopre che array ha ora un elemento, "/tmp/empty/*", non zero come vuoi. Per fortuna, questo può essere evitato attivando l'opzione di shell nullglob utilizzando shopt -s nullglob

Ma nullglob è globale, e quando si modifica uno script di shell esistente, può rompere le cose (per esempio, qualcuno ha controllare il codice di uscita di ls foo* per verificare se ci sono i file che iniziano con "pippo"?). Quindi, idealmente, mi piacerebbe accenderlo solo per un piccolo ambito - idealmente, un'espansione del nome file. È possibile spegnerlo nuovamente usando shopt -u nullglob Ma, naturalmente, solo se è stato disattivato prima:

old_nullglob=$(shopt -p | grep 'nullglob$') 
shopt -s nullglob 
array=(/tmp/empty/*) 
eval "$old_nullglob" 
unset -v old_nullglob 

mi fa pensare ci deve essere un modo migliore. L'ovvio "metterlo in una subshell" non funziona, ovviamente l'assegnazione della variabile muore con la subshell. Altro che waiting for the Austin group per importare la sintassi di ksh93, c'è?

risposta

9

Con mapfile in Bash 4, è possibile caricare una matrice da un subshell con qualcosa di simile : mapfile array < <(shopt -s nullglob; for f in ./*; do echo "$f"; done). esempio completo:

$ shopt nullglob 
nullglob  off 
$ find 
. 
./bar baz 
./qux quux 
$ mapfile array < <(shopt -s nullglob; for f in ./*; do echo "$f"; done) 
$ shopt nullglob 
nullglob  off 
$ echo ${#array[@]} 
2 
$ echo ${array[0]} 
bar baz 
$ echo ${array[1]} 
qux quux 
$ rm * 
$ mapfile array < <(shopt -s nullglob; for f in ./*; do echo "$f"; done) 
$ echo ${#array[@]} 
0 
  • Assicurarsi di glob con ./* invece di una nuda * quando si utilizza echo per stampare il nome del file
  • non funziona con caratteri newline nel nome del file :(come sottolineato da derobert

Se è necessario gestire nuove righe nel nome del file, è w malati hanno a che fare il tanto più prolisso:

array=() 
while read -r -d $'\0'; do 
    array+=("$REPLY") 
done < <(shopt -s nullglob; for f in ./*; do printf "$f\0"; done) 

Ma a questo punto, può essere più semplice di seguire il consiglio di una delle altre risposte.

+1

Questo è abbastanza buono. Fallirà se i nomi dei file contengono delle nuove righe, ma sebbene ciò sia consentito, è piuttosto raro. Finché i nomi dei file non sono sotto il controllo di un aggressore. – derobert

+0

Sì, esistono nuove righe nei nomi dei file. Normalmente, ciò dovrebbe significare solo "trovare il colpevole e punirlo/lei". A parte il fatto che l'unica volta che ho visto newline nei nomi dei file, il colpevole era un'affascinante signora anziana ... – mivk

1

Questo potrebbe essere vicino a ciò che si desidera; così com'è, richiede l'esecuzione di un comando per espandere il glob.

$ ls 
file1 file2 
$ array=($(shopt -s nullglob; ls foo*)) 
$ ls foo* 
ls: foo*: No such file or directory 
$ echo ${array[*]} 
file1 file2 

Invece di impostare array nel sottoshell, si crea una subshell utilizzando $() cui uscita è catturato da array.

+5

Non funziona con gli spazi (nessuna sorpresa): 'touch" file1 "" file 2 "; array = ($ (shopt -s nullglob; ls foo *)); set | grep^array' produce 'array = ([0] =" file1 "[1] =" file "[2] =" 2 ")' – derobert

13

disinserito quando fatto:

shopt -u nullglob 

e adeguatamente (vale a dire la conservazione dello stato precedente):

shopt -u | grep -q nullglob && changed=true && shopt -s nullglob 
... do whatever you want ... 
[ $changed ] && shopt -u nullglob; unset changed 
+1

Questo evita l'eval e non mi sono reso conto -s e -u aveva quel comportamento ('help shopt' non riesce a parlarne). Lo sto svalutando per quelli. Ma onestamente, non è molto più semplice, in termini di ciò che devi ricordare per avere ragione, c'è molto in quella prima riga. – derobert

+1

Non ho mai detto che fosse più semplice :-) Ma fa semplicemente quello che la tua domanda ha posto, usa nullglob senza influenzare altri script avviati dalla stessa sessione. Ricordare il valore di nullglob aggiunge complessità, ma è l'unico modo per _preservare_ l'ambiente così com'era. – estani

+0

Bene, la domanda era per un * migliore * o * più facile * modo di farlo (poiché il secondo esempio di codice nella domanda riesce anche a ripristinare l'impostazione al suo stato precedente). Apparentemente, non l'ho chiarito nella domanda, per favore suggerisci come migliorarlo. – derobert

6

Questo è solo un pochino meglio che la tua proposta originale:

nullglob locale = $ (shopt -p nullglob); shopt -s nullglob

... fai quello che vuoi ...

$ nullglob; nullglob unset

0

Questa è la soluzione più semplice che ho trovato:

Ad esempio, per espandere il letterale **/*.mp3 in un glob solo per una particolare variabile, è possibile utilizzare

VAR=**/*.mp3(N) 

Fonte: https://unix.stackexchange.com/a/204944/56160

+1

Nota che è una funzione zsh non trovata in bash, che OP ha specificato. – ssmith

Problemi correlati