Je me trouve dans un répertoire dans lequel j'ai deux fichiers texte:
$ touch test1.txt
$ touch test2.txt
Lorsque j'essaie de répertorier les fichiers (avec Bash) en utilisant un modèle, cela fonctionne:
$ ls test?.txt
test1.txt test2.txt
$ ls test{1,2}.txt
test1.txt test2.txt
Cependant, lorsqu'un modèle est produit par une commande incluse dans $()
, un seul des modèles fonctionne:
$ ls $(echo 'test?.txt')
test1.txt test2.txt
$ ls $(echo 'test{1,2}.txt')
ls: cannot access test{1,2}.txt: No such file or directory
Que se passe t-il ici? Pourquoi le motif {1,2}
ne fonctionne pas?
bash
bash-expansion
Herosław Miraszewski
la source
la source
?
est également cité et est développé après son$(...)
remplacement, mais l'expansion d'accolade ne le fait pas.$
-expansions:zsh -o globsubst -c 'a=/e*; b={/b*,/v*}; echo $a; echo $b'
.Réponses:
C'est une combinaison de deux choses. Tout d'abord, l'expansion d'accolade n'est pas un modèle qui correspond aux noms de fichiers: c'est une substitution purement textuelle - voir Quelle est la différence entre `a [bc] d` (crochets) et` a {b, c} d` (accolades)? . Deuxièmement, lorsque vous utilisez le résultat d'une substitution de commande en dehors des guillemets doubles (
ls $(…)
), ce qui se produit est uniquement la correspondance de modèles (et la division de mots: l'opérateur «split + glob»), pas une nouvelle analyse complète.Avec
ls $(echo 'test?.txt')
, la commandeecho 'test?.txt'
sort la chaînetest?.txt
(avec un retour à la ligne final). La substitution de commande entraîne la chaînetest?.txt
(sans retour à la ligne final, car la substitution de commande supprime les retours à la ligne de fin). Cette substitution sans guillemets subit un fractionnement de mots, produisant une liste constituée de la chaîne uniquetest?.txt
car il n'y a pas de caractères d'espacement (plus précisément, pas de caractères$IFS
dedans). Chaque élément de cette liste à un élément subit ensuite une expansion générique conditionnelle, et comme il y a un caractère générique?
dans la chaîne, l'expansion générique se produit. Étant donné que le modèletest?.txt
correspond à au moins un nom de fichier, l'élément de listetest?.txt
est remplacé par la liste des noms de fichiers qui correspondent aux modèles, produisant la liste à deux éléments contenanttest1.txt
ettest2.txt
. Enfinls
est appelé avec deux argumentstest1
ettest2
.Avec
ls $(echo 'test{1,2}')
, la commandeecho 'test{1,2}'
sort la chaînetest{1,2}
(avec un retour à la ligne final). La substitution de commande entraîne la chaînetest{1,2}
. Cette substitution sans guillemets subit un fractionnement de mots, produisant une liste composée de la chaîne uniquetest{1,2}
. Chaque élément de cette liste à un élément subit ensuite une expansion conditionnelle générique, ce qui ne fait rien (l'élément est laissé tel quel) car il n'y a pas de caractère générique dans la chaîne. Ainsils
est appelé avec l'argument uniquetest{1,2}
.À titre de comparaison, voici ce qui se passe avec
ls $(echo test{1,2})
. La commandeecho test{1,2}
génère la chaînetest1 test2
(avec un retour à la ligne final). La substitution de commande entraîne la chaînetest1 test2
(sans un retour à la ligne final). Cette substitution sans guillemets subit une séparation de mots, produisant deux chaînestest1
ettest2
. Puis, comme aucune des chaînes ne contient de caractère générique, elles sont laissées seules, elles sont doncls
appelées avec deux argumentstest1
ettest2
.la source
.txt
dans la deuxième explication.L'expansion de l'accolade ne se produira pas après la substitution de commande. Vous pouvez utiliser eval pour forcer un autre cycle d'expansion:
Son résultat est:
la source
Ce problème est très spécifique à
bash
, et c'est parce qu'ils ont décidébash
de séparer l'expansion de l'accolade de l'expansion du nom de fichier (globbing), et de l'exécuter en premier, avant toutes les autres extensions.Depuis la
bash
page de manuel:Dans votre exemple,
bash
ne verra vos accolades qu'après avoir effectué la substitution de commande (la$(echo ...)
), lorsqu'il est trop tard.Ceci est différent de tous les autres shells, qui effectuent l'expansion de l'accolade juste avant (et certains même dans le cadre de) l'expansion du nom de chemin (globbing). Cela comprend, sans s'y limiter,
csh
où les extensions de corset ont été inventées pour la première fois.Ce dernier exemple est le même
csh
,zsh
,ksh93
,mksh
oufish
.Notez également que l'expansion d'accolade dans le cadre de la globalisation est également disponible via la
glob(3)
fonction de bibliothèque (au moins sur Linux et tous les BSD), et dans d'autres implémentations indépendantes (par exemple, dans perl:)perl -le 'print join " ", <test{1,2}.txt>'
.Pourquoi cela a été fait différemment dans
bash
a probablement une histoire derrière elle, mais FWIW Je n'ai pas pu trouver d'explication logique, et je trouve toutes les rationalisations post-hoc peu convaincantes.la source
perl
utilisé pour invoquer l'csh
expansion des globes, il n'est donc pas surprenant qu'il reconnaisse toujours les mêmes opérateurs de globbing quecsh
S'il vous plaît essayez:::
ls $ (test d'écho {1,2} \. txt)
Avec une barre oblique inverse. Ça fonctionne maintenant. Supprimez également ce que l'affiche précédente, les citations. Le point n'est pas pour le motif correspondant, mais doit être pris littéralement comme période ici.
la source
{1,2}
se comporte-t-il comme il le fait? La question ne demande pas "Comment puis-je obtenir une commande en utilisant{1,2}
pour se comporter comme la commande?
fonctionne?" Vous répondez à la mauvaise question. (2) La barre oblique inverse n'a rien à voir avec cela. Votre commande fonctionne comme elle le fait car vous avez supprimé les guillemets qui se trouvaient dans la commande dans la question.Cela fonctionne si vous supprimez les guillemets
la source
?
).