web-dev-qa-db-fra.com

Qu'est-ce que la commande "eval" dans bash?

Que pouvez-vous faire avec la commande eval? Pourquoi est-ce utile? Est-ce une sorte de fonction intégrée dans bash? Il n'y a pas de page man pour ça ..

210
LanceBaynes

eval fait partie de POSIX. C'est une interface qui peut être intégrée à Shell.

Son décrit dans le "Manuel du programmeur POSIX": http://www.unix.com/man-page/posix/1posix/eval/

eval - construct command by concatenating arguments

Il prendra un argument et en construira une commande, qui sera exécutée par le Shell. Voici l'exemple de la page de manuel:

1) foo=10 x=foo
2) y='$'$x
3) echo $y
4) $foo
5) eval y='$'$x
6) echo $y
7) 10
  1. Dans la première ligne, vous définissez $foo avec la valeur '10' et $x avec la valeur 'foo'.
  2. Définissez maintenant $y, qui se compose de la chaîne '$foo'. Le signe dollar doit être échappé avec '$'.
  3. Pour vérifier le résultat, echo $y.
  4. Le résultat sera la chaîne '$foo'
  5. Maintenant, nous répétons l'affectation avec eval. Il évaluera d'abord $x à la chaîne 'foo'. Maintenant, nous avons la déclaration y=$foo qui sera évalué en y=10.
  6. Le résultat de echo $y est maintenant la valeur '10'.

Il s'agit d'une fonction courante dans de nombreuses langues, par ex. Perl et JavaScript. Jetez un œil à perldoc eval pour plus d'exemples: http://perldoc.Perl.org/functions/eval.html

150
echox

Oui, eval est une commande interne bash, elle est donc décrite dans la page de manuel bash.

eval [arg ...]
    The  args  are read and concatenated together into a single com-
    mand.  This command is then read and executed by the Shell,  and
    its  exit status is returned as the value of eval.  If there are
    no args, or only null arguments, eval returns 0.

Habituellement, il est utilisé en combinaison avec une substitution de commande . Sans un eval explicite, le Shell essaie d'exécuter le résultat d'une substitution de commande, pas de l'évaluer .

Supposons que vous souhaitiez coder un équivalent de VAR=value; echo $VAR. Notez la différence dans la façon dont le shell gère les écritures de echo VAR=value:

  1. andcoz@...:~> $( echo VAR=value )
    bash: VAR=value: command not found
    andcoz@...:~> echo $VAR
    <empty line>
    

    Le shell essaie d'exécuter echo et VAR=value comme deux commandes distinctes. Il renvoie une erreur sur la deuxième chaîne. La cession reste inefficace.

  2. andcoz@...:~> eval $( echo VAR=value )
    andcoz@...:~> echo $VAR
    value
    
    Le shell fusionne (concatène) les deux chaînes echo et VAR=value, analyse cette unité unique selon les règles appropriées et l'exécute.

Enfin et surtout, eval peut être une commande très dangereuse. Toute entrée dans une commande eval doit être soigneusement vérifiée pour éviter les problèmes de sécurité.

65
andcoz

eval n'a pas de page de manuel car il ne s'agit pas d'une commande externe distincte, mais plutôt d'un shell intégré, ce qui signifie une commande interne au Shell et connue uniquement par celui-ci (bash). La partie pertinente de la page de manuel bash dit:

eval [arg ...]
    The args are read and concatenated together into a single command.  
    This command is then  read  and executed by the Shell, and its exit 
    status is returned as the value of eval.  If there are no args, or only 
    null arguments, eval returns 0

De plus, la sortie si help eval est:

eval: eval [arg ...]
    Execute arguments as a Shell command.

    Combine ARGs into a single string, use the result as input to the Shell,
    and execute the resulting commands.

    Exit Status:
    Returns exit status of command or success if command is null.

eval est une commande puissante et si vous avez l'intention de l'utiliser, vous devez faire très attention à éviter les possibles risques de sécurité qui l'accompagnent.

19
jw013

L'instruction eval indique au shell de prendre les arguments d'eval comme commande et de les exécuter via la ligne de commande. Il est utile dans une situation comme ci-dessous:

Dans votre script, si vous définissez une commande dans une variable et que plus tard vous souhaitez utiliser cette commande, vous devez utiliser eval:

/home/user1 > a="ls | more"
/home/user1 > $a
bash: command not found: ls | more
/home/user1 > # Above command didn't work as ls tried to list file with name pipe (|) and more. But these files are not there
/home/user1 > eval $a
file.txt
mailids
remote_cmd.sh
sample.txt
tmp
/home/user1 >
18
Nikhil Gupta

Qu'est-ce que l'eval?

eval est une commande Shell qui est généralement implémentée en tant que commande intégrée.

Dans POSIX, il est répertorié comme faisant partie de "2.14. Special Built-In Utilities" à l'entrée "eval" .
Ce que signifie intégré est:

Le terme "intégré" implique que le shell peut exécuter l'utilitaire directement et n'a pas besoin de le rechercher.

Qu'est ce que ça fait?

En termes simples: crée une ligne d'entrée à analyser deux fois .

Comment ça fait ça?

Le Shell a une séquence d'étapes à suivre pour "traiter" une ligne. Vous pourriez regardez cette image et vous rendre compte que eval est la seule ligne qui monte, retour à l'étape 1, à gauche. De la description POSIX :

2.1 Présentation du shell

  1. Le Shell lit son entrée ....
  2. Le Shell divise l'entrée en jetons: mots et opérateurs
  3. Le shell analyse l'entrée en commandes simples et composées.
  4. Le Shell effectue différentes extensions (séparément) ...
  5. Le shell effectue la redirection et supprime les opérateurs de redirection et leurs opérandes de la liste des paramètres.
  6. Le Shell exécute une fonction, un fichier exécutable ou un script intégré ...
  7. Le shell attend éventuellement la fin de la commande et collecte l'état de sortie.

À l'étape 6, une fonction intégrée sera exécutée.
À l'étape 6, eval renvoie la ligne traitée à l'étape 1.
C'est la seule condition dans laquelle la séquence d'exécution revient.

C'est pourquoi je dis: Avec eval une ligne d'entrée est analysée deux fois .

Effets de l'analyse deux fois.

La première.

Et l'effet le plus important à comprendre. Est-ce que l'une des conséquences de la première fois qu'une ligne est soumise aux sept étapes de Shell illustrées ci-dessus est la citation . À l'intérieur de l'étape 4 (extensions), il y a aussi une séquence d'étapes pour effectuer toutes les extensions , dont la dernière est Suppression de devis :

La suppression du devis doit toujours être effectuée en dernier.

Donc, toujours, il y a un niveau de cotation supprimé.

Seconde.

En conséquence de ce premier effet, des parties supplémentaires/différentes de la ligne sont exposées à l'analyse syntaxique du shell et à toutes les autres étapes.

Exemple.

Indirection.

Cela permet d'exécuter des extensions indirectes:

a=b b=c    ;    eval echo \$$a            ### shall produce "c"

Pourquoi? Parce que sur la première boucle, le premier $ Est cité.
En tant que tel, il est ignoré pour les extensions par le Shell.
Le prochain $ Portant le nom a est développé pour produire "b".
Ensuite, un niveau de citation est supprimé, rendant le premier $ Sans guillemets.
Fin de la première boucle.

C'est alors, sur la deuxième boucle que la chaîne $b Est lue par le Shell.
Puis étendu à "c"
Et donné comme argument à echo.

Pour "voir" ce que va produire eval sur la première boucle (à réévaluer), utilisez echo. Ou toute commande/script/programme qui montre clairement les arguments:

$ a=b b=c
$ eval echo \$$a;
c

Remplacez eval par echo pour "voir" ce qui se passe:

$ echo echo \$$a
echo $b

Il est également possible d'afficher toutes les "parties" d'une ligne avec:

$ printf '<%s> ' echo \$$a
<echo> <$b>

Ce qui, dans cet exemple, n'est qu'un écho et une variable, mais souvenez-vous-en pour aider à évaluer des cas plus complexes.

Une correction.

Il faut dire que: il y a une erreur dans le code ci-dessus, pouvez-vous le voir?.
Facile: il manque des citations.

Comment? tu peux demander. Simple, changeons les variables (pas le code):

$ a=b b="hi     jk"
$ eval echo \$$a
hi jk

Voir les espaces manquants?
En effet, la valeur à l'intérieur de $b A été divisée par le shell.

Si cela ne vous convainc pas, essayez ceci:

$ a=b b="hi  *  jk"
$ eval echo \$$a              ### warning this will expand to the list  
                              ### of all files in the present directory.

Pourquoi?

Citations manquantes. Pour le faire fonctionner correctement (ajoutez des guillemets internes "$a" Et externes \").
Essayez ceci (est parfaitement sûr):

$ a=b b="hi      *       jk"
$ eval echo \" \$"$a" \"
hi      *       jk

À propos du manuel:

Il n'y a pas de page de manuel pour ça ..

Non, il n'y a pas de page de manuel indépendante pour cela. La recherche de manuel avec man -f eval Ou même apropos eval N'affiche aucune entrée.

Il est inclus dans man bash. Comme tout intégré.
Recherchez "Shell BUILTIN COMMANDS" puis "eval".

Un moyen plus simple d'obtenir de l'aide est: En bash, vous pouvez faire help eval Pour voir l'aide de la fonction intégrée.

Pourquoi eval est-il appelé mal?

Parce qu'il lie du texte pour coder dynamiquement.

En d'autres termes: il convertit la liste de ses arguments (et/ou les extensions de ces arguments) en une ligne exécutée. Si pour une raison quelconque, un argument a été défini par un attaquant, vous exécuterez le code de l'attaquant.

Ou encore plus simple, avec eval vous dites à celui qui a défini la valeur d'un ou plusieurs arguments:

Allez, asseyez-vous ici et tapez n'importe quelle ligne de commande, je vais l'exécuter avec mes pouvoirs.

Est-ce dangereux? Cela devrait être clair pour tout le monde.

La règle de sécurité pour eval doit être:
Exécutez eval uniquement sur les variables auxquelles vous avez donné sa valeur.

Lire plus de détails ici .

12
user79743

eval est une fonctionnalité de la plupart des langages interprétés (TCL, python, Ruby...), pas seulement des shells. Il est utilisé pour évaluer dynamiquement le code.

Dans les shells, il est implémenté en tant que commande intégrée Shell.

Fondamentalement, eval prend une chaîne comme argument et évalue/interprète le code qu'il contient. Dans les shells, eval peut prendre plus d'un argument, mais eval ne fait que les concaténer pour former la chaîne à évaluer.

Il est très puissant car vous pouvez construire du code dynamiquement et l'exécuter, ce que vous ne pouvez pas faire dans des langages compilés comme C.

Comme:

varname=$1 varvalue=$2
eval "$varname=\$varvalue" # evaluate a string like "foo=$varvalue"
                           # which in Bourne-like Shell language
                           # is a variable assignment.

Mais c'est aussi dangereux car il est important de nettoyer les parties dynamiques (fournies en externe) de ce qui est passé à eval pour la raison même où il est interprété comme du code Shell.

Par exemple, ci-dessus si $1 est evil-command; var, eval finirait par évaluer le evil-command; var=$varvalue Code shell, puis exécutez ce evil-command.

La perversité de eval est souvent exagérée.

D'accord, c'est dangereux, mais au moins nous savons que c'est dangereux.

Beaucoup d'autres commandes évalueront le code Shell dans ses arguments s'il n'est pas filtré, comme (selon le Shell), [ aka test, export, printf, GNU sed, awk, et de cours sh/bash/Perl et tous les interprètes ...

Exemples (ici en utilisant uname comme evil-command et $a en tant que données non stérilisées fournies en externe):

$ a='$(uname>&2)' sh -c 'eval "echo $a"'
Linux

$ a='x[0$(uname>&2)]' mksh -c 'export "$a=$b"'
Linux
$ a='x[0$(uname>&2)]' ksh93 -c 'printf "%d\n" "$a"'
Linux
0
$ a='x[0$(uname>&2)]' ksh93 -c '[ "$a" -gt 0 ]'
Linux
$ a=$'bar/g;e uname>&2\n;s//'; echo foo | sed "s/foo/$a/g"
Linux
bar
$ a='";system("uname");"'; awk "BEGIN{print \"$a\"}"

Linux
$ a=';uname'; sh -c "echo $a"

Linux

Ces commandes sed, export... pourraient être considérées comme plus dangereuses, car même si elles sont évidentes eval "$var" provoquera le contenu de $var à évaluer en tant que code Shell, ce n'est pas si évident avec sed "s/foo/$var/" ou export "$var=value" ou [ "$var" -gt 0 ]. La dangerosité est la même, mais elle est cachée dans ces autres commandes.

11
Stéphane Chazelas

Cet exemple peut nous éclairer:

#!/bin/bash
VAR1=25
VAR2='$VAR1'
VAR3='$VAR2'
echo "$VAR3"
eval echo "$VAR3"
eval eval echo "$VAR3"

Sortie du script ci-dessus:

$VAR2
$VAR1
25
5
LoMaPh