Bash

沒有子shell程序的子shell範例

  • November 17, 2021

我最近才了解到“子shell”與“子shell程序”不同(例如參見“子shell”和“子程序”之間的確切區別是什麼?以及子shell子程序的POSIX定義)。

為了讓自己相信這一點,我正在尋找一個命令來說明(證明)子shell 是在沒有產生子shell 的情況下創建的。

現在,我嘗試的一切似乎都會在創建子shell時產生一個子shell:

$ echo $BASHPID; (pwd; cd ..; echo $BASHPID; pwd); pwd      # `( ...)` executed in a subshell
                                                           # and in a child-shell process

$ >&2 ps | ps       # Theoretically executed in two subshells and apparently without child-shells
                   # but I cannot be sure due to the outcome of the next example

$ $ >&2 echo $BASHPID | ps      # `ps` doesn't display a child-shell for the execution of `echo`
953790                          # but `echo $BASHPID` shows a new process that is necessarily
   PID TTY         TIME CMD    # a child-shell since echo is a built-in 
948538 pts/2   00:00:00 bash
953791 pts/2   00:00:00 ps

我正在尋找一種方法來證明擁有子外殼並不一定意味著擁有子外殼……

重擊 5.0.17

在 bash shell 中,子 shell 是通過派生一個子程序來實現的,因此您不會看到子 shell 沒有在該 shell 的子程序中執行的情況。

ksh93 是我所知道的唯一一個在子shell 可能時跳過分叉的shell(這種優化仍然有很多錯誤,並且在AT&T 解散編寫它的團隊後試圖維護它的後續人員已經考慮刪除)。

例如,如果您這樣做:

strace ksh93 -c 'pwd; (cd /; umask 0; pwd; exit 2); pwd'

您會看到 ksh93 沒有分叉任何程序,而是執行以下操作:

openat(AT_FDCWD, ".", O_RDONLY|O_PATH)  = 3
fcntl(3, F_DUPFD, 10)                   = 10
close(3)                                = 0
fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
[...]

它將目前目錄保存在 fd 10 上。然後:

chdir("/")                              = 0
umask(000)                              = 002

這會更改子 shell 中的目前目錄和 umask。並且在子shell終止時(exit 2不呼叫_exit()系統呼叫):

fchdir(10)                              = 0
close(10)                               = 0

恢復目前工作目錄並:

umask(002)                              = 000

恢復umask。

一些像 FreeBSD 的 shellsh在非常特殊的情況下可以跳過分叉,比如:

var=$(printf %04d "$n")

(這裡有一個printf內置的,並且沒有對環境進行任何更改)。

在管道中,所有組件必須同時執行,因此它們必須在單獨的程序中執行,即使在 ksh93 中也是如此。

bash中,它們都在子程序中執行。在 AT&T ksh 或 zsh 中,或 with bash -O lastpipe(非互動式時),最右邊的不是(當然,您仍然需要 fork 一個子程序來執行外部命令,例如ps)。

您看不到額外的bash程序,ps >&2 | ps或者(ps)因為ps直接在該子程序中執行,在執行之前ps是 bash 解釋管道組件:子外殼。例如,在:

n=0; /bin/true "$((n=1))" | /bin/echo "$((n=2))"; echo "$n"

您將在 bash 和zsh /ksh93 中看到2and 。並在子程序中執行,直接在之前完成的子shell程序中執行,在 bash 中對於(and ) 相同,但在/ /中,是在主 shell 程序中完成的,並且子程序僅用於執行該外部實用程序,就像您不作為管道的一部分執行時一樣。0``2``2``/bin/true``/bin/echo``/bin/true``n=1``/bin/echo``n=2``zsh``ksh``bash -O lastpipe``n=2``/bin/echo "$((n=2))"

bash(與zsh/相反ksh)中,您確實在 中看到了一個額外的bash過程(: anything; ps),僅當子shell 只有一個外部命令時才進行優化,您需要在exec那里手動進行優化:(: anything; exec ps).

也一樣{ ps; } | cat

引用自:https://unix.stackexchange.com/questions/677139