Quelle est la différence entre eval et exec?

Réponses:

124

evalet execsont des bêtes complètement différentes. (Mis à part le fait que les deux exécuteront des commandes, tout comme tout ce que vous faites dans un shell.)

$ help exec
exec: exec [-cl] [-a name] [command [arguments ...]] [redirection ...]
    Replace the shell with the given command.

Ce exec cmdqui est identique à l’exécution cmd, à la différence que le shell actuel est remplacé par la commande, au lieu d’un processus séparé en cours d’exécution. En interne, par exemple en cours d' exécution /bin/lsfera appel fork()pour créer un processus enfant, puis exec()chez l'enfant à exécuter /bin/ls. exec /bin/lsd'autre part ne sera pas fourche, mais remplace simplement la coquille.

Comparer:

$ bash -c 'echo $$ ; ls -l /proc/self ; echo foo'
7218
lrwxrwxrwx 1 root root 0 Jun 30 16:49 /proc/self -> 7219
foo

avec

$ bash -c 'echo $$ ; exec ls -l /proc/self ; echo foo'
7217
lrwxrwxrwx 1 root root 0 Jun 30 16:49 /proc/self -> 7217

echo $$affiche le PID du shell que j'ai commencé et la liste /proc/selfnous donne le PID du lsshell exécuté. En règle générale, les ID de processus sont différents, mais avec execle shell et lsont le même ID de processus. De plus, la commande suivante execn'a pas été exécutée, car le shell a été remplacé.


D'autre part:

$ help eval
eval: eval [arg ...]
    Execute arguments as a shell command.

evalexécutera les arguments en tant que commande dans le shell actuel. En d'autres termes, eval foo barc'est la même chose que juste foo bar. Mais les variables seront développées avant l'exécution, nous pouvons donc exécuter des commandes sauvegardées dans des variables shell:

$ unset bar
$ cmd="bar=foo"
$ eval "$cmd"
$ echo "$bar"
foo

Il ne créera pas de processus enfant, la variable est donc définie dans le shell actuel. (Bien sûr, eval /bin/lscela créera un processus enfant, comme le /bin/lsferait un vieux vieux .)

Ou nous pourrions avoir une commande qui génère des commandes shell. En cours d'exécution, ssh-agentl'agent démarre en arrière-plan et génère un ensemble d'affectations de variables, qui peuvent être définies dans le shell actuel et utilisées par les processus enfants (les sshcommandes que vous exécutez). Par conséquent, ssh-agentpeut être démarré avec:

eval $(ssh-agent)

Et le shell actuel obtiendra les variables pour que les autres commandes héritent.


Bien sûr, si la variable cmdcontenait quelque chose comme rm -rf $HOME, alors courir eval "$cmd"ne serait pas quelque chose que vous voudriez faire. Même les choses comme les substitutions de commandes à l' intérieur de la chaîne seraient traitées, donc il faut vraiment être sûr que l'entrée evalest en sécurité avant de l' utiliser.

Souvent, il est possible d'éviter evalet d'éviter, même accidentellement, le mélange du code et des données de manière incorrecte.

ilkkachu
la source
C'est une excellente réponse, @ilkkachu. Merci!
Willian Paixao
Plus de détails sur l'utilisation de eval peuvent être trouvés ici: stackoverflow.com/a/46944004/2079103
clearlight
1
@clearlight, eh bien, cela me rappelle d’ajouter à cette réponse l’avertissement habituel à propos de ne pas utiliser evalen premier lieu . Des choses comme modifier indirectement des variables peuvent être faites dans beaucoup de shells via declare/ typeset/ namerefet des extensions comme ${!var}, alors je les utiliserais plutôt evalque si je devais vraiment l'éviter.
Ilkkachu
27

execne crée pas de nouveau processus. Il remplace le processus en cours par la nouvelle commande. Si vous avez fait cela sur la ligne de commande, cela mettra fin à votre session shell (et peut-être vous déconnecter ou fermer la fenêtre du terminal!)

par exemple

ksh% bash
bash-4.2$ exec /bin/echo hello
hello
ksh% 

Ici je suis dedans ksh(ma coquille normale). Je commence bashet puis bash je exec /bin/echo. Nous pouvons voir que je suis retombé kshaprès parce que le bashprocessus a été remplacé par /bin/echo.

Stephen Harris
la source
umm sur le visage est retombé dans ksh b / c processus a été remplacé par echo n'a pas beaucoup de sens?
14

TL; DR

execest utilisé pour remplacer le processus shell actuel par de nouveaux descripteurs de fichier et de gestion de redirection de flux / fichier si aucune commande n'a été spécifiée. evalest utilisé pour évaluer les chaînes en tant que commandes. Les deux peuvent être utilisés pour construire et exécuter une commande avec des arguments connus au moment de l'exécution, mais execremplace le processus du shell actuel en plus de l'exécution des commandes.

exec buil-in

Syntaxe:

exec [-cl] [-a name] [command [arguments]]

Selon le manuel, si une commande est spécifiée, cette commande intégrée

... remplace la coquille. Aucun nouveau processus n'est créé. Les arguments deviennent les arguments à commander.

En d'autres termes, si vous utilisiez le bashPID 1234 et si vous deviez l'exécuter exec top -u rootdans ce shell, la topcommande contiendrait alors le PID 1234 et remplacerait votre processus de shell.

Où est-ce utile? Dans quelque chose connu sous le nom de scripts wrapper. Ces scripts construisent des ensembles d'arguments ou prennent certaines décisions quant aux variables à transmettre à l'environnement, puis sont utilisés execpour se remplacer par la commande spécifiée, en fournissant bien sûr les mêmes arguments que le script d'encapsuleur.

Le manuel indique également que:

Si la commande n'est pas spécifiée, les redirections prennent effet dans le shell actuel.

Cela nous permet de rediriger n'importe quoi des flux de sortie des shells actuels dans un fichier. Cela peut être utile pour la journalisation ou le filtrage, où vous ne voulez pas voir les stdoutcommandes mais seulement stderr. Par exemple, comme ceci:

bash-4.3$ exec 3>&1
bash-4.3$ exec > test_redirect.txt
bash-4.3$ date
bash-4.3$ echo "HELLO WORLD"
bash-4.3$ exec >&3
bash-4.3$ cat test_redirect.txt 
2017 05 20 星期六 05:01:51 MDT
HELLO WORLD

Ce comportement le rend pratique pour la journalisation dans des scripts de shell , la redirection de flux vers des fichiers ou processus distincts et d'autres tâches amusantes avec des descripteurs de fichiers.

Au niveau du code source au moins pour la bashversion 4.3, le execcomposant intégré est défini dans builtins/exec.def. Il analyse les commandes reçues et, s’il y en a, il transmet les informations à la shell_execve()fonction définie dans le execute_cmd.cfichier.

En résumé, il existe une famille de execcommandes en langage de programmation C, qui shell_execve()est en fait une fonction wrapper de execve:

/* Call execve (), handling interpreting shell scripts, and handling
   exec failures. */
int
shell_execve (command, args, env)
     char *command;
     char **args, **env;
{

eval intégré

Le manuel de bash 4.3 déclare (soulignement ajouté par moi):

Les arguments sont lus et concaténés dans une seule commande. Cette commande est ensuite lue et exécutée par le shell et son état de sortie est renvoyé sous la forme de la valeur eval.

Notez qu'il n'y a pas de remplacement de processus en cours. Contrairement aux execcas où l'objectif est de simuler des execve()fonctionnalités, la fonction evalintégrée ne sert qu'à "évaluer" les arguments, comme si l'utilisateur les avait saisis sur la ligne de commande. En tant que tels, de nouveaux processus sont créés.

Où cela pourrait être utile? Comme Gilles l'a souligné dans sa réponse , "... eval n'est pas utilisé très souvent. Dans certains shells, l'utilisation la plus courante consiste à obtenir la valeur d'une variable dont le nom n'est connu qu'au moment de l'exécution". Personnellement, je l'ai utilisé dans quelques scripts sur Ubuntu où il était nécessaire d'exécuter / évaluer une commande en fonction de l'espace de travail spécifique que l'utilisateur utilisait actuellement.

Au niveau du code source, il est défini dans builtins/eval.defet transmet la chaîne d'entrée analysée à evalstring()function.

Entre autres choses, vous evalpouvez affecter des variables qui restent dans l’environnement d’exécution actuel du shell, alors que execvous ne pouvez pas:

$ eval x=42
$ echo $x
42
$ exec x=42
bash: exec: x=42: not found
Sergiy Kolodyazhnyy
la source
@ ctrl-alt-delor J'ai édité cette partie, merci, bien que cela puisse induire un nouveau processus, le PID reste simplement le même que celui du shell actuel. À l'avenir, envisagez de réviser les réponses au lieu de laisser un commentaire et un vote négatif, en particulier lorsqu'il s'agit d'un élément mineur, tel qu'une phrase de 7 mots; La modification et la correction d'une réponse prennent beaucoup moins de temps et sont beaucoup plus utiles.
Sergiy Kolodyazhnyy
5
créer un nouveau processus enfant, exécuter les arguments et renvoyer le statut de sortie.

Euh quoi? Le problème evalest que cela ne crée en aucun cas un processus enfant. Si je fais

eval "cd /tmp"

dans un shell, le shell actuel aura ensuite changé de répertoire. Il ne execcrée pas non plus de nouveau processus enfant, mais change l’exécutable en cours (à savoir le shell) pour celui qui est donné. L'identifiant du processus (et les fichiers ouverts, etc.) reste identique. Par opposition à eval, an execne retournera pas au shell appelant à moins que le execclient échoue en raison de son incapacité à trouver ou à charger l'exécutable ou à résoudre les problèmes de développement d'arguments.

evalinterprète fondamentalement ses arguments comme une chaîne après la concaténation, c’est-à-dire qu’il crée une couche supplémentaire d’expansion de caractères génériques et de fractionnement d’arguments. execne fait rien comme ça.

utilisateur180452
la source
1
La question initiale se lisait comme suit: "eval et exec sont tous deux des commandes intégrées de Bash (1) et les exécute en créant un nouveau processus enfant, en exécutant les arguments et en renvoyant le statut de sortie"; J'ai supprimé la fausse présomption.
Charles Stewart
-1

Évaluation

Ces fonctionnent:

$ echo hi
hi

$ eval "echo hi"
hi

$ exec echo hi
hi

Cependant, ceux-ci ne:

$ exec "echo hi"
bash: exec: echo hi: not found

$ "echo hi"
bash: echo hi: command not found

Process image de remplacement

Cet exemple montre comment execremplace l'image de son processus appelant:

# Get PID of current shell
sh$ echo $$
1234

# Enter a subshell with PID 5678
sh$ sh

# Check PID of subshell
sh-subshell$ echo $$
5678

# Run exec
sh-subshell$ exec echo $$
5678

# We are back in our original shell!
sh$ echo $$
1234

Avis qui a exec echo $$fonctionné avec le PID du sous-shell! De plus, une fois terminé, nous sommes revenus dans notre sh$coquille d' origine .

Par contre, evalne remplace pas l’image process. Au lieu de cela, il exécute la commande donnée comme vous le feriez normalement dans le shell lui-même. (Bien sûr, si vous exécutez une commande nécessitant un processus, elle le fait!)

sh$ echo $$
1234

sh$ sh

sh-subshell$ echo $$
5678

sh-subshell$ eval echo $$
5678

# We are still in the subshell!
sh-subshell$ echo $$
5678
Mateen Ulhaq
la source
J'ai posté cette réponse parce que les autres exemples ne l'illustraient pas vraiment d'une manière suffisamment compréhensible pour des esprits faibles comme moi.
Mateen Ulhaq
Mauvais choix de mots: la substitution de processus est un concept existant qui semble ne pas être lié à ce que vous illustrez ici.
Muru
@muru Le "processus de remplacement" est-il meilleur? Je ne sais pas quelle est la bonne terminologie. La page de manuel semble appeler cela "remplacer l'image de processus".
Mateen Ulhaq le
Hmm, ne sais pas. (Mais quand j'entends « remplacer l'image du processus », je pense: exec)
muru