Platzieren Klammern den Befehl wirklich in einer Subshell?

Platzieren Klammern den Befehl wirklich in einer Subshell?

Soweit ich gelesen habe, sollte ein Befehl, der in Klammern steht, in einer Subshell ausgeführt werden, ähnlich wie ein Skript. Wenn das zutrifft, wie sieht es dann die Variable x, wenn x nicht exportiert wird?

x=1

Die Ausführung (echo $x)auf der Kommandozeile ergibt 1

Das Ausführen echo $xin einem Skript führt wie erwartet zu nichts

Antwort1

Eine Subshell beginnt als nahezu identische Kopie des ursprünglichen Shell-Prozesses. Unter der Haube ruft die Shell denforkSystemaufruf 1 , der einen neuen Prozess erstellt, dessen Code und Speicher Kopien 2 sind . Wenn die Subshell erstellt wird, gibt es nur sehr wenige Unterschiede zwischen ihr und ihrem übergeordneten Element. Insbesondere haben sie dieselben Variablen. Sogar die $$spezielle Variable behält in Subshells denselben Wert: Es ist die Prozess-ID der ursprünglichen Shell. Ähnlich $PPIDverhält es sich mit der PID des übergeordneten Elements der ursprünglichen Shell.

Einige Shells ändern einige Variablen in der Subshell. Bash ≥4.0 setzt BASHPIDauf die PID des Shell-Prozesses, die sich in Subshells ändert. Bash, zsh und mksh sorgen dafür, $RANDOMdass in der übergeordneten Shell und in der Subshell unterschiedliche Werte entstehen. Aber abgesehen von eingebauten Sonderfällen wie diesen haben alle Variablen in der Subshell denselben Wert wie in der Originalshell, denselben Exportstatus, denselben Nur-Lese-Status usw. Alle Funktionsdefinitionen, Aliasdefinitionen, Shell-Optionen und andere Einstellungen werden ebenfalls übernommen.

Eine von erstellte Subshell (…)hat die gleichen Dateideskriptoren wie ihr Ersteller. Einige andere Methoden zum Erstellen von Subshells ändern einige Dateideskriptoren, bevor Benutzercode ausgeführt wird. Beispielsweise wird die linke Seite einer Pipe in einer Subshell 3 ausgeführt , wobei die Standardausgabe mit der Pipe verbunden ist. Die Subshell beginnt auch mit dem gleichen aktuellen Verzeichnis, der gleichen Signalmaske usw. Eine der wenigen Ausnahmen besteht darin, dass Subshells keine benutzerdefinierten Traps erben: ignorierte Signale ( ) bleiben in der Subshell ignoriert, aber andere Traps (trap '' SIGNALtrap CODESIGNAL) werden auf die Standardaktion 4 zurückgesetzt .

Eine Subshell unterscheidet sich daher von der Ausführung eines Skripts. Ein Skript ist ein separates Programm. Dieses separate Programm kann zufällig auch ein Skript sein, das vom gleichen Interpreter wie das übergeordnete Programm ausgeführt wird, aber dieser Zufall verleiht dem separaten Programm keine besondere Sichtbarkeit der internen Daten des übergeordneten Programms. Nicht exportierte Variablen sind interne Daten. Wenn also der Interpreter für das untergeordnete Shell-Skripthingerichtet, sieht es diese Variablen nicht. Exportierte Variablen, also Umgebungsvariablen, werden an ausgeführte Programme übertragen.

Daher:

x=1
(echo $x)

druckt 1, weil die Untershell eine Replikation der Shell ist, die sie erzeugt hat.

x=1
sh -c 'echo $x'

führt zufällig eine Shell als Kindprozess einer Shell aus, aber die xin der zweiten Zeile hat keine Verbindung mehr mit der xin der zweiten Zeile als in

x=1
perl -le 'print $x'

oder

x=1
python -c 'print x'

1 Sofern die Shell das Forking nicht optimiert, aber das Forking so weit wie nötig emuliert, um das Verhalten des ausgeführten Codes beizubehalten. Ksh93 optimiert viel, andere Shells tun dies meist nicht.
2 Semantisch gesehen sind sie Kopien. Aus Implementierungssicht wird viel geteilt.
3 Für die rechte Seite hängt es von der Shell ab.
4 Wenn Sie dies testen, beachten Sie Folgendes:Dinge wie$(trap)kann die Fallen der ursprünglichen Shell melden. Beachten Sie auch, dass viele Shells Fehler in Sonderfällen mit Fallen haben. Zum BeispielAbonnierenweist darauf hin, dass ab Bash 4.3 im Fall „zwei Untershells“ bash -x -c 'trap "echo ERR at \$BASH_SUBSHELL \$BASHPID" ERR; set -E; false; echo one subshell; (false); echo two subshells; ( (false) )'der ERRTrap von der verschachtelten Untershell aus ausgeführt wird, nicht jedoch der ERRTrap von der Zwischen-Untershell – set -Edie Option sollte den Trap an alle Untershells weitergeben ERR, aber die Zwischen-Untershell wird wegoptimiert und ist daher nicht vorhanden, um ihren ERRTrap auszuführen.

Antwort2

Offensichtlich ja, wie in der gesamten Dokumentation angegeben, wird ein in Klammern gesetzter Befehl in einer Subshell ausgeführt.

Die Untershell erbt eine Kopie aller Variablen des übergeordneten Elements. Der Unterschied besteht darin, dass alle Änderungen, die Sie in der Untershell vornehmen, nicht auch im übergeordneten Element vorgenommen werden.

Die Manpage von ksh macht dies etwas deutlicher als die von bash:

man ksh:

Ein in Klammern gesetzter Befehl wird in einer Unter-Shell ausgeführt, ohne nicht exportierte Variablen zu entfernen.

man bash:

(Liste)

list wird in einer Subshell-Umgebung ausgeführt (siehe unten „UMGEBUNG FÜR BEFEHLSAUSFÜHRUNG“). Variablenzuweisungen und integrierte Befehle, die die Umgebung der Shell betreffen, bleiben nach Abschluss des Befehls nicht wirksam.

Befehlsausführungsumgebung

Die Shell verfügt über eine Ausführungsumgebung, die aus Folgendem besteht: [...] Shell-Parameter, die durch Variablenzuweisung festgelegt werden [...].
Befehlsersetzung, mit Klammern gruppierte Befehle und asynchrone Befehle werden in einer Subshell-Umgebung aufgerufen, die ein Duplikat der Shell-Umgebung ist, [...]

verwandte Informationen