Je lisais un script bash créé par quelqu'un et j'ai remarqué que l'auteur n'utilise pas eval pour évaluer une variable comme une commande
L'auteur a utilisé
bash -c "$1"
au lieu de
eval "$1"
Je suppose que l'utilisation d'eval est la méthode préférée et c'est probablement plus rapide de toute façon. Est-ce vrai?
Y a-t-il une différence pratique entre les deux? Quelles sont les différences notables entre les deux?
eval "$1"
Exécute la commande dans le script actuel. Il peut définir et utiliser des variables Shell à partir du script actuel, définir des variables d'environnement pour le script actuel, définir et utiliser des fonctions à partir du script actuel, définir le répertoire actuel, umask, des limites et d'autres attributs pour le script actuel, etc. bash -c "$1"
Exécute la commande dans un script complètement séparé, qui hérite des variables d'environnement, des descripteurs de fichiers et d'autres environnements de processus (mais ne retransmet aucun changement) mais n'hérite pas des paramètres internes du shell (variables, fonctions, options, pièges, etc.).
Il existe une autre façon, (eval "$1")
, Qui exécute la commande dans un sous-shell: elle hérite de tout du script appelant mais ne retransmet aucune modification.
Par exemple, en supposant que la variable dir
n'est pas exportée et que $1
Est cd "$foo"; ls
, Alors:
cd /starting/directory; foo=/somewhere/else; eval "$1"; pwd
Répertorie le contenu de /somewhere/else
Et imprime /somewhere/else
.cd /starting/directory; foo=/somewhere/else; (eval "$1"); pwd
répertorie le contenu de /somewhere/else
et imprime /starting/directory
.cd /starting/directory; foo=/somewhere/else; bash -c "$1"; pwd
Répertorie le contenu de /starting/directory
(Car cd ""
Ne modifie pas le répertoire actuel) et imprime /starting/directory
.La différence la plus importante entre
bash -c "$1"
Et
eval "$1"
Est-ce que le premier s'exécute en sous-couche et le second ne fonctionne pas. Donc:
set -- 'var=something'
bash -c "$1"
echo "$var"
#there doesn't seem to be anything here
set -- 'var=something'
eval "$1"
echo "$var"
something
Je n'ai aucune idée pourquoi quelqu'un utiliserait jamais l'exécutable bash
de cette façon, cependant. Si vous devez l'invoquer, utilisez le sh
intégré garanti POSIX. Ou (subshell eval)
si vous souhaitez protéger votre environnement.
Personnellement, je préfère le .dot
par dessus tout.
printf 'var=something%d ; echo "$var"\n' `seq 1 5` | . /dev/fd/0
something1
something2
something3
something4
something5
La seule cause à utiliser est vraiment dans le cas où votre variable en attribue ou en évalue une autre, ou si le fractionnement de mots est important pour la sortie.
Par exemple:
var='echo this is var' ; $var
this is var
Cela fonctionne, mais uniquement parce que echo
ne se soucie pas du nombre d'arguments.
var='echo "this is var"' ; $var
"this is var"
Voir? Les guillemets doubles viennent parce que le résultat de l'expansion de Shell de $var
n'est pas évalué pour quote-removal
.
var='printf %s\\n "this is var"' ; $var
"this
is
var"
Mais avec eval
ou sh
:
var='echo "this is var"' ; eval "$var" ; sh -c "$var"
this is var
this is var
Lorsque nous utilisons eval
ou sh
, le shell effectue une deuxième passe aux résultats des extensions et les évalue également en tant que commande potentielle, de sorte que les guillemets font une différence. Vous pouvez également faire:
. <<VAR /dev/fd/0
${var:=echo "this is var"}
#END
VAR
this is var
J'ai fait un petit test:
time bash -c 'for i in {1..10000}; do bash -c "/bin/echo hi"; done'
time bash -c 'for i in {1..10000}; eval "/bin/echo hi"; done'
(Oui, je sais, j'ai utilisé bash -c pour exécuter la boucle mais cela ne devrait pas faire de différence).
Les resultats:
eval : 1.17s
bash -c : 7.15s
Donc eval
est plus rapide. Depuis la page de manuel de eval
:
L'utilitaire eval doit construire une commande en concaténant les arguments ensemble, en les séparant chacun par un caractère. La commande construite doit être lue et exécutée par le shell.
bash -c
bien sûr, exécute la commande dans un shell bash. Une remarque: j'ai utilisé /bin/echo
car echo
est un shell intégré avec bash
, ce qui signifie qu'un nouveau processus n'a pas besoin d'être démarré. Remplacement de /bin/echo
avec echo
pour le bash -c
test, il a fallu 1.28s
. C'est à peu près la même chose. Cependant, eval
est plus rapide pour exécuter les exécutables. La principale différence ici est que eval
ne démarre pas un nouveau Shell (il exécute la commande dans le courant) alors que bash -c
démarre un nouveau Shell, puis exécute la commande dans le nouveau Shell. Le démarrage d'un nouveau Shell prend du temps, c'est pourquoi bash -c
est plus lent que eval
.