Python subprocess.check_output 输出与同一命令的 bash 输出不同

Python subprocess.check_output output differ from bash output of same command

我正在尝试使用子进程从 python 获取当前 bash 版本。

在我的终端中,我可以使用 printf "%s\n" $SHELL 得到 /bin/bashprintf "%s\n" $BASH_VERSION 得到 5.1.8(1)-release.

所以我准备了这个 python 脚本:

>>> import subprocess
>>> subprocess.check_output(['printf "%s\n" $SHELL'], shell=True, stderr=subprocess.STDOUT)
b'/bin/bash\n'
>>> subprocess.check_output(['printf "%s\n" $BASH_VERSION'], shell=True, stderr=subprocess.STDOUT)
b'\n' <-- This output is wrong

谁能解释为什么子进程中 printf $BASH_VERSION 的输出与终端输出不同?

BASH_VERSION 是一个 internal variable to bash shell, it simple doesn't exists outside of it. As per https://docs.python.org/3/library/subprocess.html#popen-constructor

On POSIX with shell=True, the shell defaults to /bin/sh

因此,如果您系统上的 /bin/sh 不是 bash 的别名 - 您将一无所获。要明确指定 shell,您可以调用它,或者使用 executable 作为 kwarg。

我在我的系统上 运行 进行的一些测试,其中 /bin/sh 链接到 /usr/bin/bash

subprocess.check_output(['echo $BASH_VERSION'], shell=True, stderr=subprocess.STDOUT)                                                                                              
b'4.4.23(1)-release\n'

subprocess.check_output(['echo $BASH_VERSION'], shell=True, stderr=subprocess.STDOUT, executable='/bin/zsh')                                                                       
b'\n'

subprocess.check_output(['echo $BASH_VERSION'], shell=True, stderr=subprocess.STDOUT, executable='/bin/bash')                                                                      
b'4.4.23(1)-release\n'

请注意,在第二个示例中,我调用了没有此变量的 zsh

$SHELL 变量实际上是由 subshells 从您的实际 shell 继承的,但这并不意味着该命令实际上是 运行 使用 SHELL.

在我的例子中,我的用户默认 zsh shell,但是 运行

bash -c "echo $SHELL"                                                                                                                        
/usr/bin/zsh

还是会returnzsh,如你所见

$SHELL 具有误导性,并没有告诉您您的想法。第二个命令(显示$BASH_VERSION)是正确的; subprocess.check_output() 是 运行 在 /bin/sh 下的命令,它是 dash instead of bash in recent versions of Ubuntu(以及许多其他 linux 发行版),所以 BASH_VERSION 没有设置shell.

与流行的看法相反,SHELL 变量并不表示当前正在执行的 shell。 bash 通常会将其设置为 当前用户的默认值 shell。来自 [bash 手册中关于“Bash 变量”的部分]:

SHELL
This environment variable expands to the full pathname to the shell. If it is not set when the shell starts, Bash assigns to it the full pathname of the current user’s login shell.

因此,如果您的默认 shell(如 /etc/passwd 等某处所定义)是 /bin/zsh,并且您手动 运行 a bash shell 在此之下,它会将 SHELL 设置为“/bin/zsh”,即使您当时实际上是 运行宁 bash。

除 bash 之外的 Shell 可能会或可能不会将 SHELL 设置为任何值(请注意它列在“Bash 变量”中,而不是 "Bourne Shell Variables"其他 Bourne-family shell 使用的变量部分)。 dash 和 zsh 根本不设置 PATH,而 ksh 似乎用它做了其他事情。

因此,您可能会问,如果 subprocess.check_output() 运行 位于短划线下,为什么它会打印 $SHELL 的任何内容?我怀疑这是因为它继承了您的交互式 bash shell。如果你在其中启动了 zsh shell 和 运行 echo $SHELL,你可能也会在那里看到“/bin/bash”。