web-dev-qa-db-fra.com

Comment affecter une expression glob à une variable dans un script 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 que la variable passe entre guillemets simples (ce qui empêcherait la suppression):

+ 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), les deux répertoires sont renvoyés.

J'ai lu le Manuel de référence de Bash (v 3.2) et je ne vois aucune raison pour que la suppression du nom de fichier ne se produise pas (je ne passe pas -f au shell), globbing arrive.

41
kdgregory

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

L'ordre des extensions est le suivant: brace expansion, développement du tilde, paramètre, variable et expansion arithmétique et substitution de commande (effectuée de la manière suivante: de gauche à droite), Word fractionnement et pathname expansion.

Donc, si votre variable est substituée, l'expansion d'accolade n'a plus lieu. Cela fonctionne pour moi:

eval ls $dirs

Soyez très prudent avec eval. Il exécutera les choses textuellement. Donc si dirs contient f{m,k}t*; some_command, une_commande sera exécutée une fois que ls est terminé. Il exécutera la chaîne que vous avez donnée à eval dans le shell en cours. Il transmettra /content/dev01 /content/dev02 à ls, qu’ils existent ou non. Mettre * après le truc en fait un chemin d’expansion, et les chemins inexistants seront omis:

dirs=/content/{dev01,dev02}*

Je ne suis pas sûr à 100%, mais cela me semble logique.

30

Ici est une excellente discussion de 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.

21
feoh

Ce n'est pas une annotation de nom de fichier, c'est une extension d'accolade .. La différence est subtile, mais elle existe - dans cette opération, vous ne recevrez que les fichiers existants, tandis que dans 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

Maintenant, voici ce qui a fonctionné pour moi:

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

Pour les gens (comme moi) qui trouvent ceci via Google, les réponses de @Peter et @ feoh constituent la solution générale à "Comment globaliser des variables dans un script bash". 

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, des espaces et tous.

Vous pouvez accéder à chaque résultat sous la forme "${list_of_results[<index>]}" pour <index> à partir de 0. Vous pouvez obtenir la liste complète correctement citée sous la forme "${list_of_results[@]}"

4
cxw

Je suppose que ce dont vous avez besoin est un tableau, mais cela vous limitera à de nouveaux bashes. C'est plus économique que d'utiliser eval.

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

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

/content/{dev01,dev02}

va s'étendre à:

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

L'existence de ces répertoires est sans rapport avec l'expansion.

Cela devient imprévisible lorsque vous affectez une variable à une extension 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 de quelque manière que ce soit, elles ne s'agrandiront pas. Le résultat contiendra donc les accolades et n'aura pour la plupart aucune signification.

2
Peter

Puisque vous voulez glob files, vous ne devriez pas utiliser de développement d'accolades. Dans ce cas, utiliser l’extension d'accolade est un antipattern et est certainement le mauvais outil pour le travail.

Ce que vous voulez, c'est étendu globbing :

shopt -s extglob # likely already set in interactive shells

dirs=/content/@(dev01|dev02)
ls $dirs
1
gniourf_gniourf
ls `echo $dirs` 

travaille sous cygwin. 

0
Zsolt Botykai

Le problème que personne n'a abordé est que l'assignation de variable fait la différence.

dirs=/content/{dev01,dev02}

se dilate différemment de

echo /content/{dev01,dev02}

La question est de savoir comment affecter les résultats de l'extension à dirs

Voir: Comment utiliser la substitution Bash dans une déclaration de variable

0
Sam Liddicott