在第二周課程中,老師說到使用小括號(list)可以調(diào)出子shell漱逸,并舉了以下例子:
這個例子很好理解:在當(dāng)前shell,變量name被賦值為wang;而在通過()進(jìn)入的子shell里专缠,變量name被賦值為zhang狰挡。所以第一個echo顯示當(dāng)前shell內(nèi)變量name的值為wang捂龄;第二個echo顯示子shell內(nèi)變量name的值為zhang;第三個echo是子shell結(jié)束后加叁,回到當(dāng)前shell倦沧,變量name的值為wang.
緊接著,老師又寫了一行命令它匕,讓大家判斷它的輸出值會是什么:
根據(jù)之前的經(jīng)驗展融,name作為當(dāng)前shell的一個普通變量,并沒有被export豫柬,子shell是不會繼承的告希,輸出值自然是“wang 空值 wang”咯。然后結(jié)果卻出乎意料:
怎么回事烧给,在沒有export的情況下燕偶,子shell怎么繼承了父shell的環(huán)境變量?
課后特意去詢問老師础嫡,老師大致解釋說指么,用()打開的子shell與執(zhí)行bash打開的子shell還是不同的,它沒有單獨加載一些shell的配置榴鼎,會繼承父shell的內(nèi)容等等伯诬。本著先記住,以后慢慢理解的原則巫财,我也就似懂非懂地決定先硬記住了盗似。
但是兩種子shell到底有什么區(qū)別呢?為什么一個能繼承父shell的環(huán)境平项,另一個卻不能呢赫舒?
今天無意中看到一篇帖子,里面解釋了SHLVL和BASH_SUBSHELL兩個變量闽瓢,讓這個疑惑迎刃而解号阿。
首先,借用一下大神對這兩個變量的定義:
SHLVL是記錄多個bash進(jìn)程實例嵌套深度的累加器
BASH_SUBSHELL是記錄一個bash進(jìn)程實例的多層subshell嵌套深度的累加器
如果概念比較不好理解鸳粉,讓我們通過例子來理解吧
圖1中,SHLVL值隨著bash命令執(zhí)行的次數(shù)增加园担,而BASH_SUBSHELL值不變届谈。本質(zhì)上說枯夜,這是通過執(zhí)行外部命令bash開啟了新進(jìn)程(而這個新進(jìn)程恰好也是shell進(jìn)程罷了),而SHLVL記錄了這種嵌套的shell進(jìn)程實例的層數(shù)艰山。這樣的child shell進(jìn)程是通過執(zhí)行硬盤上的命令來生成的湖雹,它是獨立的(會讀取加載/etc/profile.d/*.sh /etc/bashrc ~/.bashrc等bash配置文件),只能訪問所謂的"父"shell的環(huán)境變量曙搬。
而通過()開啟的叫做subshell的才是當(dāng)前shell名副其實的子shell摔吏,它可以訪問父shell的任何變量。圖2可以看到通過()開啟的子shell的嵌套層級數(shù)纵装。注意征讲,SHLVL的值是不變的,也就是沒有開啟新的child shell橡娄。
我們來看看進(jìn)程樹:
-
bash
SHLVL
-
() 因為執(zhí)行完小括號內(nèi)的命令后诗箍,subshell進(jìn)程會立馬結(jié)束,所以需要通過sleep命令來留住subshell進(jìn)程挽唉,進(jìn)而通過pstree -p命令觀察它
()
() pstree
從進(jìn)程樹上看滤祖,child shell和subshell他們都在當(dāng)前shell進(jìn)程(pid15036)后.不同點在于,child shell的$BASHPID和$$值相同瓶籽,subshell則沒有自己獨立$$值匠童,只有自己的$BASHPID.
翻譯成中文的時候籠統(tǒng)地管它們叫子shell。然而它們是有本質(zhì)上的區(qū)別的∷芩常現(xiàn)在你了解了嗎汤求?
補(bǔ)充:
An example of the difference between a subshell and a child process that happens to be a shell:
補(bǔ)充2:
通過管道,也能打開subshell