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?
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.
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
.
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.
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;
{
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
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.