Ve většině shellů nullglob
není výchozí. To znamená, že například spustíte tento příkaz
ls *
v prázdném adresáři rozbalí *
glob na doslovný *
místo na prázdný seznam argumentů. Existují způsoby, jak toto chování změnit, takže *
v prázdném adresáři vrátí prázdný seznam argumentů, což by se zdálo intuitivnější.
Existuje tedy důvod, proč nullglob
je ve výchozím nastavení zakázáno? Pokud ano, jaký je ten důvod?
Přijatá odpověď:
nullglob
možnost (která je mimochodem zsh
vynález, jen o roky později přidán do bash
(2.0
)) by v řadě případů nebylo ideální. A ls
je dobrý příklad:
ls *.txt
Nebo jeho správnější ekvivalent:
ls -- *.txt
Pomocí nullglob
on by spustil ls
bez argumentu, který je považován za ls -- .
(vypište aktuální adresář), pokud se žádné soubory neshodují, což je pravděpodobně horší než volání ls
s doslovným *.txt
jako argument.
Podobné problémy byste měli s většinou textových nástrojů:
grep foo *.txt
Hledal by foo
na stdin, pokud neexistuje txt
soubor.
Rozumnějším výchozím nastavením, jako je csh, tcsh, zsh nebo fish 2.3+ (a dřívějších unixových shellů), je úplně zrušit příkaz, pokud glob neodpovídá.
bash
(od verze 3) má failglob
možnost pro to (zajímavé pro tuto diskusi, protože na rozdíl od ash
, AT&T ksh
nebo zsh
, bash
nepodporuje místní rozsahy možností (ačkoli to se ve verzi 4.4 změní), tato možnost, když je povolena globálně, narušuje několik věcí, jako jsou funkce dokončování bash.
Všimněte si, že csh a tcsh se mírně liší od zsh
, fish
nebo bash -O failglob
v případech jako:
ls -- *.txt *.html
Kde potřebujete, aby se všechny koule neshodovaly, aby byl příkaz zrušen. Pokud například existuje jeden soubor txt a žádný soubor html, bude to:
ls -- file.txt
Toto chování můžete získat pomocí zsh
pomocí setopt cshnullglob
i když rozumnější způsob, jak to udělat v zsh
by bylo použít glob jako:
ls -- *.(txt|html)
V zsh
a ksh93
, můžete také použít nullglob na základě jednotlivých zemí, což je mnohem rozumnější přístup než úprava globálního nastavení:
files=(*.txt(N)) # zsh
files=(~(N)*.txt) # ksh93
by vytvořilo prázdné pole, pokud neexistuje žádný txt
namísto selhání příkazu s chybou (nebo vytvoření pole s jedním *.txt
doslovný argument s jinými shelly).
Verze fish
před 2.3 by fungovalo jako bash -O nullglob
ale dávat varování, když je interaktivní, když globus nemá žádnou shodu. Od verze 2.3 funguje jako zsh
kromě globů používaných v for
, set
nebo count
.
Nyní, pokud jde o historii, chování bylo ve skutečnosti přerušeno od Bourne shellu. V předchozích verzích Unixu se globování provádělo prostřednictvím /etc/glob
helper a ten se choval jako csh
:příkaz by selhal, pokud by žádný z globů neodpovídal žádnému souboru, a jinak by globy bez shody odstranil.
Takže situace, ve které jsme dnes, je způsobena špatným rozhodnutím učiněným v Bourneově shellu.
Všimněte si, že Bourne shell (a shell C) přišel s další novou funkcí Unixu:prostředím. To znamenalo variabilní rozšíření (jeho předchůdce měl pouze $1
, $2
… polohové parametry). Bourne shell také zavedl substituci příkazů.
Dalším špatným návrhovým rozhodnutím Bourne shellu bylo provést globování (a rozdělení) po expanzi proměnných a substituci příkazů (možná kvůli zpětné kompatibilitě s Thompsonovým shellem, kde echo $1
by stále vyvolávalo /etc/glob
pokud $1
obsahoval zástupné znaky (tady to bylo spíše jako expanze makra preprocesoru, protože v rozšířené hodnotě byla znovu analyzována jako shell kód)).
Selhání koulí, které se neshodují, by například znamenalo:
pattern='a.*b'
grep $pattern file
by selhal příkaz (pokud tam nejsou nějaké a.whateverb
soubory v aktuálním adresáři). csh
(který také provádí globování při rozšiřování proměnné) v takovém případě příkaz selže (a řekl bych, že je to lepší, než tam nechat spící chybu, i když to není tak dobré, jako když globování neprovádíte vůbec jako v zsh
).