Perché nullglob non è predefinito?


61

Nella maggior parte delle shell nullglobnon è l'impostazione predefinita. Ciò significa, ad esempio, se si esegue questo comando

ls *

in una directory vuota, espanderà il *glob in un letterale *, anziché in un elenco vuoto di argomenti. Esistono modi per modificare tale comportamento, in modo che *in una directory vuota venga restituito un elenco vuoto di argomenti, che sembrerebbe più intuitivo.

Quindi, c'è un motivo per cui nullglobè disabilitato per impostazione predefinita? In tal caso, qual è questa ragione?


13
Qualcuno una volta ha fatto una scelta sbagliata che si è trasformata in "l'abbiamo sempre fatto in questo modo". Un fenomeno molto diffuso (non solo) nel mondo del software.
PSkocik,

4
Il motivo originale è che all'epoca l'opzione nullglob non esisteva. Quindi, per mantenere la compatibilità con le versioni precedenti, deve essere disabilitato per impostazione predefinita.
PM 2Ring

3
Anche se non menziona specificamente il glob nulla, una certa storia sui globbing è fornita da questa risposta: unix.stackexchange.com/a/136409/22724
StrongBad

4
Ho l'impressione che alcuni pensano che dovrebbe essere ovvio che nullglob dovrebbe essere abilitato di default. Non penso sia ovvio. Che le espansioni avvengano nella shell prima dell'invocazione dei comandi significa che espandersi a nulla è un comportamento meno intuitivo rispetto al fatto che il glob rimanga invariato.
Kojiro,

2
@kojiro Meno intuitivo per chi? Chiunque abbia familiarità con le shell * NIX sa che *è un glob e si espande a tutti i file esistenti ; come è "intuitivo" che ci sia un caso speciale in cui i globs della directory vuota vengono "espansi" in un letterale *?
Kyle Strand,

Risposte:


78

L' nullglobopzione (che BTW è zshun'invenzione, aggiunta solo anni dopo a bash( 2.0)) non sarebbe l'ideale in un numero di casi. Ed lsè un buon esempio:

ls *.txt

O il suo equivalente più corretto:

ls -- *.txt

Con nullglobon verrebbe eseguito lssenza alcun argomento che viene trattato come ls -- .(elenca la directory corrente) se nessun file corrisponde, il che è probabilmente peggio che chiamare lscon un letterale *.txtcome argomento.

Avresti problemi simili con la maggior parte delle utility di testo:

grep foo *.txt

Sarebbe cercare foosu stdin se non c'è txtil file.

Un default più sensato, e quello di csh, tcsh, zsh o fish 2.3+ (e delle prime shell Unix) è annullare del tutto il comando se il glob non corrisponde.

bash(dalla versione 3) ha failglobun'opzione per questo (interessante per questa discussione, poiché contrariamente a ash, AT&T ksho zsh, bashnon supporta gli ambiti locali per le opzioni (anche se questo è cambiare in 4.4), quell'opzione quando abilitata a livello globale interrompe alcune cose come le funzioni di completamento bash).

Nota che csh e tcsh sono leggermente diversi da zsh, fisho bash -O failglobin casi come:

ls -- *.txt *.html

Dove sono necessari tutti i globs per non corrispondere affinché il comando venga annullato. Ad esempio, se esiste un file txt e nessun file html, questo diventa:

ls -- file.txt

È possibile ottenere che il comportamento con zshcon setopt cshnullglobse un modo più sensato di farlo in zshsarebbe quello di utilizzare un glob come:

ls -- *.(txt|html)

In zshe ksh93, puoi anche applicare nullglob su base per-glob, che è un approccio molto più sano rispetto alla modifica di un'impostazione globale:

files=(*.txt(N))  # zsh
files=(~(N)*.txt) # ksh93

creerebbe un array vuoto se non ci sono txtfile invece di fallire il comando con un errore (o renderlo un array con un *.txtargomento letterale con altre shell).

Le versioni fishprecedenti alla 2.3 funzionerebbero come bash -O nullglobma darebbero un avvertimento quando interattivo quando un glob non ha corrispondenza. Dal 2.3, funziona come zshtranne per i globs usati in for, seto count.

Ora, sulla nota della storia, il comportamento è stato effettivamente rotto dalla shell Bourne. Nelle versioni precedenti di Unix, il globbing veniva eseguito tramite l' /etc/globhelper e quell'helper si comportava in modo simile csh: avrebbe fallito il comando se nessuno dei glob avesse corrispondenza con qualsiasi file e avrebbe rimosso i globs senza alcuna corrispondenza.

Quindi la situazione in cui ci troviamo oggi è dovuta a una decisione sbagliata presa nella shell Bourne.

Si noti che la shell Bourne (e la shell C) è arrivata con un'altra nuova funzionalità Unix: l'ambiente. Ciò significava espansione variabile (il suo predecessore aveva solo i $1, $2... parametri posizionali). La shell Bourne ha anche introdotto la sostituzione dei comandi.

Un'altra cattiva decisione di progettazione della shell Bourne è stata quella di eseguire il globbing (e la divisione) sull'espansione delle variabili e la sostituzione dei comandi (probabilmente per la compatibilità con le versioni precedenti della shell Thompson dove echo $1sarebbe comunque invocare /etc/globse $1contenesse caratteri jolly (era più simile all'espansione macro pre-processore) lì, come nel valore espanso è stato nuovamente analizzato come codice shell)).

I globs non corrispondenti che non corrispondono significherebbero ad esempio che:

pattern='a.*b'
grep $pattern file

fallirebbe il comando (a meno che non ci siano alcuni a.whateverbfile nella directory corrente). csh(che esegue anche globbing in caso di espansione variabile) in quel caso fallisce il comando (e direi che è meglio che lasciare un bug dormiente lì, anche se non è buono come non fare globbing come in zsh).


C'è un altro problema di usabilità: nullglobsembra interrompere il completamento della scheda (premendo il tasto Tab non fa nulla quando è abilitato).
Kyle Strand,

1
È possibile utilizzare nullglob su base per glob con bash - anche se la sintassi non è elegante come zshfiles=$(shopt -s nullglob;echo *.txt)
Jon Nalley,

2
@JonNalley, che memorizza la concatenazione (con spazio) dei nomi dei file (con possibile trasformazione con xpg_echo) in variabili scalari . Avresti bisogno di qualcosa di simile readarray -td '' files < <(shopt -s nullglob; printf '%s\0' *.txt)con bash4.4 o versioni successive o (shopt -s nullglob; printf '%s\0' *.txt) | xargs -r0 cmdcon GNU xargsper essere utilizzabile con nomi di file arbitrari. Oppure, sempre con bash4.4, utilizzare una funzione di supporto che utilizza local -(copiato da Ash 25 anni dopo) per un ambito locale per le opzioni.
Stéphane Chazelas,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.