Bash und Subshells

Bash und Subshells

Soweit ich es aus den Online-Dokumenten entnehme, sollte das Folgende eine Subshell für den mit eingebetteten Teil des Befehls erzeugen {}:

$ bash -c '{ sleep 10; echo "Sleeping process", $$; }  & echo $$; '
11237
 Sleeping process, 11237

Wie Sie jedoch sehen, ist die Prozess-ID in beiden Fällen dieselbe. Was übersehe ich? Vielen Dank für alle Hinweise.

Antwort1

Das {}gruppiert einfach Befehle in der aktuellen Shell, während ()eine neue Subshell gestartet wird. Sie setzen die gruppierten Befehle jedoch in den Hintergrund, was tatsächlich ein neuer Prozess ist; wenn es im aktuellen Prozess wäre, könnte es unmöglich in den Hintergrund verschoben werden. Meiner Meinung nach ist es einfacher, so etwas mit strace zu sehen:

sauer@humpy:~$ strace -f -etrace=process bash -c '{ sleep 10; echo "Sleeping process", $BASHPID, $BASH_SUBSHELL; }  & echo $BASHPID;' > /tmp/file
execve("/bin/bash", ["bash", "-c", "{ sleep 10; echo \"Sleeping proce"...], [/* 20 vars */]) = 0
arch_prctl(ARCH_SET_FS, 0x7f15a90da700) = 0
clone(Process 25347 attached
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f15a90da9d0) = 25347
[pid 25346] exit_group(0)               = ?
clone(Process 25348 attached (waiting for parent)
Process 25348 resumed (parent 25347 ready)
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f15a90da9d0) = 25348
[pid 25348] execve("/bin/sleep", ["sleep", "10"], [/* 20 vars */] <unfinished ...>
[pid 25347] wait4(-1, Process 25347 suspended
 <unfinished ...>
[pid 25348] <... execve resumed> )      = 0
[pid 25348] arch_prctl(ARCH_SET_FS, 0x7f922ad16700) = 0
[pid 25348] exit_group(0)               = ?
Process 25347 resumed
Process 25348 detached
<... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 25348
--- SIGCHLD (Child exited) @ 0 (0) ---
wait4(-1, 0x7fffaa432ad8, WNOHANG, NULL) = -1 ECHILD (No child processes)
exit_group(0)                           = ?
Process 25347 detached

sauer@humpy:~$ cat /tmp/file
25346
Sleeping process, 25347, 1

Beachten Sie, dass der Bash-Befehl gestartet wird und dann ein neues Kind mit erstellt clone(). Wenn Sie die Option -f für strace verwenden, werden auch Kindprozesse verfolgt, sodass ein weiterer Fork (also ein „Klon“) angezeigt wird, wenn es sleep ausführt. Wenn Sie die Option -f weglassen, sehen Sie nur den einen Klonaufruf, wenn der Hintergrundprozess erstellt wird:

sauer@humpy:~$ strace -etrace=clone bash -c '{ sleep 10; echo "Sleeping process", $BASHPID, $BASH_SUBSHELL; }  & echo $BASHPID;' > /tmp/file
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f2bdd5399d0) = 26394
sauer@humpy:~$ strace -etrace=process bash -c '{ sleep 10; echo "Sleeping process", $BASHPID, $BASH_SUBSHELL; }  & echo $BASHPID;' > /dev/null
execve("/bin/bash", ["bash", "-c", "{ sleep 10; echo \"Sleeping proce"...], [/* 20 vars */]) = 0
arch_prctl(ARCH_SET_FS, 0x7fd01ae86700) = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fd01ae869d0) = 26706
exit_group(0)                           = ?

Wenn Sie wirklich nur wissen möchten, wie oft Sie neue Prozesse erstellen, können Sie dies noch weiter vereinfachen, indem Sie nur auf Fork- und Clone-Aufrufe achten:

sauer@humpy:~$ strace -etrace=fork,clone bash -c '{ sleep 10; echo "Sleeping process", $BASHPID, $BASH_SUBSHELL; }  & echo $BASHPID;' > /dev/null
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f467fa769d0) = 27025

Antwort2

Aktualisieren:

Bitte lesen Sie die Antwort oben. Mit {} erstelle ich nicht wirklich eine Unterschale. Stattdessen gibt mir die Hintergrundinformation hier die Antwort, nach der ich gesucht habe.

Okay, also im Grunde habe ich den falschen "Indikator" für die Erstellung von Subshells verwendet. Ich habe davon erfahren BASHPIDvonHierund wenn ich das zusätzlich zu verwende BASH_SUBSHELL, kann ich sehen, dass tatsächlich eine Untershell erstellt wird.

Testbefehl:

$ bash -c '{ sleep 10; echo "Sleeping process", $BASHPID, $BASH_SUBSHELL; }  & echo $BASHPID; '
12074
 Sleeping process, 12075, 1

Ein weiterer Testbefehl, um auch die übergeordnete Prozess-ID sowohl für die Shell als auch für die Sub-Shell anzuzeigen:

$ bash -c '{ sleep 10; echo "Sleeping process", $BASHPID, $BASH_SUBSHELL, $PPID; }  & echo $BASHPID, $PPID; '
12411, 9128
$ Sleeping process, 12412, 1, 9128

verwandte Informationen