我正在尝试编写一个函数来替换内置函数的功能,exit
以防止自己退出终端。
我尝试使用SHLVL
环境变量,但它似乎在子 shell 中没有改变:
$ echo $SHLVL
1
$ ( echo $SHLVL )
1
$ bash -c 'echo $SHLVL'
2
我的功能如下:
exit () {
if [[ $SHLVL -eq 1 ]]; then
printf '%s\n' "Nice try!" >&2
else
command exit
fi
}
但这不允许我exit
在子 shell 中使用:
$ exit
Nice try!
$ (exit)
Nice try!
有什么好的方法可以检测我是否处于子 shell 中?
答案1
在 bash 中,您可以$BASHPID
比较$$
$ ( if [ "$$" -eq "$BASHPID" ]; then echo not subshell; else echo subshell; fi )
subshell
$ if [ "$$" -eq "$BASHPID" ]; then echo not subshell; else echo subshell; fi
not subshell
如果您不在 bash 中,$$
则应在子 shell 中保持不变,因此您需要其他方式来获取实际的进程 ID。
获取实际 pid 的一种方法是sh -c 'echo $PPID'
。如果你只是把它放在一个普通的地方,( … )
它可能看起来不起作用,因为你的 shell 已经优化了分叉。尝试额外的无操作命令,( : ; sh -c 'echo $PPID'; : )
让它认为子 shell 太复杂而无法优化。信用去往Stack Overflow 上的 John1024对于这种方法。
答案2
怎么样BASH_SUBSHELL
?
BASH_SUBSHELL
当 shell
开始在每个子 shell 或子 shell 环境中执行时,在该环境中加一。初始值为0。
$ echo $BASH_SUBSHELL
0
$ (echo $BASH_SUBSHELL)
1
答案3
[这应该是一条评论,但我的评论往往会被版主删除,所以这将保留作为答案,即使删除我也可以将其用作参考]
使用BASH_SUBSHELL
是完全不可靠的,因为它只能设置为 1 in一些子 shell,但并非在所有子 shell 中。
$ (echo $BASH_SUBSHELL)
1
$ echo $BASH_SUBSHELL | cat
0
在声明运行管道命令的子进程不是真的真正的子shell,考虑这个man bash
片段:
管道中的每个命令都作为单独的进程(即在子shell 中)执行。
以及实际意义——重要的是脚本片段是否在子进程中运行,而不是一些术语上的争论。
唯一的解决方案,正如这个问题的答案中已经解释的那样问题是检查是否$BASHPID
等于$$
或,可移植但效率低得多:
if [ "$(exec sh -c 'echo "$PPID"')" != "$$" ]; then
echo you\'re in a subshell
fi