52 votes

Comment assigner une expression glob à une variable dans un script de Bash ?

Lorsque les deux lignes de code suivantes sont exécutées dans un script bash, "ls" se plaint que les fichiers n'existent pas :

dirs=/content/{dev01,dev02}
ls -l $dirs

Lorsque j'exécute le script avec l'option -x, il semble passer la variable entre guillemets simples (ce qui empêcherait le globbing) :

+ dirs=/content/{dev01,dev01}
+ ls -l '/content/{dev01,dev01}'
ls: /content/{dev01,dev01}: No such file or directory

Si j'exécute la commande "ls" à partir de mon shell interactif (sans guillemets), elle renvoie les deux répertoires.

J'ai lu le manuel de référence de Bash (v 3.2) et je ne vois aucune raison pour que le globbing des noms de fichiers n'ait pas lieu (je ne passe pas -f à l'interpréteur de commandes), ou quoi que ce soit que je puisse définir pour m'assurer que le globbing a lieu.

31voto

Johannes Schaub - litb Points 256113

Je pense que c'est l'ordre des expansions :

L'ordre des expansions est le suivant : brace expansion , tilde expansion, paramètre, variable et l'expansion arithmétique et substitution de commande (effectuée de de gauche à droite), le séparation des mots, et pathname expansion .

Ainsi, si votre variable est substituée, l'expansion des accolades n'a plus lieu. Cela fonctionne pour moi :

eval ls $dirs

Soyez très prudent avec eval. Il exécutera le contenu textuellement. Donc si dirs contient f{m,k}t*; some_command une certaine_commande sera exécutée après la fin de l'opération ls. Elle exécutera la chaîne que vous donnez à eval dans le shell actuel. Il passera /content/dev01 /content/dev02 à ls, qu'ils existent ou non. Placer * après le truc en fait une expansion de chemin, et il omettra les chemins inexistants :

dirs=/content/{dev01,dev02}*

Je n'en suis pas sûr à 100%, mais ça me paraît logique.

1 votes

L'expansion des bracelets se produit avant l'expansion des variables

0 votes

Oui, c'est la raison. lorsque la variable est étendue, l'expansion de l'accolade n'a plus lieu, puisqu'elle a déjà eu lieu avant que la variable ne soit étendue.

2 votes

Si c'était le cas, je me serais attendu à ce que "dirs" contienne une liste de mots résultant de l'expansion des accolades, qui peuvent ou non s'appliquer au système de fichiers réel - mais cela ne semble pas se produire

31voto

feoh Points 621

Aquí est une excellente discussion sur ce que vous essayez de faire.

La réponse courte est que vous voulez un tableau :

dirs=(/content/{dev01,dev01})

Mais ce que vous faites avec les résultats peut devenir plus complexe que ce que vous visiez, je pense.

0 votes

J'ai vu ce fil de discussion, mais ça n'a pas semblé m'aider. Le problème vient de la commande suivante "ls ${dirs}", qui ne semble obtenir que la première entrée du tableau. J'ai également essayé "set ${dirs};ls $*" mais le résultat est le même.

5 votes

Vous devez faire ls "${dirs[@]}" . mais il développera les noms au moment de l'affectation aux répertoires. ainsi les répertoires contiendront réellement les noms de fichiers, pas seulement le motif

1 votes

Le nouveau lien est mort

17voto

cxw Points 872

Pour les gens (comme moi) qui ont trouvé ceci par Google, les réponses de @Peter et @feoh sont la solution générale à "Comment globaliser les variables dans bash script".

list_of_results=(pattern)

sauvegardera les noms de fichiers existants correspondant à pattern dans le tableau list_of_results . Chaque élément de list_of_results contiendra un nom de fichier, espaces et tout.

Vous pouvez accéder à chaque résultat comme "${list_of_results[<index>]}" para <index> en commençant par 0. Vous pouvez obtenir la liste complète, correctement citée, sous la forme suivante "${list_of_results[@]}" .

9voto

Yoni Roit Points 11338

Ce n'est pas un globage de nom de fichier, c'est une expansion d'accolades. La différence est subtile, mais elle existe - dans le cas du globbing de nom de fichier, vous ne recevriez que des fichiers existants comme résultat, alors que dans le cas de l'expansion d'accolade, vous pouvez générer n'importe quel type de chaîne.

http://www.gnu.org/software/bash/manual/bashref.html#Brace-Expansion

http://www.gnu.org/software/bash/manual/bashref.html#Filename-Expansion

Voilà ce qui a marché pour moi :

#!/bin/sh
dirs=`echo ./{dev01,dev02}`
ls $dirs

0 votes

Alors vous pourriez faire dirs=( ./{dev01,dev02} ) ; ce que feoh dit. mais cela s'étendra au moment de l'affectation. ce qui n'est peut-être pas ce qu'il veut.

3voto

Peter Points 21

Je pense que ce qu'il vous faut, c'est un tableau, mais cela vous limitera aux bashes les plus récents. C'est plus sûr que d'utiliser eval.

dirs=( /"content with spaces"/{dev01,dev02} )

dirs=( /content/{dev01,dev02} )
ls -l "${dirs[@]}"

/content/{dev01,dev02}

s'étendra à :

"/content/dev01" "/content/dev02"

L'existence de ces répertoires n'est pas pertinente pour l'expansion.

Il devient imprévisible lorsque vous assignez une variable à une expansion d'accolade.

dirs=/content/{dev01,dev02}

peut se transformer en

"/content/dev01"

ou

"/content/dev01 /content/dev02"

ou

"/content/dev01" "/content/dev02"

ou

"/content/{dev01,dev02}"

Si vous citez les accolades d'une manière ou d'une autre, elles ne se développeront pas, de sorte que le résultat contiendra les accolades et sera pratiquement dépourvu de sens.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X