子shell命令执行

子shell命令执行

我正在阅读 bash 手册,并看到一个部分指出:

“命令替换、用括号分组的命令以及异步命令在与 shell 环境重复的子 shell 环境中调用”

我的问题是,有没有办法可以真正看到子 shell 中执行的内容。例如,假设我写了下面的行。我需要做什么才能实际查看子 shell 中执行的内容?

$ printf "first\nsecond\nthird" | grep -E "(first|second)"

我如何检查子 shell 中实际执行的内容(我怀疑它对“第一个”执行 grep,然后对第二个 shell 对“第二个”执行 grep,但我只想看看它在做什么)。

答案1

我认为你不能直接看到它。

xtrace( set -x) 确实显示evals 和命令替换的嵌套级别,但不显示括号中的组。

Bash 在子进程中运行子 shell,所以原则上你可以尝试运行strace或类似地查看fork()clone()在Linux上)和execve()系统调用shell及其子进程,但输出有点难以解释。此外,像这样的内置函数printf不会出现在execve()调用中,并且当 Bash 进程只有一个要运行的命令时,它会跳过一级分叉。例如,bash -c '/bin/false'不会对外部命令进行分叉,同样,(/bin/false)只有一个对子 shell 的分叉,而不是对子 shell 和外部命令的两个分叉。

您还可以$BASHPID在不同的地方打印它,并将其与$$.$BASHPID包含当时实际运行的 Bash 进程的 PID,同时$$包含主 Bash 进程的 PID,并且不会更新子 shell。虽然它没有显示运行的命令,但散布这些打印输出有点混乱:

$ echo $$ >&2; { echo "lhs: $BASHPID" >&2; printf foobar; } | { echo "rhs: $BASHPID" >&2; grep oo; }
30163
rhs: 30166
lhs: 30165
foobar

是的,我们可以从输出中看出管道的两个部分都在子外壳中运行,即使您引用的部分中没有提到这一点。

不管怎样,在子shell中运行的主要是那些,你引用提到的三个,加上管道。

异步/后台进程很明显:它们与 shell 同时运行,因此它们不能位于同一进程中。管道的各个部分也必须同时运行,因此最多可以在主 shell 进程中运行。 (在 Bash 中,如果选项已设置且有效,最右边的部分将执行此操作lastpipe,否则它们都会获得自己的子 shell/进程。)

带括号的组明确要求使用子 shell,而且,我想不出命令替换的借口,所以我想我们只能接受它们也这样做。

子 shell 中的更改不会传播到主 shell,因此测试它也相对简单。例如,如果你运行:

bash -c 's=
         s+=direct.
         (s+=parens.)
         $(s+=comsub.)
         s+=pipeleft. | s+=piperight.
         s+=async. &
         eval s+=eval.
         echo $s'

你得到了输出direct.eval.,所以这些是没有在子 shell 中运行。其余的都做了。如果您-O lastpipe在 之前添加-c,或shopt -s lastpipe;在脚本内添加,您还会看到piperight已添加到列表中。

答案2

首先,我不确定grepexec 是否在两个子 shell 中。

但要检查执行命令时发生的情况,可以使用 command truss。就像是:

printf "first\nsecond\nthird" | truss -o out.log grep -E "(first|second)"

将存储在out.log系统调用、信号等命令中。

这里是手册页truss以供将来参考。

关于strace我认为你可以在命令中替换它:

printf "first\nsecond\nthird" | strace -o out.log grep -E "(first|second)"

但请注意,与truss

如果您想跟踪整个 shell、子 shell 创建、系统调用等,您应该使用以下格式的命令:

strace -o out.log 'printf "first\nsecond\nthird" | grep -E "(first|second)"'

相关内容