首頁 > shell教程 閱讀:0更新時間:2020-03-28 02:19:32

如何檢測子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 還提供了另外兩個環境變量&mdash;&mdash;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.shchmod +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。

注意,&ldquo;進程替換&rdquo;看起來好像產生了一個子 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%的其他從業者!

相關文章

優秀教程

国产亚洲欧美日韩