【问题标题】:Subprocess return code different than "echo $?"子进程返回码不同于“echo $?”
【发布时间】:2016-07-19 01:02:51
【问题描述】:

我正在使用 subprocess 在 Python 中调用 bash 命令,我得到的返回码与 shell 显示的不同。

import subprocess
def check_code(cmd):
    print "received command '%s'" % (cmd)
    p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    p.wait()
    print "p.returncode is '%d'" % (p.returncode)
    exit()
    if p.returncode == 0:
        return True
    else:
        return False
    #End if there was a return code at all
#End get_code()

发送“ls /dev/dsk &> /dev/null”时,check_code 返回 0,但“echo $?”在终端中产生“2”:

Welcome to Dana version 0.7
Now there is Dana AND ZOL

received command 'ls /dev/dsk &> /dev/null'
p.returncode is '0'
root@Ubuntu-14:~# ls /dev/dsk &> /dev/null
root@Ubuntu-14:~# echo $?
2
root@Ubuntu-14:~#

有人知道这里发生了什么吗?

【问题讨论】:

  • 我认为您看到的是 shell 的返回码,而不是 ls 的返回码。
  • p.wait()的返回值是不是也有误?
  • 请记住,不建议使用shell=True。尤其是对于不受信任的用户输入。

标签: python bash subprocess


【解决方案1】:

根据subprocess.Popen,您的Python 脚本中使用的shell 是sh。这个 shell 是 POSIX 标准,与 Bash 不同,后者有几个非标准特性,例如速记重定向 &> /dev/nullsh,Bourne shell,将此符号解释为“在后台运行我,并将 stdout 重定向到 /dev/null”。

由于您的subprocess.Popen 打开了一个sh,它在自己的后台运行ls,因此使用sh 的返回值代替ls,在本例中为0。

如果您希望 Python 具有 Bash 行为,我相信您可能必须重新配置(可能重新编译)Python 本身。使用sh 语法更简单,即ls /dev/dsk 2> /dev/null

【讨论】:

    【解决方案2】:

    按照 xi_ 的建议,我将命令拆分为空格分隔的字段,但无法使用“&>”和“/dev/null”运行。我删除了它们,它起作用了。

    然后我将命令全部重新组合在一起以在没有“&> /dev/null”的情况下对其进行测试,这也有效。似乎添加“&> /dev/null”会以某种方式抛出子进程。

    Welcome to Dana version 0.7
    Now there is Dana AND ZOL
    
    received command 'cat /etc/fstab'
    p.wait() is 0
    p.returncode is '0'
    
    received command 'cat /etc/fstabb'
    p.wait() is 1
    p.returncode is '1'
    
    received command 'cat /etc/fstab &> /dev/null'
    p.wait() is 0
    p.returncode is '0'
    
    received command 'cat /etc/fstabb &> /dev/null'
    p.wait() is 0
    p.returncode is '0'
    root@Ubuntu-14:~# cat /etc/fstab &> /dev/null
    root@Ubuntu-14:~# echo $?
    0
    root@Ubuntu-14:~# cat /etc/fstabb &> /dev/null
    root@Ubuntu-14:~# echo $?
    1
    root@Ubuntu-14:~#
    

    我最初在调用中添加了“&> /dev/null”,因为我在屏幕上看到了 STDERR 的输出。一旦我将 stderr=PIPE 添加到子进程调用中,它就消失了。我只是想默默地检查幕后输出的代码。

    如果有人能解释为什么在 Python 中将“&> /dev/null”添加到子进程调用会导致其行为异常,我很乐意选择它作为答案!

    【讨论】:

    • 我们使用 bash 作为 shell,但 python 使用 sh,它对 &> 的解释与 bash 不同。
    【解决方案3】:

    您将其用作subprocess.Popen(cmd, shell=True),将cmd 用作字符串。

    这意味着子进程将在后台调用 /bin/sh 并带有参数。因此,您将获得 shell 的退出代码。

    如果您确实需要命令的退出代码,请将其拆分为列表并使用shell=False

    subprocess.Popen(['cmd', 'arg1'], shell=False)

    【讨论】:

    • 如果你需要&>/dev/null,你就无法避免使用shell。此外,shell 的退出代码应该是最后运行的命令的退出代码。
    猜你喜欢
    • 1970-01-01
    • 2019-10-12
    • 2012-06-11
    • 1970-01-01
    • 2019-05-30
    • 2017-11-29
    • 2015-10-28
    • 2020-10-04
    • 1970-01-01
    相关资源
    最近更新 更多