Warum ist eine Variable in einer Subshell sichtbar?

18

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 varVariable, obwohl sie nicht exportiert wird, und in dem ./fileFall, 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 straceherauszufinden, wie dies geschieht, und überraschenderweise festgestellt, dass bash dieselben Argumente für den Klonsystemaufruf verwendet. Dies bedeutet, dass sowohl der gegabelte Prozess ()als ./fileauch 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 ./fileFall, obwohl dieselben Argumente auf dem Klonsystemaufruf basieren?

user3718463
quelle
vinc17 sag wahr, auch wenn du pstree bekommst, wenn du subshell hast, glaubst du das.
PersianGulf

Antworten:

15

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 auf execveund ä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.

vinc17
quelle
Oky, aber ich habe jetzt versucht, die Shell zu starten, und ich habe versucht, ./file auszuführen, aber ich kann keinen Aufruf für exec finden, und daher sollte der Adressraum für beide Prozesse gleich sein. Wie kann dies erklärt werden?
user3718463
@ user3718463 Haben Sie die -fOption stracezum Nachverfolgen von Kindern verwendet? Das ist notwendig, um die Execs zu finden.
Vinc17
yup, ich finde es heraus, vielen Dank, mir fehlte die Option -f, und so kann ich den Aufruf von exec sys nicht finden
user3718463
16

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 forkhandelt es sich um einen Sonderfall des allgemeineren cloneSystemaufrufs, den Sie im straceProtokoll beobachtet haben . Das Kind führt einen Teil des Shell-Skripts aus. Der untergeordnete Prozess wird als Subshell bezeichnet . Das direkteste Konstrukt dieser Art ist command1 &: command1Wird 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 Pipes command3 | command4(wird command3in einer Subshell ausgeführt, wird command4in 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 Variable BASHPIDauf die Prozess-ID des untergeordneten Prozesses.

Bei der Ausführung ./filewird ein externer Befehl ausgeführt. Zunächst gibt die Shell einen untergeordneten Prozess aus. dieses Kind Prozess dann ausführt (mit dem execveSystemaufruf) der ausführbaren Datei ./file. Ein untergeordneter Prozess erbt Prozessattribute seiner Eltern: Umgebung, aktuelles Verzeichnis usw. Interne Aspekte der Anwendung gehen beim execveAufruf 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) nicht execve.

Gilles 'SO - hör auf böse zu sein'
quelle
Diese Antwort hat einige Dinge für mich geklärt. Das einzige, was ich nicht verstehe, ist dieser Satz im zweiten Absatz: "Das Kind führt einen Teil des Shell-Skripts aus." Auf welches Shell-Skript wird verwiesen?
flow2k
@ flow2k Das Skript (dh das Programm), das die Shell interpretiert.
Gilles 'SO - hör auf böse zu sein'