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

61

Dans la plupart des coquilles, ce nullglobn'est pas la valeur par défaut. Cela signifie, par exemple, si vous exécutez cette commande

ls *

dans un répertoire vide, le *glob sera étendu à un littéral *, à une liste d'arguments vide. Il existe des moyens de modifier ce comportement afin que, *dans un répertoire vide, une liste d'arguments vide soit retournée, ce qui semblerait plus intuitif.

Alors, y a-t-il une raison pour laquelle il nullglobest désactivé par défaut? Si oui, quelle est cette raison?

Dakkaron
la source
13
Une fois, quelqu'un a fait un mauvais choix qui s'est transformé en "nous l'avons toujours fait de cette façon". Un phénomène très répandu (pas seulement) dans le monde du logiciel.
PSkocik
4
La raison initiale est que l'option nullglob n'existait pas à l'époque. Donc, pour maintenir la compatibilité ascendante, il doit être désactivé par défaut.
mardi 2
3
Bien qu'elle ne mentionne pas spécifiquement le glob glob nul, une partie de l'historique sur le globbing est fournie par cette réponse: unix.stackexchange.com/a/136409/22724
StrongBad
4
J'ai l'impression que certains pensent qu'il devrait être évident que nullglob devrait être activé par défaut. Je ne pense pas que ce soit évident. Que des extensions se produisent dans le shell avant l’appel de commande signifie que développer à rien est un comportement moins intuitif que le glob restant inchangé.
kojiro
2
@kojiro Moins intuitif pour qui? Toute personne familiarisée avec les shell * NIX sait qu'il *s'agit d'un glob et s'étend à tous les fichiers existants ; comment est-il "intuitif" pour qu'il y ait un cas particulier où les globs de répertoire vides sont "étendus" à un littéral *?
Kyle Strand

Réponses:

78

L’ nullgloboption (BTW étant une zshinvention ajoutée seulement des années plus tard à bash( 2.0)) ne serait pas idéale dans un certain nombre de cas. Et lsest un bon exemple:

ls *.txt

Ou son équivalent plus correct:

ls -- *.txt

Avec nullglobon serait exécuté lssans argument considéré comme ls -- .(liste le répertoire en cours) si aucun fichier ne correspond, ce qui est probablement pire que d'appeler lsavec un littéral *.txtcomme argument.

Vous auriez des problèmes similaires avec la plupart des utilitaires de texte:

grep foo *.txt

Chercherais foosur stdin s'il n'y a pas de txtfichier.

Un choix plus judicieux, et celui de csh, tcsh, zsh ou fish 2.3+ (et des premiers shells Unix) consiste à annuler la commande si le glob ne correspond pas.

bash(depuis la version 3) a une failgloboption pour cela (intéressant pour cette discussion, car contrairement à ashAT & T kshou zsh, bashne supporte pas les étendues locales pour les options (bien que cela change dans la version 4.4), cette option, lorsqu'elle est activée globalement, casse quelques problèmes comme les fonctions d'achèvement de bash).

Notez que csh et tcsh sont légèrement différentes de zsh, fishou bash -O failglobdans 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 existe un fichier txt et aucun fichier html, cela devient:

ls -- file.txt

Vous pouvez obtenir ce comportement avec zshavec setopt cshnullglobbien d' une manière plus raisonnable de le faire en zshserait d'utiliser un glob comme:

ls -- *.(txt|html)

Dans zshet 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 avait pas de txtfichier au lieu d'échouer la commande avec une erreur (ou d'en faire un tableau avec un *.txtargument littéral avec d'autres shells).

Les versions fishantérieures à 2.3 fonctionneraient de la même façon, bash -O nullglobmais donneraient un avertissement lorsqu'elles sont interactives lorsqu'un glob n'a pas de correspondance Depuis la version 2.3, cela fonctionne comme zshsauf pour les globs utilisés dans for, setou count.

Sur la note d’histoire, le comportement a en fait été interrompu par le shell Bourne. Dans les versions précédentes d'Unix, la globulation était effectuée via l' /etc/globassistant et ce dernier se comportait comme suit csh: la commande échouerait si aucun des globs ne correspondait à un fichier et supprimait les globs sans correspondance sinon.

La situation dans laquelle nous nous trouvons aujourd’hui est donc due à une mauvaise décision prise dans le shell Bourne.

Notez que le shell Bourne (et le shell C) est accompagné d’une autre nouvelle fonctionnalité Unix: l’environnement. Cette expansion signifiait variable (son prédécesseur avait seulement les $1, $2... les paramètres de position). Le shell Bourne a également introduit la substitution de commande.

Une autre décision d' une mauvaise conception de la coque Bourne était de réaliser globbing (et le fractionnement) lors de l'expansion des variables et la substitution de commande (éventuellement pour une compatibilité descendante avec la coque Thompson où echo $1invoquerais encore /etc/globsi les $1caractères génériques contenus (il a été plus comme extension macro pré-processeur là, comme dans la valeur développée a été analysé à nouveau comme un code shell)).

Des globs défaillants qui ne correspondent pas signifieraient par exemple que:

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

échouerait la commande (à moins que certains a.whateverbfichiers ne se trouvent dans le répertoire en cours). cshDans ce cas, la commande échoue (et je dirais que c'est mieux que de laisser un bogue en sommeil ici, même s'il n'est pas aussi bon que de ne pas faire de globing du tout comme dans zsh).

Stéphane Chazelas
la source
Il existe un autre problème d’utilisabilité: nullglobsemble rompre l’achèvement de la tabulation (appuyer sur la touche de tabulation ne fait rien quand il est activé).
Kyle Strand
1
Il est possible d'utiliser nullglob sur un par glob base avec bash - bien que la syntaxe est pas aussi élégant que zshfiles=$(shopt -s nullglob;echo *.txt)
Jon Nalley
2
@JonNalley, qui stocke la concaténation (avec espace) des noms de fichiers (avec transformation possible avec xpg_echo) en variables scalaires . Vous aurez besoin de quelque chose comme readarray -td '' files < <(shopt -s nullglob; printf '%s\0' *.txt)avec bash4.4 ou plus ou (shopt -s nullglob; printf '%s\0' *.txt) | xargs -r0 cmdavec GNU xargspour que cela soit utilisable avec des noms de fichiers arbitraires. Ou, toujours avec bash4.4, utilisez une fonction d'assistance qui utilise local -(copiée à partir de cendres 25 ans plus tard) un champ local pour les options.
Stéphane Chazelas