Das Learning Bash Book erwähnt, dass eine Subshell nur Umgebungsvariablen und Dateideskriptoren usw. erbt und keine Variablen, die nicht exportiert werden:
$ var=15
$ (echo $var)
15
$ ./file # this file include the same command echo $var
$
Wie ich weiß, erstellt die Shell zwei Subshells für ()
und für ./file
, aber warum ()
identifiziert die Subshell in diesem Fall die var
Variable, obwohl sie nicht exportiert wird, und in dem ./file
Fall, dass sie sie nicht identifiziert hat?
# Strace for ()
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f24558b1a10) = 25617
# Strace for ./file
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f24558b1a10) = 25631
Ich habe versucht strace
herauszufinden, wie dies geschieht, und überraschenderweise festgestellt, dass bash dieselben Argumente für den Klonsystemaufruf verwendet. Dies bedeutet, dass sowohl der gegabelte Prozess ()
als ./file
auch der Prozessadressraum des übergeordneten Prozesses identisch sind, also warum ()
Ist in diesem Fall die Variable für die Subshell sichtbar und geschieht dies nicht für den ./file
Fall, obwohl dieselben Argumente auf dem Klonsystemaufruf basieren?
Antworten:
Das Learning Bash Book ist falsch. Subshells erben alle Variablen. Gerade
$$
(die PID der Originalschale) wird beibehalten. Der Grund dafür ist , dass für ein Sub - Shell, die Shell gerade Gabeln und nicht eine neue Shell (im Gegenteil, wenn Sie geben nicht ausführt./file
, ein neuer Befehl ausgeführt wird , zB eine neue Schale, in dem Strace Ausgang, Blick aufexecve
und ähnlichen) . Im Grunde genommen handelt es sich also nur um eine Kopie (mit einigen dokumentierten Unterschieden).Hinweis: Dies ist nicht spezifisch für Bash. Dies gilt für jede Shell.
quelle
-f
Optionstrace
zum Nachverfolgen von Kindern verwendet? Das ist notwendig, um die Execs zu finden.Entweder Sie oder das Buch verwechseln eine Subshell mit einem Subprozess, der eine Shell ist.
Einige Shell-Konstrukte führen dazu, dass die Shell einen untergeordneten Prozess forkt . Unter Linux
fork
handelt es sich um einen Sonderfall des allgemeinerenclone
Systemaufrufs, den Sie imstrace
Protokoll beobachtet haben . Das Kind führt einen Teil des Shell-Skripts aus. Der untergeordnete Prozess wird als Subshell bezeichnet . Das direkteste Konstrukt dieser Art istcommand1 &
:command1
Wird in einer Subshell ausgeführt, und nachfolgende Befehle werden in der übergeordneten Shell ausgeführt. Andere Konstrukte, die eine Subshell erstellen, umfassen Befehlsersetzung$(command2)
und Pipescommand3 | command4
(wirdcommand3
in einer Subshell ausgeführt, wirdcommand4
in den meisten Shells in einer Subshell ausgeführt, jedoch nicht in ksh oder zsh).Eine Subshell ist eine Kopie des übergeordneten Prozesses. Sie verfügt also nicht nur über dieselben Umgebungsvariablen, sondern auch über dieselben internen Definitionen: Variablen (einschließlich
$$
der Prozess-ID des ursprünglichen Shell-Prozesses), Funktionen, Aliase, Optionen usw. Bevor der Code in der Subshell ausgeführt wird, setzt bash die VariableBASHPID
auf die Prozess-ID des untergeordneten Prozesses.Bei der Ausführung
./file
wird ein externer Befehl ausgeführt. Zunächst gibt die Shell einen untergeordneten Prozess aus. dieses Kind Prozess dann ausführt (mit demexecve
Systemaufruf) der ausführbaren Datei./file
. Ein untergeordneter Prozess erbt Prozessattribute seiner Eltern: Umgebung, aktuelles Verzeichnis usw. Interne Aspekte der Anwendung gehen beimexecve
Aufruf verloren: Nicht exportierte Variablen, Funktionen usw. sind Bash-Begriffe, von denen der Kernel nichts weiß, und Sie gehen verloren, wenn Bash ein anderes Programm ausführt. Selbst wenn dieses andere Programm zufällig ein Bash-Skript ist, wird es von einer neuen Instanz von Bash ausgeführt, die nicht weiß oder sich darum kümmert, dass der übergeordnete Prozess zufällig auch eine Instanz von Bash ist. Daher überlebt eine Shell-Variable (nicht exportierte Variable) nichtexecve
.quelle