Dans la plupart des shells, nullglob
n'est pas la valeur par défaut. Cela signifie, par exemple, si vous exécutez cette commande
ls *
dans un répertoire vide, il étendra le *
glob à un littéral *
, à la place d'une liste d'arguments vide. Il existe des moyens de modifier ce comportement afin que *
dans un répertoire vide renverra une liste vide d'arguments, ce qui semblerait plus intuitif.
Alors, y a-t-il une raison pour laquelle nullglob
est désactivé par défaut? Si oui, quelle est cette raison?
L'option nullglob
(dont BTW est une invention zsh
, ajoutée seulement des années plus tard à bash
(2.0
)) ne serait pas idéal dans un certain nombre de cas. Et ls
est un bon exemple:
ls *.txt
Ou son équivalent plus correct:
ls -- *.txt
Avec nullglob
on exécuterait ls
sans argument traité comme ls -- .
(liste le répertoire courant) si aucun fichier ne correspond, ce qui est probablement pire que d'appeler ls
avec un littéral *.txt
comme argument.
Vous auriez des problèmes similaires avec la plupart des utilitaires de texte:
grep foo *.txt
Recherche foo
sur stdin s'il n'y a pas de fichier txt
.
Une valeur par défaut plus raisonnable, et celle de csh, tcsh, zsh ou fish 2.3+ (et des premiers shells Unix) est d'annuler complètement la commande si le glob ne correspond pas.
bash
(depuis la version 3) a une option failglob
pour cela (intéressant pour cette discussion, car contrairement à ash
, AT&T ksh
ou zsh
, bash
ne prend pas en charge les étendues locales pour les options (bien que cela doive changer en 4.4), cette option, lorsqu'elle est activée globalement, casse quelques éléments comme les fonctions de complétion bash).
Notez que csh et tcsh sont légèrement différents de zsh
, fish
ou bash -O failglob
dans des cas comme:
ls -- *.txt *.html
Où vous avez besoin que tous les globs ne correspondent pas pour que la commande soit annulée. Par exemple, s'il y a un fichier txt et aucun fichier html, cela devient:
ls -- file.txt
Vous pouvez obtenir ce comportement avec zsh
avec setopt cshnullglob
bien qu'une façon plus sensée de le faire dans zsh
serait d'utiliser un glob comme:
ls -- *.(txt|html)
Dans zsh
et ksh93
, vous pouvez également appliquer nullglob sur une base globale, ce qui est une approche beaucoup plus saine que de modifier un paramètre global:
files=(*.txt(N)) # zsh
files=(~(N)*.txt) # ksh93
créerait un tableau vide s'il n'y a pas de fichier txt
au lieu d'échouer la commande avec une erreur (ou d'en faire un tableau avec un *.txt
argument littéral avec d'autres shells).
Les versions de fish
antérieures à 2.3 fonctionneraient comme bash -O nullglob
mais donne un avertissement lorsqu'il est interactif lorsqu'un glob n'a pas de correspondance. Depuis 2.3, il fonctionne comme zsh
sauf pour les globes utilisés dans for
, set
ou count
.
Maintenant, sur la note d'historique, le comportement était en fait cassé par le Bourne Shell. Dans les versions précédentes d'Unix, la globalisation était effectuée via le /etc/glob
helper et que helper se comportait comme csh
: il échouerait la commande si aucun des globs ne correspondait à aucun fichier et supprimerait les globs sans correspondance sinon.
La situation dans laquelle nous nous trouvons aujourd'hui est donc due à une mauvaise décision prise dans le Bourne Shell.
Notez que le Bourne Shell (et le C Shell) est venu avec une autre nouvelle fonctionnalité Unix: l'environnement. Cela signifiait une expansion variable (son prédécesseur n'avait que le $1
, $2
... paramètres de position). Le Bourne Shell a également introduit la substitution de commandes.
Une autre mauvaise décision de conception du Bourne Shell a été d'effectuer un globbing (et un fractionnement) lors de l'expansion des variables et de la substitution de commande (éventuellement pour une compatibilité descendante avec le Thompson Shell où echo $1
invoquerait toujours /etc/glob
si $1
contenait des caractères génériques (cela ressemblait plus à une expansion de macro préprocesseur, car la valeur développée était à nouveau analysée comme code Shell)).
L'absence de globes qui ne correspondent pas signifierait par exemple que:
pattern='a.*b'
grep $pattern file
échouerait la commande (à moins qu'il y ait quelques a.whateverb
fichiers dans le répertoire courant). csh
(qui effectue également la globalisation lors de l'expansion variable) échoue à la commande dans ce cas (et je dirais que c'est mieux que de laisser un bogue dormant là-bas, même si ce n'est pas aussi bon que de ne pas faire de globalisation du tout comme dans zsh
).