web-dev-qa-db-fra.com

Pourquoi nullglob n'est-il pas par défaut?

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?

63
Dakkaron

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).

81