【问题标题】:Python subprocess.check_output output differ from bash output of same commandPython subprocess.check_output 输出与同一命令的 bash 输出不同
【发布时间】:2022-02-02 17:18:00
【问题描述】:

我正在尝试使用子进程从 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 的输出与终端不同?

【问题讨论】:

    标签: python bash shell ubuntu subprocess


    【解决方案1】:

    BASH_VERSION 是一个 internal variable 到 bash shell,它简单不存在于它之外。根据https://docs.python.org/3/library/subprocess.html#popen-constructor

    在 shell=True 的 POSIX 上,shell 默认为 /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 变量实际上是由子shell 从您的实际 shell 继承的,但这并不意味着该命令实际上是使用该 SHELL 运行的。

    就我而言,我的用户将zsh 作为默认shell,但正在运行

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

    仍会返回 zsh,如您所见

    【讨论】:

      【解决方案2】:

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

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

      SHELL
      此环境变量扩展为完整路径名 壳。如果在 shell 启动时没有设置它,Bash 会分配给它 当前用户登录 shell 的完整路径名。

      因此,如果您的默认 shell(在 /etc/passwd 之类的某个地方定义)是 /bin/zsh,并且您在其下手动运行 bash shell,它甚至会将 SHELL 设置为“/bin/zsh”尽管您当时实际上正在运行 bash。

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

      所以,您可能会问,如果subprocess.check_output() 在破折号下运行,为什么它会为$SHELL 打印任何内容?我怀疑这是因为它从您的交互式 bash shell 继承了它。如果你启动了一个 zsh shell 并在其中运行echo $SHELL,你大概也会在那里看到“/bin/bash”。

      【讨论】:

        猜你喜欢
        • 2017-05-01
        • 2016-10-10
        • 2013-12-05
        • 1970-01-01
        • 2017-05-18
        • 2022-07-16
        • 2015-05-02
        • 2015-02-16
        相关资源
        最近更新 更多