Je suis confus au sujet d'un script bash.
J'ai le code suivant:
function grep_search() {
magic_way_to_define_magic_variable_$1=`ls | tail -1`
echo $magic_variable_$1
}
Je veux pouvoir créer un nom de variable contenant le premier argument de la commande et portant la valeur par exemple de la dernière ligne de ls
.
Donc pour illustrer ce que je veux:
$ ls | tail -1
stack-overflow.txt
$ grep_search() open_box
stack-overflow.txt
Alors, comment définir / déclarer $magic_way_to_define_magic_variable_$1
et comment l'appeler dans le script?
Je l' ai essayé eval
, ${...}
, \$${...}
, mais je suis toujours confus.
Réponses:
Utilisez un tableau associatif, avec des noms de commandes comme clés.
Si vous ne pouvez pas utiliser de tableaux associatifs (par exemple, vous devez prendre en charge
bash
3), vous pouvez utiliserdeclare
pour créer des noms de variables dynamiques:et utilisez l'expansion indirecte des paramètres pour accéder à la valeur.
Voir BashFAQ: Indirection - Évaluation des variables indirectes / de référence .
la source
-a
déclare un tableau indexé, pas un tableau associatif. Sauf si l'argument togrep_search
est un nombre, il sera traité comme un paramètre avec une valeur numérique (qui vaut par défaut 0 si le paramètre n'est pas défini).4.2.45(2)
et declare ne le liste pas comme optiondeclare: usage: declare [-afFirtx] [-p] [name[=value] ...]
. Il semble cependant fonctionner correctement.declare -h
dans 4.2.45 (2) pour moi montredeclare: usage: declare [-aAfFgilrtux] [-p] [name[=value] ...]
. Vous pouvez vérifier que vous exécutez réellement 4.x et non 3.2.declare $varname="foo"
?${!varname}
est beaucoup plus simple et largement compatibleJ'ai cherché une meilleure façon de le faire récemment. Le tableau associatif me paraissait excessif. Regarde ce que j'ai trouvé:
...puis...
la source
prefix_${middle}_postfix
(c.-à-d. votre formatage ne fonctionnerait pas pourvarname=$prefix_suffix
)Au-delà des tableaux associatifs, il existe plusieurs façons d'obtenir des variables dynamiques dans Bash. Notez que toutes ces techniques présentent des risques, qui sont discutés à la fin de cette réponse.
Dans les exemples suivants, je suppose que
i=37
vous souhaitez aliaser la variable nomméevar_37
dont la valeur initiale estlolilol
.Méthode 1. Utilisation d'une variable «pointeur»
Vous pouvez simplement stocker le nom de la variable dans une variable d'indirection, un peu comme un pointeur C. Bash a alors une syntaxe pour lire la variable aliasée: se
${!name}
développe à la valeur de la variable dont le nom est la valeur de la variablename
. Vous pouvez le considérer comme une expansion en deux étapes: se${!name}
développe en$var_37
, qui se développe enlolilol
.Malheureusement, il n'y a pas de syntaxe homologue pour modifier la variable aliasée. Au lieu de cela, vous pouvez réaliser une affectation avec l'une des astuces suivantes.
1a. Assigner avec
eval
eval
est le mal, mais c'est aussi le moyen le plus simple et le plus portable d'atteindre notre objectif. Vous devez soigneusement échapper au côté droit de la tâche, car elle sera évaluée deux fois . Une manière simple et systématique de le faire est d'évaluer le côté droit au préalable (ou de l'utiliserprintf %q
).Et vous devriez vérifier manuellement que le côté gauche est un nom de variable valide, ou un nom avec index (et si c'était le cas
evil_code #
?). En revanche, toutes les autres méthodes ci-dessous l'appliquent automatiquement.Inconvénients:
eval
est le mal.eval
est le mal.eval
est le mal.1b. Assigner avec
read
Le
read
builtin vous permet d'assigner des valeurs à une variable dont vous donnez le nom, un fait qui peut être exploité en conjonction avec des chaînes ici:La
IFS
pièce et l'option-r
s'assurent que la valeur est attribuée telle quelle, tandis que l'option-d ''
permet d'attribuer des valeurs multilignes. En raison de cette dernière option, la commande retourne avec un code de sortie différent de zéro.Notez que, puisque nous utilisons une chaîne ici, un caractère de nouvelle ligne est ajouté à la valeur.
Inconvénients:
1c. Assigner avec
printf
Depuis Bash 3.1 (sorti en 2005), le
printf
builtin peut également affecter son résultat à une variable dont le nom est donné. Contrairement aux solutions précédentes, cela fonctionne, aucun effort supplémentaire n'est nécessaire pour échapper aux choses, pour éviter le fractionnement, etc.Inconvénients:
Méthode 2. Utilisation d'une variable «référence»
Depuis Bash 4.3 (sorti en 2014), le
declare
builtin a une option-n
pour créer une variable qui est une «référence de nom» à une autre variable, un peu comme les références C ++. Tout comme dans la méthode 1, la référence stocke le nom de la variable aliasée, mais chaque fois que la référence est accédée (que ce soit pour la lecture ou l'affectation), Bash résout automatiquement l'indirection.En outre, Bash a une syntaxe spéciale et très déroutant pour obtenir la valeur de la référence elle - même, juge par vous - même:
${!ref}
.Cela n'évite pas les pièges expliqués ci-dessous, mais au moins cela rend la syntaxe simple.
Inconvénients:
Des risques
Toutes ces techniques d'aliasing présentent plusieurs risques. Le premier exécute du code arbitraire chaque fois que vous résolvez l'indirection (soit pour la lecture, soit pour l'affectation) . En effet, au lieu d'un nom de variable scalaire, comme
var_37
, vous pouvez aussi aliaser un indice de tableau, commearr[42]
. Mais Bash évalue le contenu des crochets à chaque fois que cela est nécessaire, donc l'aliasarr[$(do_evil)]
aura des effets inattendus ... Par conséquent, n'utilisez ces techniques que lorsque vous contrôlez la provenance de l'alias .Le deuxième risque est la création d'un alias cyclique. Comme les variables Bash sont identifiées par leur nom et non par leur portée, vous pouvez par inadvertance créer un alias pour elle-même (tout en pensant que cela alias une variable à partir d'une portée englobante). Cela peut se produire en particulier lors de l'utilisation de noms de variables communs (comme
var
). Par conséquent, n'utilisez ces techniques que lorsque vous contrôlez le nom de la variable aliasée .La source:
la source
${!varname}
technique nécessite une variable intermédiaire pourvarname
.L'exemple ci-dessous renvoie la valeur de $ name_of_var
la source
echo
s avec une substitution de commande (qui manque les guillemets) est inutile. De plus, une option-n
devrait être donnéeecho
. Et, comme toujours,eval
est dangereux. Mais tout cela est inutile puisque Bash plus sûr, la syntaxe plus claire et plus courte dans ce but:${!var}
.Cela devrait fonctionner:
la source
Cela fonctionnera aussi
Dans ton cas
la source
Selon BashFAQ / 006 , vous pouvez utiliser
read
avec ici la syntaxe de chaîne pour attribuer des variables indirectes:Usage:
la source
Utilisation
declare
Il n'est pas nécessaire d'utiliser des préfixes comme sur les autres réponses, ni sur les tableaux. Utilisez seulement
declare
, guillemets doubles , et l' expansion des paramètres .J'utilise souvent l'astuce suivante pour analyser les listes d'
one to n
arguments contenant des arguments au formatkey=value otherkey=othervalue etc=etc
, comme:Mais en élargissant la liste argv comme
Conseils supplémentaires
la source
printf
oueval
Wow, la plupart de la syntaxe est horrible! Voici une solution avec une syntaxe plus simple si vous avez besoin de référencer indirectement des tableaux:
Pour des cas d'utilisation plus simples, je recommande la syntaxe décrite dans le Advanced Bash-Scripting Guide .
la source
foo_1
etfoo_2
sont exemptes d'espaces et de symboles spéciaux. Exemples d'entrées problématiques:'a b'
créera deux entrées à l'intérieurmine
.''
ne créera pas d'entrée à l'intérieurmine
.'*'
s'étendra au contenu du répertoire de travail. Vous pouvez éviter ces problèmes en citant:eval 'mine=( "${foo_'"$i"'[@]}" )'
[@]
constructions."${array[@]}"
s'étendra toujours à la liste correcte d'entrées sans problèmes comme le fractionnement de mots ou l'expansion de*
. En outre, le problème de fractionnement de mot ne peut être contourné queIFS
si vous connaissez un caractère non nul qui n'apparaît jamais dans le tableau. En outre, le traitement littéral de*
ne peut pas être obtenu par réglageIFS
. Soit vous définissezIFS='*'
et vous divisez au niveau des étoiles, soit vous définissezIFS=somethingOther
et les*
élargissements.|
ouLF
comme IFS. Encore une fois, le problème général des boucles est que la création de jetons se produit par défaut, de sorte que les guillemets sont la solution spéciale pour autoriser les chaînes étendues contenant des jetons. (Il s'agit soit de globbing / extension de paramètres, soit de chaînes étendues entre guillemets, mais pas les deux.) Si 8 guillemets sont nécessaires pour lire une var, le shell n'est pas le bon langage.Pour les tableaux indexés, vous pouvez les référencer comme suit:
Les tableaux associatifs peuvent être référencés de la même manière mais nécessitent l'
-A
activation audeclare
lieu de-a
.la source
Une méthode supplémentaire qui ne dépend pas de la version de shell / bash dont vous disposez consiste à utiliser
envsubst
. Par exemple:la source
script.sh
fichier:Tester:
Selon
help eval
:Vous pouvez également utiliser l'
${!var}
expansion indirecte de Bash , comme déjà mentionné, mais elle ne prend pas en charge la récupération d'index de tableau.Pour plus d'informations ou des exemples, consultez BashFAQ / 006 sur l'indirection .
Cependant, vous devriez reconsidérer l'utilisation de l'indirection selon les notes suivantes.
la source
pour le
varname=$prefix_suffix
format, utilisez simplement:la source