In der POSIX-Terminologie ist eine Subshell-Umgebung mit dem Begriff der Shell-Ausführungsumgebung verknüpft .
Eine Subshell-Umgebung ist eine separate Shell-Ausführungsumgebung, die als Duplikat der übergeordneten Umgebung erstellt wird. Diese Ausführungsumgebung enthält Dinge wie geöffnete Dateien, Umask, Arbeitsverzeichnis, Shell-Variablen / Funktionen / Aliase ...
Änderungen an dieser Subshell-Umgebung wirken sich nicht auf die übergeordnete Umgebung aus.
Traditionell in der Bourne-Shell oder ksh88, auf der die POSIX-Spezifikation basiert, wurde dies durch Verzweigen eines untergeordneten Prozesses durchgeführt.
Die Bereiche, in denen POSIX die Ausführung von Befehlen in einer Subshell-Umgebung erfordert oder zulässt, sind diejenigen, in denen ksh88 traditionell einen untergeordneten Shell-Prozess gabelte.
Implementierungen werden jedoch nicht gezwungen, dafür einen untergeordneten Prozess zu verwenden.
Eine Shell kann stattdessen wählen, diese separate Ausführungsumgebung nach Belieben zu implementieren.
Zum Beispiel speichert ksh93 die Attribute der übergeordneten Ausführungsumgebung und stellt sie nach Beendigung der Subshell-Umgebung in Kontexten wieder her, in denen das Forking vermieden werden kann (eine Optimierung, da das Forking auf den meisten Systemen recht teuer ist).
Zum Beispiel in:
cd /foo; pwd
(cd /bar; pwd)
pwd
POSIX erfordert die cd /foo
Ausführung in einer separaten Umgebung und die Ausgabe von:
/foo
/bar
/foo
Es ist nicht erforderlich, dass es in einem separaten Prozess ausgeführt wird. Wenn zum Beispiel stdout zu einer kaputten Pipe wird, kann beim pwd
Ausführen in der Subshell-Umgebung das SIGPIPE sehr gut an den einzigen Shell-Prozess gesendet werden.
Die meisten Shells einschließlich bash
implementieren es, indem sie den Code (...)
in einem untergeordneten Prozess auswerten (während der übergeordnete Prozess auf seine Beendigung wartet), aber ksh93 wird stattdessen beim Ausführen des Codes im (...)
selben Prozess alles im selben Prozess ausführen :
- Denken Sie daran, dass es sich in einer Subshell-Umgebung befindet.
- auf
cd
, speichern Sie das vorherige Arbeitsverzeichnis ( in der Regel auf einem Dateideskriptor mit O_CLOEXEC geöffnet), außer dem Wert der OLDPWD, PWD Variablen und alles , was cd
ändern könnte und dann tut diechdir("/bar")
- Bei der Rückkehr von der Subshell wird das aktuelle Arbeitsverzeichnis wiederhergestellt (mit einem
fchdir()
auf dem gespeicherten fd) und alles andere, was die Subshell möglicherweise geändert hat.
Es gibt Kontexte, in denen ein untergeordneter Prozess nicht vermieden werden kann. ksh93 gibt sich nicht ein:
var=$(subshell)
(subshell)
Aber tut in
{ subshell; } &
{ subshell; } | other command
Das heißt, die Fälle, in denen Dinge in separaten Prozessen ausgeführt werden müssen, damit sie gleichzeitig ausgeführt werden können.
ksh93-Optimierungen gehen noch weiter. Zum Beispiel während in
var=$(pwd)
Die meisten Shells würden einen Prozess pwd
verzweigen, das untergeordnete Element den Befehl ausführen lassen, dessen Standardausgabe in eine Pipe umgeleitet wird pwd
, das aktuelle Arbeitsverzeichnis in diese Pipe schreiben und der übergeordnete Prozess das Ergebnis am anderen Ende der Pipe lesen und ksh93
all das von beiden virtualisieren erfordert die Gabel noch das Rohr. Eine Gabel und ein Rohr würden nur für nicht eingebaute Befehle verwendet.
Beachten Sie, dass es andere Kontexte als Unterschalen gibt, für die Schalen einen untergeordneten Prozess verzweigen. Um beispielsweise einen Befehl auszuführen, der in einer separaten ausführbaren Datei gespeichert ist (und der kein Skript für denselben Shell-Interpreter ist), müsste eine Shell einen Prozess verzweigen, um diesen Befehl darin auszuführen, wie dies sonst nicht der Fall wäre Sie können weitere Befehle ausführen, nachdem dieser Befehl zurückgegeben wurde.
Im:
/bin/echo "$((n += 1))"
Dies ist keine Subshell. Der Befehl wird in der aktuellen Shell-Ausführungsumgebung ausgewertet. Die n
Variable der aktuellen Shell-Ausführungsumgebung wird inkrementiert. Die Shell gibt jedoch einen untergeordneten Prozess aus, um diesen /bin/echo
Befehl mit der Erweiterung $((n += 1))
als Argument auszuführen .
Viele Shells implementieren eine Optimierung, indem sie einen untergeordneten Prozess nicht dazu veranlassen, diesen externen Befehl auszuführen, wenn es sich um den letzten Befehl eines Skripts oder einer Unterschale handelt (für die Unterschalen, die als untergeordnete Prozesse implementiert sind). ( bash
Dies geschieht jedoch nur, wenn dieser Befehl der einzige Befehl der Subshell ist.)
Dies bedeutet, dass bei diesen Shells, wenn der letzte Befehl in der Subshell ein externer Befehl ist, die Subshell keinen zusätzlichen Prozess erzeugt. Wenn Sie vergleichen:
a=1; /bin/echo "$a"; a=2; /bin/echo "$a"
mit
a=1; /bin/echo "$a"; (a=2; /bin/echo "$a")
Es wird die gleiche Anzahl von Prozessen erstellt, nur im zweiten Fall wird die zweite Verzweigung früher ausgeführt, so dass die a=2
Zweig in einer Subshell-Umgebung ausgeführt wird.