web-dev-qa-db-fra.com

Quelle est la différence entre eval et exec?

eval et exec sont tous deux des commandes intégrées de bash (1) qui exécutent des commandes.

Je vois aussi que exec a quelques options mais est-ce la seule différence? Qu'arrive-t-il à leur contexte?

85
Willian Paixao

eval et exec sont des bêtes complètement différentes. (Mis à part le fait que les deux exécuteront des commandes, mais il en va de même pour 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 que fait exec cmd Est exactement la même chose que d'exécuter simplement cmd, sauf que le shell actuel est remplacé par la commande, au lieu d'un processus distinct en cours d'exécution. En interne, l'exécution de say /bin/ls Appellera fork() pour créer un processus enfant, puis exec() dans l'enfant pour exécuter /bin/ls. exec /bin/ls D'autre part ne pas bifurquer, mais remplace simplement le Shell.

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 $$ Imprime le PID du Shell que j'ai commencé, et la liste /proc/self Nous donne le PID du ls qui a été exécuté à partir du Shell. Habituellement, les ID de processus sont différents, mais avec exec le Shell et ls ont le même ID de processus. De plus, la commande suivant exec ne s'est pas exécutée, car le shell a été remplacé.


D'autre part:

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

eval exécutera les arguments comme une commande dans le shell actuel. En d'autres termes, eval foo bar Est identique à simplement foo bar. Mais les variables seront développées avant l'exécution, nous pouvons donc exécuter des commandes enregistrées dans des variables Shell:

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

Il ne créera pas de processus enfant, de sorte que la variable est définie dans le shell actuel. (Bien sûr, eval /bin/ls Créera un processus enfant, de la même manière qu'un ancien /bin/ls.)

Ou nous pourrions avoir une commande qui génère des commandes Shell. L'exécution de ssh-agent Démarre l'agent 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 commandes ssh que vous exécuterez). Par conséquent, ssh-agent Peut être démarré avec:

eval $(ssh-agent)

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


Bien sûr, si la variable cmd contenait quelque chose comme rm -rf $HOME, Alors exécuter eval "$cmd" Ne serait pas quelque chose que vous voudriez faire. Même des choses comme les substitutions de commandes à l'intérieur de la chaîne seraient traitées, donc on devrait vraiment être sûr que l'entrée de eval est sûre avant de l'utiliser.

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

136
ilkkachu

exec ne crée pas de nouveau processus. Il remplace le processus en cours avec la nouvelle commande. Si vous l'avez fait sur la ligne de commande, cela mettra effectivement 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 dans ksh (mon Shell normal). Je démarre bash puis dans bash I exec /bin/echo. Nous pouvons voir que j'ai été replacé dans ksh après parce que le processus bash a été remplacé par /bin/echo.

27
Stephen Harris

TL; DR

exec est utilisé pour remplacer le processus Shell actuel par un nouveau et gérer la redirection de flux/descripteurs de fichier si aucune commande n'a été spécifiée. eval est 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 exec remplace le processus du shell actuel en plus d'exécuter des commandes.

exécutable intégré

Syntaxe:

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

Selon le manuel, s'il y a une commande spécifiée, cette fonction intégrée

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

En d'autres termes, si vous exécutiez bash avec le PID 1234 et si vous deviez exécuter exec top -u root Dans ce Shell, la commande top aura alors le PID 1234 et remplacera votre Shell processus.

Où est-ce utile? Dans quelque chose connu sous le nom de scripts wrapper. Ces scripts créent des ensembles d'arguments ou prennent certaines décisions concernant les variables à passer dans l'environnement, puis utilisent exec pour se remplacer par la commande spécifiée, et bien sûr fournir les mêmes arguments que le script wrapper a construit le long du chemin.

Le manuel indique également que:

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

Cela nous permet de rediriger quoi que ce soit des flux de sortie des shells actuels vers un fichier. Cela peut être utile à des fins de journalisation ou de filtrage, où vous ne voulez pas voir stdout des commandes 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 se connecter dans des scripts Shell , rediriger les flux vers des fichiers séparés ou processus , et autres trucs amusants avec des descripteurs de fichiers.

Au niveau du code source au moins pour bash version 4.3, le exec intégré est défini dans builtins/exec.def. Il analyse les commandes reçues et s'il y en a, il transmet les choses à la fonction Shell_execve() définie dans le fichier execute_cmd.c.

Pour faire court, il existe une famille de commandes exec en langage de programmation C, et Shell_execve() est essentiellement 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 indique (c'est moi qui souligne)

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

Notez qu'aucun remplacement de processus ne se produit. Contrairement à exec où le but est de simuler la fonctionnalité execve(), le eval intégré ne sert qu'à "évaluer" les arguments, comme si l'utilisateur les avait tapés sur la commande ligne. Ainsi, de nouveaux processus sont créés.

Où cela pourrait-il être utile? Comme Gilles l'a souligné dans cette réponse , "... eval n'est pas utilisé très souvent. Dans certains shells, l'utilisation la plus courante est d'obtenir la valeur d'une variable dont le nom n'est pas connu avant 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.def Et transmet la chaîne d'entrée analysée à la fonction evalstring().

Entre autres choses, eval peut assigner des variables qui restent dans l'environnement d'exécution Shell actuel, tandis que exec ne peut pas:

$ eval x=42
$ echo $x
42
$ exec x=42
bash: exec: x=42: not found
14
créer un nouveau processus enfant, exécuter les arguments et renvoyer l'état de sortie.

Quoi? L'intérêt de eval est qu'il ne crée en aucun cas un processus enfant. Si je fais

eval "cd /tmp"

dans un Shell, ensuite le current Shell aura changé de répertoire. exec ne crée pas non plus un nouveau processus enfant, au lieu de cela il change l'exécutable actuel (à savoir le Shell) pour celui donné; l'ID du processus (et les fichiers ouverts et autres) restent les mêmes. Contrairement à eval, un exec ne reviendra pas au shell appelant à moins que le exec lui-même échoue en raison de l'impossibilité de trouver ou de charger l'exécutable ou de mourir aux problèmes d'expansion d'argument .

eval interprète fondamentalement son ou ses arguments comme une chaîne après concaténation, c'est-à-dire qu'il fera une couche supplémentaire d'expansion générique et de division d'argument. exec ne fait rien de tel.

5
user180452