SHLVL 和 BASH_SUBSHELL 兩個變量的區別


SHLVL 是記錄多個 Bash 進程實例嵌套深度的累加器,而 BASH_SUBSHELL 是記錄一個 Bash 進程實例中多個子 Shell(subshell)嵌套深度的累加器。

看不懂上面這句話不要緊,因為是我臨時編的。其實如果你混淆了這兩個變量,我猜你多半是對 BASH_SUBSHELL 這個變量名中的 subshell 概念不清,下面我們就講講什么是 subshell,什么不是。

很多人誤以為在 Bash 里面再執行一次 Bash,或者再執行一個 Shell 腳本就是進入子 Shell 了,所以他們會有下面這樣的疑問:

$ bash # 執行另外一個 bash 命令

$ echo $BASH_SUBSHELL 

$ 0 # 怎么還是 0,我這不是在上個 Shell 的子 Shell 里嗎?

$ echo ' echo $BASH_SUBSHELL ' > test; chmod +x test; ./test;

$ 0 # 難道再執行 Shell 腳本也不是子 Shell?

然而並不是,這些都不是子 Shell,這種情況只能描述成是“當前 Shell 啟動了個外部命令,而這個外部命令剛好是個 Shell”,真正的子 Shell 是不需要重新執行硬盤上的外部命令的,全部是內存中的操作。上面這個示例中的 BASH_SUBSHELL 都應該替換成 SHLVL 才能看到累加效果。

有幾本書給子 Shell 下過定義:

Advanced Bash-Scripting Guide 說:

A subshell is a child process launched by a shell (or shell script).

Bash Cookbook 說:

A subshell is a forked copy of the parent shell and shares it’s environment.

The Korn Shell: Unix & Linux Programming Manual 說:

A subshell is a separate copy of the parent shell, so variables, functions, and aliases from the parent shell are available to the subshell

第一本書流傳較廣,但它這句話說的太泛了,很容易誤導人,雖然子 Shell 的確是當前 Shell 的子進程,但當前 Shell 的子進程不一定都是子 Shell(可能已經替換成了其他程序)。在 Bash 里面,只有特定的語法才會讓代碼進入子 Shell,比如管道兩邊的命令,比如用小括號括起來等等:

$ (echo $BASH_SUBSHELL)

1

$ ( ( ( ( (echo $BASH_SUBSHELL) ) ) ) )

5

真正的子 Shell 可以訪問其父 Shell 的任何變量,而通過再執行一次 bash 命令所啟動的 Shell 只能訪問其父 Shell 傳來的環境變量。這篇教程里面專門寫了個例子:

For an example of the difference between a subshell and a child process that happens to be a shell:

unset a; a=1
(echo "a is $a in the subshell") sh -c 'echo "a is $a in the child shell"'

In the subshell, the regular shell variable a is visible; but because it is not exported, the full child process does not see it.

上面的例子中把當前 Shell 執行外部命令 sh 啟動的 Shell 叫做 child shell,可惜在中文里還是得翻譯成子 Shell。。。

從 c 語言層面講,真正的子 Shell 是當前 Shell 進程調用了 fork() 函數,在內存中復制出一個幾乎一模一樣的子進程。而執行 bash 命令啟動的所謂 child shell 是在執行 fork() 函數的基礎上,又執行了一次 execve() 函數,execve() 函數會重新加載硬盤上的 bash 命令並執行,替換剛才 fork 出來的那個 shell 進程,除了傳入的環境變量外,是個嶄新的進程。

總結一下就是說,SHLVL 變量是記錄了所謂的 child shell 的嵌套深度,而 BASH_SUBSHELL 是記錄了 subshell 的嵌套深度。

把 child shell 叫成子 Shell,在口頭上說說還可以,因為中文里沒有其它什么好的叫法用來指代它,但你心里得明白,這不是術語子 Shell 真正的含義。

寫到這里,我想這篇文章的標題應該改成“什么是子 Shell”了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM