我正在尝试编写一个函数来替换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中的好方法是什么?
在 bash 中,您可以
$BASHPID
比较$$
如果您不在 bash 中,
$$
则在子 shell 中应该保持不变,因此您需要一些其他方法来获取您的实际进程 ID。获取实际 pid 的一种方法是
sh -c 'echo $PPID'
. 如果你只是把它放在一个简单的( … )
地方,它可能看起来不起作用,因为你的 shell 已经优化了 fork。尝试额外的 no-op 命令( : ; sh -c 'echo $PPID'; : )
,让它认为 subshell 太复杂而无法优化。这种方法归功于 Stack Overflow 上的John1024 。怎么样
BASH_SUBSHELL
?[这应该是评论,但我的评论往往会被版主删除,所以这将保留作为答案,即使被删除,我也可以将其用作参考]
使用
BASH_SUBSHELL
是完全不可靠的,因为它仅在某些子外壳中设置为 1,而不是在所有子外壳中。在声称运行管道命令的子进程不是真正的子shell之前,请考虑以下
man bash
代码段:以及实际意义——脚本片段是否在子进程中运行是必不可少的,而不是一些术语上的狡辩。
正如在这个问题的答案中已经解释的那样,唯一的解决方案是检查是否
$BASHPID
等于$$
或可移植但效率低得多: