Warum verlässt "exec non-existent-file" die Shell, wenn es sich um ein Skript handelt, das aus einer Quelle stammt?

6

In der Dokumentation zu wird angegeben exec, dass die Shell im Fehlerfall beendet wird, wenn die aktuelle Shell nicht interaktiv ist und die execfailOption nicht festgelegt ist.

Ich bemerkte ein seltsames Verhalten in einer interaktiven Bash-Shell, das ich mit den Dokumenten jedoch nicht erklären kann. Dieser Befehl

exec non-existent-file

erzeugt eine Fehlermeldung und die Shell bleibt wie erwartet aktiv. Wenn ich jedoch den gleichen Befehl in eine Datei und sourcediese Datei stecke, wird die aktuelle Shell von der fehlgeschlagenen Ausführung beendet.

Könnte mir jemand helfen zu verstehen, warum dies passiert?

Alexey Alexandrov
quelle
1
Ich habe keine Ahnung warum das so ist. Du hast Recht; Die Beschaffung einer Datei führt sie interaktiv aus. Könnte es sein, dass Sie einen Fehler gefunden haben bash?
Wildcard

Antworten:

2

Wie ich fühle, ist es ein Fehler. Unten ist eine Art "Detektivgeschichte".

Ja, im exec.defCode sehen wir:

if (subshell_environment || (interactive == 0 && no_exit_on_failed_exec == 0))
  exit_shell (exit_value);

so, execwürde bewirken , dass die Hülle a) für nicht-interaktiv Shell verlass mit false„no_exit_on_failed_exec“ b) , wenn execwird in Subshell läuft

Wenn der Befehl für die interaktive Shell vorhanden ist, wird die Shell durch den aufgerufenen Befehl ersetzt:

exec

Diese eingebaute Shell ersetzt den aktuellen Prozess durch einen angegebenen Befehl. Wenn die Shell auf einen Befehl stößt, wird normalerweise ein untergeordneter Prozess angehalten, um den Befehl tatsächlich auszuführen. Bei Verwendung des integrierten Befehls exec wird die Shell nicht gespalten, und der ausgeführte Befehl ersetzt die Shell . Wenn es in einem Skript verwendet wird, erzwingt es daher ein Beenden des Skripts, wenn der ausgeführte Befehl beendet wird .

Beispiel 15-24. Auswirkungen von exec

#!/bin/bash
exec echo "Exiting \"$0\" at line $LINENO."   # Exit from script here.
# $LINENO is an internal Bash variable set to the line number it's on.
**# The following lines never execute.**

http://www.tldp.org/LDP/abs/html/internal.html#EXECREF

sourceBefehl ruft keine Subshell auf - erzwingt die Ausführung aller Befehle in Ihrer aktuellen, aktiven Shell (wird z. B. zum Setzen von Variablen verwendet - nicht für eine Subshell, die beendet wird, sondern für Ihre aktuelle Shell). Nach dem Ausführen des Befehls sowohl mit sourceals auch direkt in der Shell wird Ihre Shell beendet (dies ist das erwartete Verhalten).

Wenn ein Skript unter Verwendung von "source" ausgeführt wird, wird es in der vorhandenen Shell ausgeführt. Alle vom Skript erstellten oder geänderten Variablen bleiben verfügbar, nachdem das Skript abgeschlossen wurde. http://ss64.com/bash/source.html

Ich habe es kompiliert bash-4.2und im gdbDebugger ausgeführt.

Dies sind die letzten Befehle, die vor dem Beenden ausgeführt werden:

(gdb) 
bash: exec: non-existing-file: не найден
163       exit_value = EX_NOTFOUND; /* As per Posix.2, 3.14.6 */
(gdb) 
165       goto failed_exec;
(gdb) 
235   FREE (command);
(gdb) 
237   if (subshell_environment || (interactive == 0 && no_exit_on_failed_exec == 0))
(gdb)
238     exit_shell (exit_value);
(gdb)
[Inferior 1 (process 4034) exited with code 0177]

Variablen drucken:

(gdb) p subshell_environment 
$1 = 0
(gdb) p interactive
$2 = 0
(gdb) p no_exit_on_failed_exec 
$3 = 0

Es stellt sich heraus, dass die Shell nicht interaktiv ist ( interactive=0), während das eingebaute System beschafft wird exec. Dies ist der Grund für ein solches Verhalten. Es widerspricht dem dokumentierten Verhalten, man könnte also sagen, Sie haben einen Fehler gefunden.

Die Änderung non-interactive, interactive=0geschieht hier ( evalfile.c):

interactive:
Old value = 1
New value = 0
_evalfile (filename=0xa014c8 "exec1.sh", flags=14)
at evalfile.c:226
223  if (flags & FEVAL_NONINT)
224    interactive = 0;

wegen flags=14. flagssind in der function source_file( evalfile.c) einen Level höher gesetzt :

#1  0x0000000000485dcf in source_file (filename=0x9fb8c8 "exec1.sh", sflags=0)

338  flags = FEVAL_BUILTIN|FEVAL_UNWINDPROT|FEVAL_NONINT;
339  if (sflags)
340    flags |= FEVAL_NOPUSHARGS;
341  /* POSIX shells exit if non-interactive and file error. */
342  if (posixly_correct && interactive_shell == 0 && executing_command_builtin == 0)
343    flags |= FEVAL_LONGJMP;
344  rval = _evalfile (filename, flags);

Und die Definitionen sind:

#define FEVAL_BUILTIN       0x002
#define FEVAL_UNWINDPROT    0x004
#define FEVAL_NONINT        0x008

-> Flags 1110 = 14.

So, wie ich es verstehe, ist es ein Fehler: sourceführt Befehle in der aktuellen Shell aus, setzt aber das Flag FEVAL_NONINT 0x008- nicht interaktiv (Fehler hier):, evalfile.cin source_file:

338 flags = FEVAL_BUILTIN|FEVAL_UNWINDPROT|FEVAL_NONINT;

Ich habe ein Problem in einem Bug-Tracker erstellt:

http://savannah.gnu.org/support/index.php?108980

Würde es sehen.


EDIT1: Als bash Supporter kommentierte das Ticket

"Die Shell ist derzeit nicht interaktiv, wenn ein Dateiargument in die integrierte Quelle eingelesen wird (interactive == 0), obwohl die Shell selbst interaktiv ist (interactive_shell == 1)."

Auch, wie er sagte, dürfte sich dieses Verhalten in naher Zukunft nicht ändern.

Die Frage scheint jetzt geschlossen.

John_West
quelle