当我运行`sh -c"命令"`,一个新的shell或者没有这些时,是否有一个子shell?

Rad*_*anu 12 bash command-line

在bash中,当我运行以下命令时:

sh -c "command"
Run Code Online (Sandbox Code Playgroud)

是否创建了一个子shell然后command执行?

我的猜测是命令将在当前的shell中运行,但我不确定.这个猜测来自我已经使用以下命令测试的事实:

echo $BASHPID, $BASH_SUBSHELL
Run Code Online (Sandbox Code Playgroud)

sh -c "echo $BASHPID, $BASH_SUBSHELL"
Run Code Online (Sandbox Code Playgroud)

结果是一样的.但是,有人告诉我这可能有点误导,因为在执行命令之前可能会替换变量.这是真的吗?

Hen*_*eld 8

我会说不.

子shell通常保留给shell进程需要在隔离环境中执行操作的任何实例,但是可以访问shell的当前状态(的副本),包括所有全局和局部变量.

例子:

  • 管道: echo x | read y
  • 命令替换: line=$( read < file && echo "$REPLY" )

在大多数情况下,这会导致shell进程自行分叉.

在ksh93的最新更新中,一些子shell实际上可能不会分叉新进程.

问题的关键在于子shell始终是隐式创建的.

Running sh -c ...将创建一个新的 shell进程,它将丢弃大部分状态并从头开始.这意味着普通(本地,全局)shell变量消失了.当然,新shell将包含所有导出变量的副本.


对问题的不同解释可能是-c选项是否为运行分配了一个新进程... 答:没有.它没有.但是,传递给-c参数的某些命令可能需要shell生成子shell,就像它们是脚本的一部分或交互式键入一样.


Has*_*tur 8

我认为通过示例可以了解情况
(在我的情况下sh是一个符号链接/bin/dash).
我做了这个测试sh:

echo $$ ; sh -c 'echo $$ ; sh -c '"'"'echo $$'"'"' '
16102
7569
7570
Run Code Online (Sandbox Code Playgroud)

三种不同PID,三种不同shell.(如果有不同的shell,则没有子shell生成).


以类似的方式 BASH

echo $$ $BASHPID, $BASH_SUBSHELL ; bash -c 'echo $$  $BASHPID $BASH_SUBSHELL  ; bash -c '"'"'echo  $$ $BASHPID $BASH_SUBSHELL '"'"' '
16102 16102, 0
10337 10337 0
10338 10338 0
Run Code Online (Sandbox Code Playgroud)

三种不同$BASHPID没有区别$BASH_SUBSHELL(请参阅下面的注释,了解$$和之间的差异$BASHPID).
如果我们是在一个子shell不需要重新初始化,然后$$$BASHPID应有所不同.
以同样的方式$BASH_SUBSHELL不增加,它总是如此0.因此有2条线索再次说明没有新的子壳生成,我们只有新的炮弹.


man bash(4.2.45(1)-release)我汇报一下一些相关部件时,子shell是催生:

  1. 管道中的每个命令都作为单独的进程执行(即,在子shell中).

  2. 如果命令由控制操作符&终止,则shell 在子shell中在后台执行命令. shell不等待命令完成,返回状态为0.

    由a分隔的命令; 按顺序执行; shell等待每个命令依次终止.返回状态是最后执行的命令的退出状态....

  3. ( list ) 列表在子shell环境中执行
    {list; } list只是在当前的shell环境中执行.

  4. 协进程是由前面外壳命令coproc保留字.协处理在子shell中异步执行...

  5. $扩展为shell的进程ID .在()子shell中,它扩展为当前shell的进程ID ,而不是子shell.

备注:

  • BASHPID扩展为当前bash进程的进程ID.这与$$ 某些情况不同,例如不需要重新初始化bash的子shell.
  • BASH_SUBSHELL每次生成子shell或子shell环境时增加1.初始值为0.

  • 对于使用单引号''和双引号之间的差异,"" 您可以看到这个问题.让我们只记得,如果你在双引号中编写命令,""那么变量将通过原始shell的参数扩展进行评估,如果启用了extquote,则默认情况下是shopt.(参见4.3.2 Bash参考手册中内置的商店)

    *extquote* 如果设置,则$'string'和$"string"引用在用双引号括起来的$ {parameter}扩展中执行.默认情况下启用此选项.

如需进一步参考,您可能会发现有用的例


kon*_*box 6

我也检查过它,不,我不认为(-c)召唤一个子壳.如果我们引用sh它自己,则yes sh是一个被召唤的shell,但如果它是关于它sh自身内的子shell则不是.我们可以通过运行如下命令来验证这一点:

# bash -c 'pstree -p; echo Value of PPID in Callee: $PPID; echo Callee PID: $BASHPID / $$'; echo "Caller PID: $$"
bash(28860)???bash(17091)???pstree(17092)
Value of PPID in Callee: 28860
Callee PID: 17091 / 17091
Caller PID: 28860
Run Code Online (Sandbox Code Playgroud)

正如您所看到的那样,被调用的shell(17091)和调用者shell(28860)都直接作为子父级连接.他们之间什么都没有.$BASHPID并且$$甚至是相同的,在这种情况下如果你在子壳上应该是不同的.这只是告诉我在调用命令时没有调用子shell -c.

这只有一个特殊的行为,那就是召唤一个外部二进制文件,例如:

# bash -c 'pstree -p'; echo "Caller PID: $$"
bash(28860)???pstree(17124)
Caller PID: 28860
Run Code Online (Sandbox Code Playgroud)

bash将自己从forking中解脱出来并决定直接执行exec()唯一的命令.您可能会认为,如果命令引用外部可执行二进制文件,或许bash总是对最后一个命令执行此操作,但是不会:

# bash -c 'echo Value of PPID in Callee: $PPID; echo Callee PID: $BASHPID / $$; pstree -p'; echo "Caller PID: $$"
Value of PPID in Callee: 28860
Callee PID: 17128 / 17128
bash(28860)???bash(17128)???pstree(17129)
Caller PID: 28860
Run Code Online (Sandbox Code Playgroud)

现在约

echo $ BASHPID,$ BASH_SUBSHELL

sh -c"echo $ BASHPID,$ BASH_SUBSHELL"

结果是一样的.

它应该是相同的,如果echo $BASHPID, $BASH_SUBSHELL在同一个shell中执行,因为"echo $BASHPID, $BASH_SUBSHELL"首先由shell解释命令,然后将它作为参数传递给它sh.如果BASHPID让我们说28860BASH_SUBSHELL0,那么扩展的值"echo $BASHPID, $BASH_SUBSHELL"就是'echo 28860, 0'命令实际上是这种情况sh -c 'echo 28860, 0'.实际上,这种方法的正确方法是使用单引号来仅允许在新的被调用shell的上下文中进行解释:sh -c 'echo $BASHPID, $BASH_SUBSHELL'虽然我不确定命令本身是否有助于测试.

所以基本上我所说的是,如果召唤一个子壳,那么测试echo $BASHPID, $BASH_SUBSHELL+ sh -c "echo $BASHPID, $BASH_SUBSHELL"没有证明任何东西-c,并且那个告诉你它可能误导的人因为变量可能被替换是正确的.

尽管如此,我自己的测试表明,Bash确实没有用它来召唤一个子shell(-c).

  • 这是正确的答案。有关SHLVL和BASH_SUBSHELL之间的区别的讨论,请参见此处(http://www.tldp.org/LDP/abs/html/subshel​​ls.html)。我差点说你错了,因为SHLVL确实增加了。但是一直以来,我都误解了该变量的含义。 (2认同)