Mise à jour: certaines personnes disent qu'il ne faut jamais utiliser eval. Je ne suis pas d'accord. Je pense que le risque survient lorsque des entrées corrompues peuvent être transmises eval
. Cependant, il existe de nombreuses situations courantes dans lesquelles ce n'est pas un risque, et il vaut donc la peine de savoir comment utiliser eval dans tous les cas. Cette réponse de stackoverflow explique les risques de eval et les alternatives à eval. En fin de compte, il appartient à l'utilisateur de déterminer si / quand eval est sûr et efficace à utiliser.
Le bash eval
instruction vous permet d'exécuter des lignes de code calculées ou acquises, par votre script bash.
L'exemple le plus simple serait peut-être un programme bash qui ouvre un autre script bash sous forme de fichier texte, lit chaque ligne de texte et utilise eval
pour les exécuter dans l'ordre. C'est essentiellement le même comportement que le bashsource
instruction , qui est celle que l'on utiliserait, à moins qu'il ne soit nécessaire d'effectuer une sorte de transformation (par exemple, filtrage ou substitution) sur le contenu du script importé.
J'en ai rarement eu besoin eval
, mais j'ai trouvé utile de lire ou d'écrire des variables dont les noms étaient contenus dans des chaînes affectées à d'autres variables. Par exemple, pour effectuer des actions sur des ensembles de variables, tout en conservant une faible empreinte de code et en évitant la redondance.
eval
est conceptuellement simple. Cependant, la syntaxe stricte du langage bash et l'ordre d'analyse de l'interpréteur bash peuvent être nuancés et donner l' eval
impression d'être cryptiques et difficiles à utiliser ou à comprendre. Voici l'essentiel:
L'argument passé à eval
est une expression de chaîne calculée au moment de l'exécution. eval
exécutera le résultat analysé final de son argument comme une ligne de code réelle dans votre script.
La syntaxe et l'ordre d'analyse sont stricts. Si le résultat n'est pas une ligne exécutable de code bash, dans la portée de votre script, le programme plantera sur l' eval
instruction lorsqu'il essaiera d'exécuter des ordures.
Lors du test, vous pouvez remplacer l' eval
instruction par echo
et voir ce qui est affiché. S'il s'agit d'un code légitime dans le contexte actuel, son exécution eval
fonctionnera.
Les exemples suivants peuvent aider à clarifier le fonctionnement de eval ...
Exemple 1:
eval
l'instruction devant le code 'normal' est un NOP
$ eval a=b
$ eval echo $a
b
Dans l'exemple ci-dessus, les premières eval
instructions n'ont aucun but et peuvent être éliminées. eval
est inutile dans la première ligne car il n'y a pas d'aspect dynamique dans le code, c'est-à-dire qu'il est déjà analysé dans les dernières lignes du code bash, donc il serait identique à une instruction normale de code dans le script bash. Le 2ème eval
est également inutile, car, bien qu'il y ait une étape d'analyse convertissant $a
en son équivalent de chaîne littérale, il n'y a pas d'indirection (par exemple, pas de référencement via la valeur de chaîne d'un nom bash réel ou d'une variable de script tenue par bash), donc il se comporterait de la même manière comme une ligne de code sans le eval
préfixe.
Exemple 2:
Effectuez une affectation var en utilisant des noms var passés sous forme de valeurs de chaîne
$ key="mykey"
$ val="myval"
$ eval $key=$val
$ echo $mykey
myval
Si vous le faisiez echo $key=$val
, le résultat serait:
mykey=myval
Cela , étant le résultat final de l'analyse de chaîne, c'est ce qui sera exécuté par eval, d'où le résultat de l'instruction echo à la fin ...
Exemple 3:
Ajout de plus d'indirection à l'exemple 2
$ keyA="keyB"
$ valA="valB"
$ keyB="that"
$ valB="amazing"
$ eval eval \$$keyA=\$$valA
$ echo $that
amazing
Ce qui précède est un peu plus compliqué que l'exemple précédent, s'appuyant davantage sur l'ordre d'analyse et les particularités de bash. La eval
ligne serait à peu près analysée en interne dans l'ordre suivant (notez que les instructions suivantes sont du pseudocode, pas du vrai code, juste pour essayer de montrer comment l'instruction serait décomposée en étapes en interne pour arriver au résultat final) .
eval eval \$$keyA=\$$valA # substitution of $keyA and $valA by interpreter
eval eval \$keyB=\$valB # convert '$' + name-strings to real vars by eval
eval $keyB=$valB # substitution of $keyB and $valB by interpreter
eval that=amazing # execute string literal 'that=amazing' by eval
Si l'ordre d'analyse supposé n'explique pas suffisamment ce que fait eval, le troisième exemple peut décrire l'analyse plus en détail pour aider à clarifier ce qui se passe.
Exemple 4:
Découvrez si les variables, dont les noms sont contenus dans des chaînes, contiennent elles-mêmes des valeurs de chaîne.
a="User-provided"
b="Another user-provided optional value"
c=""
myvarname_a="a"
myvarname_b="b"
myvarname_c="c"
for varname in "myvarname_a" "myvarname_b" "myvarname_c"; do
eval varval=\$$varname
if [ -z "$varval" ]; then
read -p "$varname? " $varname
fi
done
Dans la première itération:
varname="myvarname_a"
Bash analyse l'argument de eval
, et eval
voit littéralement ceci au moment de l'exécution:
eval varval=\$$myvarname_a
Le pseudo- code suivant tente d'illustrer comment bash interprète la ligne de code réel ci-dessus , pour arriver à la valeur finale exécutée par eval
. (les lignes suivantes descriptives, pas de code bash exact):
1. eval varval="\$" + "$varname" # This substitution resolved in eval statement
2. .................. "$myvarname_a" # $myvarname_a previously resolved by for-loop
3. .................. "a" # ... to this value
4. eval "varval=$a" # This requires one more parsing step
5. eval varval="User-provided" # Final result of parsing (eval executes this)
Une fois que toute l'analyse est terminée, le résultat est ce qui est exécuté, et son effet est évident, démontrant qu'il n'y a rien de particulièrement mystérieux en eval
lui-même, et la complexité réside dans l' analyse de son argument.
varval="User-provided"
Le code restant dans l'exemple ci-dessus teste simplement pour voir si la valeur affectée à $ varval est nulle et, si tel est le cas, invite l'utilisateur à fournir une valeur.
$($n)
s'exécute$n
dans un sous-shell. Il essaie d'exécuter la commande1
qui n'existe pas.