it-swarm.com.de

Was ist der Unterschied zwischen eval und exec?

eval und exec sind beide in Befehle von bash (1) eingebaut, die Befehle ausführen.

Ich sehe auch, dass exec einige Optionen hat, aber ist das der einzige Unterschied? Was passiert mit ihrem Kontext?

85
Willian Paixao

eval und exec sind völlig verschiedene Tiere. (Abgesehen von der Tatsache, dass beide Befehle ausführen, aber auch alles, was Sie in einer Shell tun.)

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

Was exec cmd Tut, ist genau das Gleiche wie das Ausführen von cmd, außer dass die aktuelle Shell durch den Befehl ersetzt wird, anstatt dass ein separater Prozess ausgeführt wird. Wenn Sie beispielsweise say /bin/ls Ausführen, wird fork() aufgerufen, um einen untergeordneten Prozess zu erstellen, und dann exec() im untergeordneten Prozess, um /bin/ls Auszuführen. exec /bin/ls Andererseits wird nicht gegabelt, sondern ersetzt nur die Shell.

Vergleichen Sie:

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

mit

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

echo $$ Gibt die PID der von mir gestarteten Shell aus, und die Auflistung von /proc/self Gibt die PID des ls an, das von der Shell ausgeführt wurde. Normalerweise sind die Prozess-IDs unterschiedlich, aber mit exec haben die Shell und ls dieselbe Prozess-ID. Außerdem wurde der Befehl nach exec nicht ausgeführt, da die Shell ersetzt wurde.


Auf der anderen Seite:

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

eval führt die Argumente als Befehl in der aktuellen Shell aus. Mit anderen Worten, eval foo bar Ist dasselbe wie nur foo bar. Die Variablen werden jedoch vor der Ausführung erweitert, sodass wir Befehle ausführen können, die in Shell-Variablen gespeichert sind:

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

Es wird keinen untergeordneten Prozess erstellen, daher wird die Variable in der aktuellen Shell festgelegt. (Natürlich erstellt eval /bin/ls Einen untergeordneten Prozess, genau wie ein einfacher alter /bin/ls.)

Oder wir könnten einen Befehl haben, der Shell-Befehle ausgibt. Durch Ausführen von ssh-agent Wird der Agent im Hintergrund gestartet und eine Reihe von Variablenzuweisungen ausgegeben, die in der aktuellen Shell festgelegt und von untergeordneten Prozessen verwendet werden können (die Befehle ssh, die Sie ausführen würden). Daher kann ssh-agent Begonnen werden mit:

eval $(ssh-agent)

Und die aktuelle Shell erhält die Variablen, die andere Befehle erben sollen.


Wenn die Variable cmd zufällig etwas wie rm -rf $HOME Enthält, ist das Ausführen von eval "$cmd" Natürlich nichts, was Sie tun möchten. Sogar Dinge wie Befehlsersetzungen innerhalb der Zeichenfolge würden verarbeitet, daher sollte man wirklich sicherstellen, dass die Eingabe in eval sicher ist, bevor man sie verwendet.

Oft ist es möglich, eval zu vermeiden und zu vermeiden, dass Code und Daten versehentlich falsch gemischt werden.

136
ilkkachu

exec erstellt keinen neuen Prozess. Es ersetzt den aktuellen Prozess durch den neuen Befehl. Wenn Sie dies in der Befehlszeile getan haben, wird Ihre Shell-Sitzung effektiv beendet (und Sie werden möglicherweise abgemeldet oder das Terminalfenster geschlossen!).

z.B.

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

Hier bin ich in ksh (meiner normalen Shell). Ich starte bash und dann in bash I exec /bin/echo. Wir können sehen, dass ich danach wieder in ksh abgelegt wurde, weil der Prozess bash durch /bin/echo Ersetzt wurde.

27
Stephen Harris

TL; DR

exec wird verwendet, um den aktuellen Shell-Prozess durch neue zu ersetzen und Stream-Umleitungs-/Dateideskriptoren zu verarbeiten, wenn kein Befehl angegeben wurde. eval wird verwendet, um Zeichenfolgen als Befehle auszuwerten. Beide können verwendet werden, um einen Befehl mit zur Laufzeit bekannten Argumenten aufzubauen und auszuführen, aber exec ersetzt zusätzlich zur Ausführung von Befehlen den Prozess der aktuellen Shell.

exec Buil-In

Syntax:

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

Laut Handbuch, wenn ein Befehl angegeben ist, ist dieser eingebaut

... ersetzt die Shell. Es wird kein neuer Prozess erstellt. Die Argumente werden zu den zu befehlenden Argumenten.

Mit anderen Worten, wenn Sie bash mit PID 1234 ausgeführt haben und exec top -u root Innerhalb dieser Shell ausgeführt haben, hat der Befehl top die PID 1234 und ersetzt Ihre Shell Prozess.

Wo ist das nützlich? In so genannten Wrapper-Skripten. Solche Skripte bauen eine Reihe von Argumenten auf oder treffen bestimmte Entscheidungen darüber, welche Variablen an die Umgebung übergeben werden sollen, und verwenden dann exec, um sich durch den angegebenen Befehl zu ersetzen, und stellen natürlich dieselben Argumente bereit, die das Wrapper-Skript erstellt hat auf dem Weg.

Im Handbuch heißt es außerdem:

Wenn kein Befehl angegeben wird, werden alle Umleitungen in der aktuellen Shell wirksam

Auf diese Weise können wir alles aus aktuellen Shells-Ausgabestreams in eine Datei umleiten. Dies kann für Protokollierungs- oder Filterzwecke nützlich sein, bei denen Sie nicht stdout von Befehlen sehen möchten, sondern nur stderr. Zum Beispiel wie folgt:

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

Dieses Verhalten macht es praktisch für Anmelden von Shell-Skripten , Umleiten von Streams in separate Dateien oder Prozesse und andere lustige Dinge mit Dateideskriptoren.

Auf der Quellcodeebene ist zumindest für bash Version 4.3 das integrierte exec in builtins/exec.def Definiert. Es analysiert die empfangenen Befehle und leitet sie, falls vorhanden, an die in der Datei execute_cmd.c Definierte Funktion Shell_execve() weiter.

Kurz gesagt, es gibt eine Familie von exec Befehlen in der Programmiersprache C, und Shell_execve() ist im Grunde eine Wrapper-Funktion von execve:

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

eval eingebaut

Das Bash 4.3-Handbuch besagt (Hervorhebung von mir hinzugefügt):

Die Argumente werden zu einem einzigen Befehl gelesen und verkettet. Dieser Befehl wird dann von der Shell gelesen und ausgeführt , und sein Beendigungsstatus wird als Wert von eval zurückgegeben.

Beachten Sie, dass kein Prozessaustausch stattfindet. Im Gegensatz zu exec, bei dem das Ziel darin besteht, die Funktionalität von execve() zu simulieren, dient das integrierte eval nur dazu, Argumente zu "bewerten", so als hätte der Benutzer sie in den Befehl eingegeben Linie. Dadurch entstehen neue Prozesse.

Wo könnte dies nützlich sein? Wie Gilles betonte in dieser Antwort , "... wird eval nicht sehr oft verwendet. In einigen Shells wird am häufigsten der Wert einer Variablen ermittelt, deren Name erst zur Laufzeit bekannt ist." . Persönlich habe ich es in einigen Skripten unter Ubuntu verwendet, in denen es notwendig war, einen Befehl basierend auf dem spezifischen Arbeitsbereich, den der Benutzer gerade verwendete, auszuführen/auszuwerten.

Auf der Quellcodeebene ist es in builtins/eval.def Definiert und übergibt die analysierte Eingabezeichenfolge an die Funktion evalstring().

Unter anderem kann evalVariablen zuweisen , die in der aktuellen Shell-Ausführungsumgebung verbleiben, während exec nicht:

$ eval x=42
$ echo $x
42
$ exec x=42
bash: exec: x=42: not found
14
erstellen Sie einen neuen untergeordneten Prozess, führen Sie die Argumente aus und geben Sie den Exit-Status zurück.

UH, was? Der springende Punkt von eval ist, dass es in keiner Weise einen untergeordneten Prozess erstellt. Wenn ich mache

eval "cd /tmp"

in einer Shell hat danach die aktuelle Shell das Verzeichnis geändert. exec erstellt auch keinen neuen untergeordneten Prozess, sondern ändert die aktuelle ausführbare Datei (nämlich die Shell) für die angegebene; Die Prozess-ID (und geöffnete Dateien und andere Inhalte) bleiben unverändert. Im Gegensatz zu eval kehrt ein exec nicht zur aufrufenden Shell zurück, es sei denn, exec selbst schlägt fehl, weil die ausführbare Datei nicht gefunden oder geladen werden kann oder Probleme bei der Argumenterweiterung auftreten .

eval interpretiert seine Argumente nach der Verkettung grundsätzlich als Zeichenfolge, dh es wird eine zusätzliche Ebene der Platzhaltererweiterung und Argumentaufteilung durchgeführt. exec macht so etwas nicht.

5
user180452