BASH_SUBSHELL 變數不生效的情況

紫雲飛發表於2015-09-21

BASH_SUBSHELL 實現於 Bash 3.0,我一直想不到它在實際編碼中有什麼用,後來在 Bash 的 Change Log 裡找到一句話,才知道它是作除錯用的:

New variables to support the bash debugger: BASH_ARGC, BASH_ARGV,
BASH_SOURCE, BASH_LINENO, BASH_SUBSHELL, BASH_EXECUTION_STRING,
BASH_COMMAND

BASH_SUBSHELL 是 subshell 巢狀層次的累加器,那麼按理說,所有的 subshell 裡這個變數都應該加 1。那 Bash 的哪些語法是在 subshell 裡執行的呢?下面列舉一下:

  1. 小括號分組 (...)
  2. 命令替換 `...` 和 $(...)
  3. 程式替換 <() 和 >()
  4. 管道 ... | ...
  5. 後臺命令 ... &

那麼當 BASH_SUBSHELL 出現在這些語法中時,它的值就應該加 1,對不對?然而實際情況卻不是這樣的,在我寫這篇文章時,Bash 的最新穩定版是 4.3.30,在該版本中,BASH_SUBSHELL 只會在上面列出的 1 和 2 兩種語法裡生效:

$ (echo $BASH_SUBSHELL)

1

$ echo `echo $BASH_SUBSHELL`

1

$ echo $(echo $BASH_SUBSHELL)

1

在後三種語法裡不生效: 

$ cat <(echo $BASH_SUBSHELL)

0

$ echo $BASH_SUBSHELL | cat

0

$ echo $BASH_SUBSHELL &

[1] 91155

$ 0

搜尋了一翻,在 help-bash 上發現已經有人提了個 bug,不過他僅僅提到了程式替換和命令替換中 BASH_SUBSHELL 表現不一致的事,Bash 作者也回覆說會在 Bash 下個版本也就是是 4.4 裡修復。然後我下了 4.4 alpha 版編譯之後發現,程式替換中 BASH_SUBSHELL 是生效了,但在管道和後臺命令中仍沒效果,於是我又頂起了這個郵件,詢問 Bash 作者是否能一起修復,他的回覆是,後臺命令那個他會修的,但管道命令那個不準備修復。然後我就沒再追問為什麼了,應該是實現上有困難,畢竟這是 Bash 私有的東西,作者有權決定該不該修。總之,我想說的是,在未來 Bash 4.4 釋出的時候,BASH_SUBSHELL 在除了管道之外的其他子 Shell 裡,都應該能生效了。

此外,當我問這個問題的時候,有人回覆說,之所以 BASH_SUBSHELL 為 0,是因為它是在父 Shell 裡展開之後才傳入子 Shell 的,也就是說,echo $BASH_SUBSHELL | cat 在傳入子 Shell 的時候就已經成了 echo 0 | cat,但這是不對的,我們可以舉一個反例,如果真是那樣的話,echo $BASHPID | cat 應該和 echo $BASHPID 的輸出一樣,但實際卻是不一樣的。所以可以總結一下就是,從來都不存在“在父 Shell 中展開變數,在子 Shell 裡執行展開後的命令” 這一回事,所有的變數都是在子 Shell 中展開然後執行的。

相關文章