Wie kann ich die PID einer Subshell erhalten?

12

Wie kann ich die PID einer Subshell erhalten?

Zum Beispiel:

$ echo $$
16808

Dies funktioniert nicht, da die ursprüngliche Shell erweitert wird $$:

$ ( echo $$ )
16808

Warum funktioniert das einfache Zitieren nicht? Erweitert $$sich die Unterschale nicht in sich selbst, nachdem die ursprüngliche Shell das einfache Anführungszeichen entfernt hat?

$ ( echo '$$' )
$$

Warum funktioniert das auch evalnicht? Wird evalvon der Subshell betrieben? Warum gibt es mir die PID der Originalschale?

$ ( eval echo '$$' )
16808

Vielen Dank.

Tim
quelle
Ich schlage eine Wiedereröffnung vor, da die Fragen meiner Meinung nach wesentlich anders sind ("Wie vermeide ich eine $$Erweiterung?" Im Vergleich zu "Unterschiedliche PID in der Unterschale").
Peter

Antworten:

12

Zusätzlich zu bash's $BASHPIDkönnen Sie dies portabel tun mit:

pid=$(exec sh -c 'echo "$PPID"')

Beispiel:

(pid=$(exec sh -c 'echo "$PPID"'); echo "$$ $pid")

Sie können daraus eine Funktion machen:

# usage getpid [varname]
getpid(){
    pid=$(exec sh -c 'echo "$PPID"')
    test "$1" && eval "$1=\$pid"
}

Beachten Sie, dass einige Shells (z. B. zshoder ksh93) NICHT einen Unterprozess für jede mit erstellte Subshell starten (...). In diesem Fall $pidkann es sein, dass es dasselbe ist wie $$, was genau richtig ist, da dies die PID des Prozesses ist, von dem aus getpidaufgerufen wurde.

Mosvy
quelle
1
Nein. Nehmen Sie jedoch bitte nicht an, dass eine Subshell notwendigerweise in einem Subprozess ausgeführt wird - dies ist beispielsweise nicht der Fall ksh93.
Mosvy
1
In ksh93 funktioniert es einwandfrei - es gibt immer die PID des Prozesses zurück, von dem es aufgerufen wurde. Es ist das (...)aus dem Beispiel, das möglicherweise keinen separaten Prozess erzeugt, wie es in bash.
Mosvy
1
Einige Shells mögen zshoder yashoptimieren a fork()für den letzten Befehl in einer Subshell. Sie können sogar die Abzweigung für die Unterschale optimieren, wenn dies der letzte Befehl in einem Skript ist, sodass Sie getpidsogar das übergeordnete Element von melden können $$. Sie können Folgendes definieren getpid: getpid(){ sh -c 'echo "$PPID"'; return; }Deaktivieren, um das Problem zu vermeiden.
Stéphane Chazelas
1
@HaroldFischer 1. Ohne die execoder ohne diese Optimierung ist der sh -c ...Prozess ein Enkelkind anstelle eines untergeordneten Elements des Prozesses, in dem eine $(...)Befehlssubstitution verwendet wird, und $PPIDist die $(...)PID der Subshell. Genau das passiert im obigen set -E+ trap ERRBash-Beispiel.
Mosvy
1
@HaroldFischer 2. test "$1"testet, ob $1es sich um eine leere Zeichenfolge handelt oder nicht - eine schnelle und schmutzige Methode, um zu testen, ob dieser Funktion ein varnameArgument zum Zuweisen der PID zugewiesen wurde oder nicht; Die Verwendung einer Funktion war überhaupt nicht die beste Idee .
Mosvy
18
$ echo $BASHPID
37152
$ ( echo $BASHPID )
18633

Aus dem Handbuch:

BASHPID

Erweitert sich auf die Prozess-ID des aktuellen Bash-Prozesses. Dies unterscheidet sich $$unter bestimmten Umständen von Unterschalen, bei denen Bash nicht neu initialisiert werden muss.

$

Erweitert sich auf die Prozess-ID der Shell. In einer ()Subshell wird die Prozess-ID der aktuellen Shell und nicht die Subshell erweitert.

Verbunden:

Kusalananda
quelle
Vielen Dank. (1) Was bedeutet "neu initialisiert"? (2) Könnten Sie auch überlegen, warum diese Methoden, die ich versucht habe, nicht funktionieren?
Tim
@ Tim Ich glaube, das wird von Gilles hier beantwortet . Bash nicht aktualisiert einfach nicht $$in Subshells.
Kusalananda
Meinst du, ich sollte immer $ BASHPID anstelle von $$ verwenden, auf jeden Fall in Bash? Wann soll ich welche verwenden?
Tim
@Tim Es hängt davon ab, ob Sie in einer Subshell die Prozess-ID des Skripts oder der Subshell erhalten möchten. Beide Möglichkeiten stehen zur Verfügung und welche die richtige ist, hängt von der Anwendung ab. Darauf kann keine spezifischere Antwort gegeben werden.
Kusalananda
1
@Tim Die PID einer übergeordneten Shell einer Subshell kann nur dann zuverlässig gefunden werden, wenn Sie $BASHPIDin einer Variablen speichern und diese in der Subshell verwenden. Es gibt $PPID, aber das ist die übergeordnete PID der Shell in demselben Sinne wie $$die PID der Shell (sie wird in einer Subshell nicht zurückgesetzt). Es gibt keine $BASHPPIDVariable.
Kusalananda