eval
et exec
sont tous deux des commandes intégrées de bash (1) qui exécutent des commandes.
Je vois aussi exec
quelques options mais est-ce la seule différence? Qu'advient-il de leur contexte?
eval
et exec
sont tous deux des commandes intégrées de bash (1) qui exécutent des commandes.
Je vois aussi exec
quelques options mais est-ce la seule différence? Qu'advient-il de leur contexte?
Réponses:
eval
et exec
sont 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 cmd
qui 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/ls
fera appel fork()
pour créer un processus enfant, puis exec()
chez l'enfant à exécuter /bin/ls
. exec /bin/ls
d'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/self
nous donne le PID du ls
shell exécuté. En règle générale, 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 suivante exec
n'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.
eval
exécutera les arguments en tant que commande dans le shell actuel. En d'autres termes, eval foo bar
c'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/ls
cela créera un processus enfant, comme le /bin/ls
ferait un vieux vieux .)
Ou nous pourrions avoir une commande qui génère des commandes shell. En cours d'exécution, ssh-agent
l'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 ssh
commandes que vous exécutez). Par conséquent, ssh-agent
peut ê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 cmd
contenait 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 eval
est en sécurité avant de l' utiliser.
Souvent, il est possible d'éviter eval
et d'éviter, même accidentellement, le mélange du code et des données de manière incorrecte.
eval
en premier lieu . Des choses comme modifier indirectement des variables peuvent être faites dans beaucoup de shells via declare
/ typeset
/ nameref
et des extensions comme ${!var}
, alors je les utiliserais plutôt eval
que si je devais vraiment l'éviter.
exec
ne 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 bash
et puis bash je exec /bin/echo
. Nous pouvons voir que je suis retombé ksh
après parce que le bash
processus a été remplacé par /bin/echo
.
exec
est 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. 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 de l'exécution des commandes.
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 bash
PID 1234 et si vous deviez l'exécuter exec top -u root
dans ce shell, la top
commande 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 exec
pour 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 stdout
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 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 bash
version 4.3, le exec
composant 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.c
fichier.
En résumé, il existe une famille de exec
commandes 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;
{
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 exec
cas où l'objectif est de simuler des execve()
fonctionnalités, la fonction eval
inté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.def
et transmet la chaîne d'entrée analysée à evalstring()
function.
Entre autres choses, vous eval
pouvez affecter des variables qui restent dans l’environnement d’exécution actuel du shell, alors que exec
vous ne pouvez 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 le statut de sortie.
Euh quoi? Le problème eval
est 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 exec
cré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 exec
ne retournera pas au shell appelant à moins que le exec
client échoue en raison de son incapacité à trouver ou à charger l'exécutable ou à résoudre les problèmes de développement d'arguments.
eval
interprè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. exec
ne fait rien comme ça.
É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 exec
remplace 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, eval
ne 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
exec
)