Subshell
Subshell
Subshell 的定义在一些书本或资料中模糊不清,给出的解释往往和某些例子自相矛盾,让人捉摸不透。 因此,为了避免这种语义与逻辑上的问题,这里不会给出它的定义(建议参考官方 BASH 手册), 也不会使用 “子 Shell” 这个翻译,仅仅从它的实际表现去理解它的定义。 下面列举的变量和 Subshell 概念有着密切的联系。
Bash Reference Manual
Bash 参考手册
BASH_SUBSHELL
Shell 变量,非环境变量,官方手册解释。
Incremented by one within each subshell or subshell environment when the shell begins executing in that environment. The initial value is 0. If BASH_SUBSHELL is unset, it loses its special properties, even if it is subsequently reset.
也可以使用 man 命令,内容可能会有一点差别。
man bash | grep -A 3 'BASH_SUBSHELL' | head -n 4
echo $BASH_SUBSHELL
0
SHLVL
环境变量,官方手册解释。
Incremented by one each time a new instance of Bash is started. This is intended to be a count of how deeply your Bash shells are nested.
使用 man 命令。
man bash | grep 'SHLVL'
这个值是从 1 开始的。
echo $SHLVL
1
命令分组
全称 Command Grouping,Bash 提供两种方法创建命令分组。
括号:()
该方法会创建一个 Subshell 环境去处理命令分组。
(pwd; echo $BASH_SUBSHELL)
/home/kuga
1
(pwd; (echo $BASH_SUBSHELL))
/home/kuga
2
(pwd; (echo $SHLVL))
1
可以得出以下结论。
- BASH_SUBSHELL:每创建一个 Subshell 就加 1。
- SHLVL:无论创建多少个 Subshell,都不变。
花括号:{}
这种方法不会创建 Subshell,命令分组是在当前 Shell 的上下文中处理的。 在语法上,花括号与命令之间的空格不能省略,每个命令结尾的分号也是必须的。
{ pwd; { echo $BASH_SUBSHELL; } }
/home/kuga
0
{ pwd; { echo $SHLVL; } }
/home/kuga
1
Shell PID
可通过 BASHPID 或 $$
查看 Shell 的 PID,但它们是有区别的。
BASHPID
Shell 变量,非环境变量,官方解释。
Expands to the process ID of the current Bash process. This differs from $$ under certain circumstances, such as subshells that do not require Bash to be re-initialized. Assignments to BASHPID have no effect. If BASHPID is unset, it loses its special properties, even if it is subsequently reset.
echo $BASHPID
56414
使用 ()
查看 BASHPID。
(ps -f --forest; echo $BASHPID)
UID PID PPID C STIME TTY TIME CMD
kuga 56414 56412 0 10:11 pts/0 00:00:00 -bash
kuga 57325 56414 0 15:13 pts/0 00:00:00 \_ -bash
kuga 57326 57325 0 15:13 pts/0 00:00:00 \_ ps -f --forest
57325
可以看到,BASHPID 输出了 Subshell 的 PID。
特殊参数 $$
官方解释。
($$) Expands to the process ID of the shell. In a subshell, it expands to the process ID of the invoking shell, not the subshell.
在 Subshell 中,$$
表示的是 invoking shell 的 PID。
(pwd; (ps -f --forest; echo $$))
/home/kuga
UID PID PPID C STIME TTY TIME CMD
kuga 56414 56412 0 10:11 pts/0 00:00:00 -bash
kuga 57347 56414 0 15:20 pts/0 00:00:00 \_ -bash
kuga 57348 57347 0 15:20 pts/0 00:00:00 \_ -bash
kuga 57349 57348 0 15:20 pts/0 00:00:00 \_ ps -f --forest
56414
可以看到,无论有多少个 Subshells,$$
始终表示顶层 Bash 的 PID。
创建 Bash 实例
在 Bash 中输入 bash
就可以创建一个全新的 Bash 实例。
bash
ps -f --forest
UID PID PPID C STIME TTY TIME CMD
kuga 56414 56412 0 10:11 pts/0 00:00:00 -bash
kuga 57359 56414 0 15:29 pts/0 00:00:00 \_ bash
kuga 57402 57359 0 15:30 pts/0 00:00:00 \_ ps -f --forest
这时候再观察一下上面提到的变量。
echo $BASH_SUBSHELL $SHLVL $BASHPID $$
0 2 57359 57359
- BASH_SUBSHELL:没有变化。
- SHLVL:从 1 -> 2。
- BASHPID:新 Bash 实例的 PID。
- $$:新 Bash 实例的 PID。
如果说这种创建 Bash 的方式也是 Subshell 的话,语义和表现上就会自相矛盾。