10 votes

L'extension avec *.txt dans la coquille ne fonctionne pas s'il n'existe aucun fichier .txt

J'ai joué avec l'expansion, et j'ai remarqué un comportement particulier. J'ai essayé de faire :

echo ./*.txt

Et je n'avais aucun fichier .txt dans mon répertoire actuel. Le résultat que j'ai obtenu était :

./*.txt

Je suis juste curieux : Pourquoi ai-je obtenu cela ? Je m'attendais à ne pas obtenir de résultat.

PS : Lorsque j'avais un fichier .txt, l'expansion était correctement interprétée. En d'autres termes, disons que j'avais un fichier, smthn.txt, l'echo a en effet renvoyé répertoire_actuel/smthn.txt.

15voto

Hailie Mathieson Points 113

Adaptation de la page manuel de l'interpréteur de commandes bash,

bash analyse chaque mot à la recherche des caractères *, ? et [. Si l'un de ces caractères apparaît, le mot est considéré comme un motif et remplacé par une liste de noms de fichiers triée par ordre alphabétique correspondant au motif. Si aucun nom de fichier correspondant n'est trouvé et que l'option shell nullglob n'est pas activée, le mot reste inchangé. Si l'option nullglob est activée et aucun correspondance n'est trouvée, le mot est supprimé.

Dans ce cas, je suppose que nullglob n'est pas activé, donc le mot reste inchangé - d'où la sortie que vous voyez.

14voto

Eliah Kagan Points 111731

Je m'attendais à ne pas obtenir de sortie.

Si nullglob était la valeur par défaut, de nombreuses commandes se comporteraient de manière assez inattendue, car il est (peut-être malheureusement) courant que les commandes traitent le cas de zéro arguments de nom de fichier de manière qualitativement différente du cas de un ou plusieurs arguments de nom de fichier.

Supposez que vous ayez activé nullglob (shopt -s nullglob) et que vous vous trouviez dans un répertoire où aucun fichier ne correspond à *.txt. Alors *.txt se développera en effet en rien - non pas un champ vide, mais pas de champ du tout - comme vous le pensiez. Mais cela aurait ces résultats :

  • ls *.txt listerait tous les fichiers dans le répertoire actuel (sauf les fichiers cachés), car c'est ce que fait ls lorsque vous ne lui passez aucun argument de nom de fichier.
  • cat *.txt lirait depuis l'entrée standard, car lorsque cat n'a pas d'arguments de nom de fichier, il est comme si vous aviez exécuté cat -. Si vous exécutez de manière interactive, il se met en attente d'une entrée. De nombreuses commandes se comportent ainsi.
  • cp *.txt dest/ échouerait avec l'erreur cp: opérande de fichier de destination manquante après 'dest/'. Ce n'est pas une catastrophe, mais c'est déroutant et très différent du succès silencieux qui est probablement souhaité.
  • file *.txt, et divers autres programmes sans comportement spécial pour le cas de zéro argument de nom de fichier, échoueraient toujours avec une erreur ou un message d'utilisation lorsqu'aucun n'est passé.
  • Même les cas qui semblent intuitivement devoir fonctionner n'auraient souvent pas le résultat attendu. printf 'Fichier obtenu : "%s"\n' *.txt imprimerait Fichier obtenu : "" au lieu de rien.
  • L'omission accidentelle de citer les occurrences de *, ? et [ qui ne sont pas censés être développés par le shell produirait plus souvent des résultats clairement erronés, mais de manière difficile à comprendre. Par exemple, si aucun nom de fichier dans le répertoire actuel ne commençait par gedit, alors apt list gedit* (où apt list 'gedit*' était prévu) deviendrait simplement apt list et listerait toutes les packages disponibles.

Il est donc bien que vous n'obteniez pas ce comportement sans le demander. Probablement la situation pratique la plus courante qui est effectivement simplifiée par nullglob est pour f in *.txt. Voir aussi cette question (à laquelle la réponse de Sergiy Kolodyazhnyy est liée).

La question plus difficile à répondre est pourquoi failglob - où c'est une erreur d'expansion d'avoir un glob qui ne correspond à aucun fichier - n'est pas la valeur par défaut dans bash. Je crois que la réponse de Sergiy Kolodyazhnyy capture la raison cela même sans l'aborder directement. Conserver des globs non développés sans produire d'erreur d'expansion est (peut-être malheureusement) le comportement standardisé, et c'est aussi un comportement traditionnel, et donc attendu. Bien que bash ne tente pas d'être entièrement conforme à POSIX sauf s'il est invoqué avec le nom sh ou passe l'option --posix, bon nombre de ses choix de conception même en dehors du mode POSIX suivent directement POSIX. Ils devaient choisir un comportement, et il y a des inconvénients associés à aller à l'encontre des attentes des utilisateurs.


Je pense que c'est l'aspect le moins historiquement influent de la question, c'est pourquoi je l'ai laissé pour la fin... mais il vaut la peine de mentionner qu'il y a quelque chose de conceptuellement étrange dans le comportement de nullglob.

nullglob semble élégant au premier abord car, syntaxiquement, il ne traite pas différemment le cas de zéro fichiers correspondants du cas de un, deux, ou de tout autre nombre. Les commandes que nous exécutons, en arguments auxquelles se développent les globs, ne tendent pas à les traiter de la même manière, comme détaillé ci-dessus. Mais syntaxiquement, cela semble au moins correct, ce qui je pense est la motivation de votre question.

Et pourtant, il y a une autre incohérence, plus subtile, que nullglob ne résout pas - qu'elle amplifie même. Le cas de zéro caractères de glob ("jokers") est traité de manière profondément différente de celui de un, ou deux, ou de tout autre nombre. Par exemple, avec shopt -s nullglob, si ab?d?f ne correspond à aucun fichier, il est supprimé ; si ab?d ne correspond à aucun fichier, il est supprimé ; mais si ab ne correspond à aucun fichier (c'est-à-dire s'il n'y a pas de fichier dont le nom est exactement ab), il n'est toujours pas supprimé. Bien sûr, ce serait une catastrophe s'il était supprimé, car il se peut qu'il ne soit pas censé faire référence à un fichier existant dans le répertoire actuel du tout ; il pourrait même ne pas faire référence à un fichier. Mais cela élimine tout espoir de cohérence totale.

Les trois comportements que bash offre - le comportement par défaut de traiter les globs qui ne correspondent à aucun fichier comme s'ils n'étaient pas des globs et de les transmettre non développés, le comportement que vous attendiez de les traiter (si vous permettez ce tour de phrase étrange) comme signifiant les zéro des fichiers qui correspondent (nullglob), et le comportement sûr de les considérer comme des erreurs (failglob) - représentent tous des approches différentes de l'ambiguïté inhérente au shell qui ne peut pas savoir si un mot donné est destiné à être un nom de fichier. Le shell effectue ses expansions sans savoir comment les commandes particulières que vous appelez avec lui vont traiter leurs arguments.

C'est l'un des nombreux exemples de séparation des préoccupations. Dans les systèmes dont la conception suit la philosophie Unix, chaque partie est censée faire une chose et la faire bien. Le shell traite le texte en commandes et arguments et invoque ces commandes, la plupart desquelles sont externes au shell lui-même. Cela a tendance à être beaucoup plus agréable et plus polyvalent que les systèmes où les commandes externes sont elles-mêmes responsables de réaliser ces transformations (comme avec les processeurs de commandes traditionnels dans DOS et Windows). Mais cela a ses inconvénients occasionnels.

6voto

Sergiy Kolodyazhnyy Points 97292

La raison principale est que c'est un comportement standard spécifié par POSIX - la norme qui couvre le langage de commande shell et, entre autres choses, la correspondance de motifs (les shells tels que bash, shell dash - le shell par défaut d'Ubuntu /bin/sh et ksh suivent cette norme). De la section 2.13.3 Modèles Utilisés pour l'Expansion de Noms de Fichiers :

Si le motif ne correspond à aucun nom de fichier ou chemin existant, la chaîne de motif est laissée inchangée.

Cela a bien sûr un effet secondaire - correspondance avec des noms de fichiers qui peuvent être littéralement *.txt. L'option nullglob dans bash et zsh peut aider : si cette option est activée via shopt -s nullglob (et elle n'est pas activée par défaut, ce qui s'applique à cette question), alors globstar sera étendu à une chaîne vide lorsque aucun nom de fichier correspondant n'est trouvé. ksh93 a son propre mécanisme avancé de correspondance de motifs qui atteint le même effet ~(N)*.txt

Voir aussi Pourquoi nullglob n'est-il pas la valeur par défaut ?

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X