在 Bash 中,如何检测我是否处于子 shell 中?

在 Bash 中,如何检测我是否处于子 shell 中?

我正在尝试编写一个函数来替换内置函数的功能,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

相关内容