Histoire de Bash globbing

11

Y a-t-il une raison historique pour laquelle le "globbing" de Bash et les expressions régulières ne sont pas identiques? Par exemple, je crois que dans Bash [1-2]*correspond à tout ce qui commence par un 1 ou un 2 suivi de quoi que ce soit d'autre, alors qu'une expression régulière [1-2]*ne correspondrait qu'à une séquence de 1 et de 2. Mes scripts Bash et REGEX foo sont tous deux assez faibles et je rencontre régulièrement des problèmes associés à ces différences qui m'ont rendu curieux de savoir pourquoi elles sont différentes.

StrongBad
la source
3
Envisageriez-vous de faire à la rm -- ^[^.].*\.txt$place de rm -- *.txt?
Stéphane Chazelas
1
La plupart de vos Q sont abordés dans ce fil de lwn: lwn.net/Articles/96687
slm
Il existe des commandes qui fonctionnent sur les noms de fichiers et prennent des expressions rationnelles. Par exemple, recherchez, find . -regex ".*\.txt$" | xargs rm --ou renamepour renommer des fichiers (c'est sedpour les noms de fichiers), méfiez-vous que certains systèmes ont un autre rename.
ctrl-alt-delor
@richard, mon ^[^.].*\.txt$devait prendre en compte l'ignorance des fichiers dot. Notez que la -regexest une extension de GNU, des coquilles comme ksh93 ou zsh peuvent incorporer des expressions rationnelles dans leurs petites boules (essayez par exemple: ksh93 -c 'echo ~(E:^[^.].*\.txt$)')
Stéphane Chazelas
2
Cette bash suit si soigneusement la pratique existante tout en évitant les changements et extensions irrémédiablement incompatibles est l'une de ses plus grandes forces.
ormaaj

Réponses:

12

basha été initialement conçu à la fin des années 80 comme un clone partiel de kshcertaines fonctionnalités interactives de csh / tcsh.

Les origines du globbing doivent être trouvées dans les coquilles antérieures sur lesquelles il s'appuie.

kshlui-même est une extension du shell Bourne. Le shell Bourne lui-même (sorti pour la première fois en 1979 dans Unix V7) était une implémentation propre à partir de zéro, mais il ne s'écartait pas complètement du shell Thompson (le shell de V1 -> V6) et incorporait des fonctionnalités du shell Mashey.

En particulier, les arguments de commande étaient toujours séparés par des blancs, |était désormais le nouvel opérateur de canal mais ^était toujours pris en charge comme alternative (et explique également pourquoi vous le faites [!a-z]ou non [^a-z]), $1était toujours le premier argument d'un script et la barre oblique inverse était toujours le caractère d'échappement. . De nombreux opérateurs regexp ( ^\|$) ont une signification particulière dans le shell.

La coque Thompson s'appuyait sur un utilitaire externe pour la globulation. Une fois shtrouvé sans guillemets *, [ou ?s dans la commande, il exécuterait la commande glob.

rm *.txt

finirait par exécuter glob comme:

["glob", "rm", "*.txt"]

et glob finirait par fonctionner rmavec la liste des fichiers correspondant à ce modèle.

grep a.\*b *.txt

se déroulerait globcomme:

["glob", "grep", "a.\252b", "*.txt"]

Ce qui *précède a été cité en définissant le 8e bit sur ce caractère, empêchant globde le traiter comme un caractère générique. globretirerait alors ce bit avant d'appeler grep.

Pour faire l'équivalent avec des expressions régulières, cela aurait été:

regexp rm '\.txt$'

Ou:

regexp rm '^[^.].*\.txt$'

pour exclure les fichiers dot.

La nécessité d'échapper aux opérateurs car ils se doublent de caractères spéciaux shell, le fait que ., commun aux noms de fichiers, est un opérateur regexp, il n'est pas très approprié de faire correspondre les noms de fichiers et compliqué pour un débutant. Dans la plupart des cas, vous n'avez besoin que de caractères génériques qui peuvent remplacer un ( ?) ou n'importe quel nombre ( *) de caractères.

Maintenant, différents shells ont ajouté différents opérateurs de globbing. De nos jours, les globes ksh et zsh (et dans une certaine mesure bash -O extglobqui implémentent un sous-ensemble de globes ksh) sont fonctionnellement équivalents aux regexps avec une syntaxe moins lourde à utiliser avec les noms de fichiers et la syntaxe shell actuelle. Par exemple, dans zsh(avec l'extension extendedglob), vous pouvez faire:

echo a#.txt

si vous voulez (peu probable) faire correspondre les noms de fichiers composés de séquences asuivies de .txt. Plus echo (^a*\.txt$)simple que (ici, utiliser des accolades comme moyen d'isoler les opérateurs regex des opérateurs shell qui auraient pu être à sens unique pour les shells).

echo (foo|bar|<1-20>).(#i)mpg

Pour les fichiers mpg (insensibles à la casse) dont le nom de base est foo, bar ou un nombre décimal de 1 à 20 ...

ksh93peut désormais incorporer des expressions rationnelles (de base, étendues, de type perl ou "augmentées") dans ses globes (bien que ce soit assez bogué) et fournit même un outil pour convertir entre glob et regexp ( printf %R, printf %P):

echo ~(Ei:.*\.txt)

à match (non caché) txt fichiers avec E Xtended expressions régulières, crémerie- i nsensitively.

Stéphane Chazelas
la source
Rédaction cool! Vous ne pouvez en fait utiliser ~(opt:pat)aucune des options capitalisées. Peut-être print -r -- ~(Ei).*\.txt$. Mettre le motif à l'intérieur ne semble utile que pour éviter d'avoir à activer ou désactiver une option pour une partie d'un motif. Curieusement, vous pouvez mélanger et assortir plusieurs langages de modèles dans le même glob. ~(Ki)*.~(E)txt$est équivalent. (Au final, tout est simplement converti en regex et passé au moteur de regex de libast en interne).
ormaaj
@ormaaj, ~(Ei:.*\.txt)fonctionne pour moi même avec des versions vieilles de 15 ans comme ksh93 o +.
Stéphane Chazelas
Fonctionne également avec l'un de mes binaires de test enregistrés (24/12/2014), mais je me souviens avoir rencontré des problèmes avec cela. Les choses étaient toujours brisées au hasard et réparées à nouveau entre chaque version lorsque ksh était encore commercialement développé. Je me souviens que le code de correspondance de motifs était l'une des zones fragiles.
ormaaj
@ormaaj, une différence entre ~(E)xet ~(E:x)est que ce dernier est ancré (correspond xuniquement sur tandis que l'ancien correspond à tout ce qui contient x), ce qui peut être le type de problème que vous avez rencontré (utilisez ~(-lr)~(E:x)pour supprimer l'ancrage, ~(E-lr:x)ne fera pas). En tout cas, je suis d'accord, c'est assez buggé, même dans la dernière version.
Stéphane Chazelas
9

Les langues régulières ont été introduites par Kleene en 1956. Le papier séminal n'avait pas la notation moderne complète pour les expressions régulières, mais il a introduit "l'étoile de Kleen": A*ce qui signifie "n'importe quel nombre de répétitions de A". Au cours de la décennie suivante, des notations plus ou moins standard sont apparues, en particulier .pour un caractère arbitraire et ?pour signifier que le caractère précédent est facultatif.

La notation de globbing de Bash découle de la globcommande introduite tout au long d' Unix v1 en 1971. À l'époque, le globbing était effectué par un programme distinct; il a ensuite été déplacé dans la coquille. La première globcommande doit ?signifier «n'importe quel caractère» et *signifier «n'importe quelle séquence de caractères». Je ne sais pas pourquoi les personnages ont été choisis; ?est assez intuitif et *peut avoir été inspiré de celui des expressions régulières.

Les globes n'étaient pas destinés à être aussi généraux que les expressions régulières, et les expressions régulières n'étaient pas très répandues à l'époque, il n'y avait donc aucun appel à unifier les concepts. Dès le début, il y avait des incompatibilités syntaxiques, avec ?, .et qui *signifie des choses différentes dans les modèles de noms de fichiers et dans les expressions régulières.

Les coques modernes telles que bash se développent sur des modèles globaux, mais c'était une évolution progressive maintenant la compatibilité descendante. Ksh88 (la version 1988 du shell Korn ) a introduit une syntaxe étendue pour les modèles de shell, qui ne pouvait pas être la même syntaxe que les expressions régulières habituelles mais qui s'en inspirait fortement: *(PATTERN)signifier n'importe quel nombre de répétitions de PATTERN, @(PATTERN1|PATTERN2)signifier « PATTERN1ou PATTERN2», etc.

Les versions modernes de bash (depuis 2.02) prennent en charge les modèles étendus de ksh88, si vous émettez en shopt -s extglobpremier.

Gilles 'SO- arrête d'être méchant'
la source
Bash n'a-t-il jamais pris en charge les extglobs? Pour autant que je sache, Bash, zsh et {pd, m} ksh prennent en charge les mêmes globes que ceux décrits dans le manuel ksh88 depuis les premiers jours. Ksh à ce jour n'a même pas d'option pour désactiver les quantificateurs globaux "étendus", et ksh93 est le seul du groupe à avoir des extensions au-delà de ce que ksh88 avait.
ormaaj
2
@ormaaj Ksh88 a étendu les globes et l' extgloboption a été introduite dans bash 2.02 vers 1998. Zsh a acquis ksh_globdans la série 3.1 quelque part à la même époque. Zsh a ses propres extensions globbing (certaines nécessitant l' extended_globoption).
Gilles 'SO- arrête d'être méchant'
Je vois. Il était donc en fait assez tard pour justifier la nécessité d'une option. (Je pense que la désactivation par défaut est plutôt inutile de nos jours mais, intéressante.)
ormaaj
1
@ormaaj, notez que bash, contrairement à ksh, extglob rend bash non compatible POSIX car il n'est pas désactivé dans les variables. Dans ksh, var='@(*)'; echo $vars'étend à tous les noms de fichiers dans le répertoire en cours qui commencent @(et se terminent )comme POSIX l'exige tandis que dans bash -O extglobil s'étend à tous les fichiers. (encore, on peut considérer que le comportement bash a plus de sens ici (et le comportement ksh est assez pénible lorsque vous voulez avoir des modèles dans les variables)). Cette syntaxe de glob est si maladroite à cause de cela (compatibilité POSIX / Bourne). Comparez avec les globes étendus zsh.
Stéphane Chazelas
@ StéphaneChazelas C'est vrai, et j'aime à quel point ksh est un peu intelligent à ce sujet. Il arrive rarement à jouer, sauf s'il est réellement contraint à POSIX. Avec presque toutes les utilisations du fractionnement de mots remplacées par de meilleures fonctionnalités, et le stockage des modèles dans les variables étant de toute façon une nuisance extrême car vous devez vider IFS, désactivez l'expansion d'accolade partout sauf bash. Je pense qu'il est toujours impossible d'être complètement en sécurité avec des modèles stockés. Ce vieux problème d'évasion n'a jamais été vraiment résolu par exemple.
ormaaj
1

Raison historique: OUI. Référence:
http://en.wikipedia.org/wiki/Glob_(programming)#Origin

Juste pour montrer la divergence, voici un bon et simple exemple: a*

  • globbing shell: le sens est, le premier caractère est aet puis quoi que ce soit (a, ab, abca ...)
  • regex: la signification est, zéro ou plusieurs répétitions de caractère a(a, aa, aaa ...)

Je conviens volontiers que cette divergence de sens est très déroutante pour les nouveaux utilisateurs.

La globalisation est peut-être plus facile à saisir pour les nouveaux arrivants, mais c'est aussi une construction moins puissante.

fgeorgatos
la source