如何檢測子Shell和子進程?
上節我們說了子 shell 和子進程的區別,這節就來看一下如何檢測它們。
我們都知道使用 $ 變量可以獲取當前進程的 ID,我在父 Shell 和子 Shell 中都輸出 $ 的值,只要它們不一樣,不就是創建了一個新的進程嗎?那我們就來試一下吧。
[mozhiyan@localhost ~]$ echo $$ #父Shell PID 3299 [mozhiyan@localhost ~]$ (echo $$) #組命令形式的子Shell PID 3299 [mozhiyan@localhost ~]$ echo "http://c.biancheng.net" | { echo $$; } #管道形式的子Shell PID 3299 [mozhiyan@localhost ~]$ read < <(echo $$) #進程替換形式的子Shell PID [mozhiyan@localhost ~]$ echo $REPLY 3299
你看,子 Shell 和父 Shell 的 ID 都是一樣的,哪有產生新進程了?作者你是不是騙人呢?
其實不是我騙人,而是你掉坑里了,因為 $ 變量在子 Shell 中無效!Base 官方文檔說,在普通的子進程中,$ 確實被展開為子進程的 ID;但是在子 Shell 中,$ 卻被展開成父進程的 ID。
除了 $,Bash 還提供了另外兩個環境變量——SHLVL 和 BASH_SUBSHELL,用它們來檢測子 Shell 非常方便。
SHLVL 是記錄多個 Bash 進程實例嵌套深度的累加器,每次進入一層普通的子進程,SHLVL 的值就加 1。而 BASH_SUBSHELL 是記錄一個 Bash 進程實例中多個子 Shell(sub shell)嵌套深度的累加器,每次進入一層子 Shell,BASH_SUBSHELL 的值就加 1。
1) 我們還是用實例來說話吧,先說 SHLVL。創建一個腳本文件,命名為 test.sh,內容如下:
#!/bin/bash echo "$SHLVL $BASH_SUBSHELL"
然后打開 Shell 窗口,依次執行下面的命令:
[mozhiyan@localhost ~]$ echo "$SHLVL $BASH_SUBSHELL" 2 0 [mozhiyan@localhost ~]$ bash #執行bash命令開啟一個新的Shell會話 [mozhiyan@localhost ~]$ echo "$SHLVL $BASH_SUBSHELL" 3 0 [mozhiyan@localhost ~]$ bash ./test.sh #通過bash命令運行腳本 4 0 [mozhiyan@localhost ~]$ echo "$SHLVL $BASH_SUBSHELL" 3 0 [mozhiyan@localhost ~]$ chmod +x ./test.sh #給腳本增加執行權限 [mozhiyan@localhost ~]$ ./test.sh 4 0 [mozhiyan@localhost ~]$ echo "$SHLVL $BASH_SUBSHELL" 3 0 [mozhiyan@localhost ~]$ exit #退出內層Shell exit [mozhiyan@localhost ~]$ echo "$SHLVL $BASH_SUBSHELL" 2 0
SHLVL 和 BASH_SUBSHELL 的初始值都是 0,但是輸出結果中 SHLVL 的值從 2 開始,我猜測 Bash 在初始化階段可能創建了子進程,我們暫時不用理會它,將關注點放在值的變化上。
仔細觀察的讀者應該會發現,使用 bash 命令開啟新的會話后,需要使用 exit 命令退出才能回到上一級 Shell 會話。bash ./test.sh
和chmod +x ./test.sh; ./test.sh
這兩種運行腳本的方式,在腳本運行期間會開啟一個子進程,運行結束后立即退出子進程。
2) 再說一下 BASH_SUBSHELL,請看下面的命令:
[mozhiyan@localhost ~]$ echo "$SHLVL $BASH_SUBSHELL" 2 0 [mozhiyan@localhost ~]$ (echo "$SHLVL $BASH_SUBSHELL") #組命令 2 1 [mozhiyan@localhost ~]$ echo "hello" | { echo "$SHLVL $BASH_SUBSHELL"; } #管道 2 1 [mozhiyan@localhost ~]$ var=$(echo "$SHLVL $BASH_SUBSHELL") #命令替換 [mozhiyan@localhost ~]$ echo $var 2 1 [mozhiyan@localhost ~]$ ( ( ( (echo "$SHLVL $BASH_SUBSHELL") ) ) ) #四層組命令 2 4
你看,組命令、管道、命令替換這幾種寫法都會進入子 Shell。
注意,“進程替換”看起來好像產生了一個子 Shell,其實只是玩了一個障眼法而已。進程替換只是借助文件在()
內部和外部的命令之間傳遞數據,但是它并沒有創建子 Shell;換句話說,()
內部和外部的命令是在一個進程(也就是當前進程)中執行的。
我們不妨來實際檢測一下:
[mozhiyan@localhost ~]$ echo "$SHLVL $BASH_SUBSHELL" 2 0 [mozhiyan@localhost ~]$ echo "hello" > >(echo "$SHLVL $BASH_SUBSHELL") 2 0
SHLVL 和 BASH_SUBSHELL 變量的值都沒有發生改變,說明進程替換既沒有進入子進程,也沒有進入子 Shell。
beylze編程學院,一個分享編程知識和seo優化知識的網站。跟著beylze一起學習,每天都有進步。
通俗易懂,深入淺出,一篇文章只講一個知識點。
文章不深奧,不需要鉆研,在公交、在地鐵、在廁所都可以閱讀,隨時隨地漲姿勢。
文章不涉及代碼,不燒腦細胞,人人都可以學習。
當你決定關注beylze(公眾號:beylze),你已然超越了90%的其他從業者!