【问题标题】:Retrieving the output of subprocess.call() [duplicate]检索 subprocess.call() 的输出 [重复]
【发布时间】:2011-01-01 01:50:50
【问题描述】:

如何获取使用subprocess.call() 运行的进程的输出?

StringIO.StringIO 对象传递给stdout 会出现此错误:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/subprocess.py", line 444, in call
    return Popen(*popenargs, **kwargs).wait()
  File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/subprocess.py", line 588, in __init__
    errread, errwrite) = self._get_handles(stdin, stdout, stderr)
  File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/subprocess.py", line 945, in _get_handles
    c2pwrite = stdout.fileno()
AttributeError: StringIO instance has no attribute 'fileno'
>>> 

【问题讨论】:

  • 迈克的回答是正确的。请注意,StringIO在大多数情况下 像文件一样工作,但不是全部。它不适用于您的情况,因为 multiprocessing 模块在某些情况下假定实际文件。这可能已修复:请参阅 bugs.python.org/issue5313 了解相关错误。
  • 其实communicate()使用select.select(),它只接受文件描述符,所以它不是一个真正的错误。当我第一次遇到它时,我对此感到很困惑,探索 subprocess.py 的深度教会了我很多东西!。
  • 我认为subprocess.run 使这更简单,从 Python 3.5 开始。有机会我会补充答案的。
  • 请注意,接受的答案已经过时了。 Python 2.7 的简单答案是subprocess.check_output();在 Python 3.5+ 中,您还需要查看 subprocess.run()。如果可以避免的话,应该没有必要或不想使用原始subprocess.Popen(),尽管一些更复杂的用例需要它(然后你必须自己做必要的管道)。 Stack Overflow subprocess tag info page 为不那么琐碎的案例提供了一些很好的资源。

标签: python pipe subprocess stringio


【解决方案1】:

关键是使用函数subprocess.check_output

例如,以下函数捕获进程的 stdout 和 stderr 并返回它们以及调用是否成功。它兼容 Python 2 和 3:

from subprocess import check_output, CalledProcessError, STDOUT

def system_call(command):
    """ 
    params:
        command: list of strings, ex. `["ls", "-l"]`
    returns: output, success
    """
    try:
        output = check_output(command, stderr=STDOUT).decode()
        success = True 
    except CalledProcessError as e:
        output = e.output.decode()
        success = False
    return output, success

output, success = system_call(["ls", "-l"])

如果您想将命令作为字符串而不是数组传递,请使用此版本:

from subprocess import check_output, CalledProcessError, STDOUT
import shlex

def system_call(command):
    """ 
    params:
        command: string, ex. `"ls -l"`
    returns: output, success
    """
    command = shlex.split(command)
    try:
        output = check_output(command, stderr=STDOUT).decode()
        success = True 
    except CalledProcessError as e:
        output = e.output.decode()
        success = False
    return output, success

output, success = system_call("ls -l")

【讨论】:

  • Np,很容易搜索。顺便说一句,它还有一个 input= 选项来指定输入以通过管道传递到命令中,而不是使用 Popen 的那种超级复杂的方式。
  • 很棒,因为它甚至可以使用非零退出代码!
  • 绝对好方法,正是我想要的。并澄清 Jason 的评论:使用非零退出代码,您将进入异常处理程序部分,因此您的 success 将是 False
【解决方案2】:

我最近才弄清楚如何做到这一点,下面是我当前项目中的一些示例代码:

#Getting the random picture.
#First find all pictures:
import shlex, subprocess
cmd = 'find ../Pictures/ -regex ".*\(JPG\|NEF\|jpg\)" '
#cmd = raw_input("shell:")
args = shlex.split(cmd)
output,error = subprocess.Popen(args,stdout = subprocess.PIPE, stderr= subprocess.PIPE).communicate()
#Another way to get output
#output = subprocess.Popen(args,stdout = subprocess.PIPE).stdout
ber = raw_input("search complete, display results?")
print output
#... and on to the selection process ...

您现在将命令的输出存储在变量“输出”中。 “stdout = subprocess.PIPE”告诉类从 Popen 中创建一个名为“stdout”的文件对象。据我所知,communicate() 方法只是作为一种方便的方式来返回输出元组和您运行的进程的错误。此外,该进程在实例化 Popen 时运行。

【讨论】:

  • 并没有真正回答如何使用 call 的问题(明智地)。
【解决方案3】:

如果您的 Python 版本 >= 2.7,则可以使用 subprocess.check_output,它基本上完全符合您的要求(它以字符串形式返回标准输出)。

简单示例(linux版本,见注):

import subprocess

print subprocess.check_output(["ping", "-c", "1", "8.8.8.8"])

请注意,ping 命令使用的是 linux 表示法(-c 表示计数)。如果您在 Windows 上尝试此操作,请记住将其更改为 -n 以获得相同的结果。

如下所述,您可以在this other answer 中找到更详细的说明。

【讨论】:

  • Found another answer with working code。如果你用过,请点赞。
  • 但请注意,如果进程返回非零退出码,check_output 会抛出异常。
  • 请注意,check_output 遭受管道填充问题为run,因为它只是调用run。因此,如果您有一个生成更多输出的进程,它将无限期挂起。来自@Mike's@Jabba's 答案的Popen 解决方案效果更好
  • 但另一个答案没有演示如何从 check_call 读取标准输出。
  • 你真的应该把它放在你的答案中。
【解决方案4】:

对于python 3.5+,建议您使用run function from the subprocess module。这将返回一个CompletedProcess 对象,您可以从中轻松获取输出和返回码。

from subprocess import PIPE, run

command = ['echo', 'hello']
result = run(command, stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(result.returncode, result.stdout, result.stderr)

【讨论】:

  • 这是一个绝妙的答案。但是不要像我第一次那样忽略 'stdout=PIPE' 部分,否则输出将非常短暂!
  • 您应该使用text=True 而不是universal_newlines=True,因为前者是为了向后兼容而保留的。
  • “建议使用run 函数” - 当我使用这些参数时,这对我不起作用,这似乎是 Windows 的问题。我通过将shell=True 添加到run 命令来解决它!无论您使用什么操作系统,如果您使用的是 Python 3.7+,请将 stdout=PIPE, stderr=PIPE 替换为 capture_output=True 以添加不会死锁的缓冲区。
  • @Post169 Python 3.7+ 人群的绝佳答案!所以我跑了rc = subprocess.run(ogrList, capture_output=True)然后print("\n", rc.stdout),效果很好!谢谢!
【解决方案5】:

Ipython外壳:

In [8]: import subprocess
In [9]: s=subprocess.check_output(["echo", "Hello World!"])
In [10]: s
Out[10]: 'Hello World!\n'

基于 sargue 的回答。归功于 sargue。

【讨论】:

  • s=subprocess.check_output(["echo", "Hello World!"]); print(s) 打印为 b'Hello World!\n' 我怎样才能摆脱 b''index ? @jhegedus
  • 试试 b'Hello World!\n'.decode("utf-8")
【解决方案6】:

subprocess.call() 的输出只能重定向到文件。

您应该改用subprocess.Popen()。然后,您可以将subprocess.PIPE 传递给stderr、stdout 和/或stdin 参数,并使用communicate() 方法从管道中读取:

from subprocess import Popen, PIPE

p = Popen(['program', 'arg1'], stdin=PIPE, stdout=PIPE, stderr=PIPE)
output, err = p.communicate(b"input data that is passed to subprocess' stdin")
rc = p.returncode

推理是subprocess.call()使用的类文件对象必须有一个真实的文件描述符,从而实现fileno()方法。仅使用任何类似文件的对象都无法解决问题。

请参阅here 了解更多信息。

【讨论】:

  • 此页面docs.python.org/library/subprocess.html#module-subprocess 不鼓励使用subprocess.PIPE,知道如何克服这个问题吗?
  • 另外,这个问题具体说明了使用 subprocess.call ,而 Mike 的答案实际上是使用 Popen,因为 subprocess.call 仅返回返回码,但无法访问任何流。那是如果使用 2.6,如果使用 2.7 @Sergi 答案可以使用
  • @Halst:文档警告PIPE 调用call()(在这种情况下不要使用PIPE)。正如这个答案所暗示的那样,可以将PIPEsubprocess.Popen 一起使用,例如output, _ = Popen(..., stdout=PIPE).communicate()
  • @NathanBasanese:简而言之:不使用PIPE,除非您使用管道。 call()Popen().wait(),因此它不会消耗管道(一旦相应的 OS 管道缓冲区填满,子进程将永远挂起)。如果使用PIPE,则Popen().communicate() 从管道写入/读取数据,从而允许子进程继续。
  • // 啊,好的。这就说得通了。奇怪的是,它甚至允许 PIPE 作为参数。不过,无论如何,我是一个很好的 StackOverFlow 公民,并为它提出了一个问题:stackoverflow.com/questions/32364849/… 你愿意把它作为答案吗?
【解决方案7】:

我有以下解决方案。它捕获执行的外部命令的退出代码、标准输出和标准错误:

import shlex
from subprocess import Popen, PIPE

def get_exitcode_stdout_stderr(cmd):
    """
    Execute the external command and get its exitcode, stdout and stderr.
    """
    args = shlex.split(cmd)

    proc = Popen(args, stdout=PIPE, stderr=PIPE)
    out, err = proc.communicate()
    exitcode = proc.returncode
    #
    return exitcode, out, err

cmd = "..."  # arbitrary external command, e.g. "python mytest.py"
exitcode, out, err = get_exitcode_stdout_stderr(cmd)

我还有一篇关于它的博文here

编辑:解决方案已更新为不需要写入 temp 的较新解决方案。文件。

【讨论】:

  • @Jabba 出于某种原因它无法工作,除非我在 Popen() 的参数中添加 shell=True,你能解释一下原因吗?
  • @JaeGeeTee:你想调用什么命令?我的猜测是你想调用一个包含管道的命令(例如“cat file.txt | wc -l”)。
  • @Jabba 我正在运行ping 127.0.0.1
  • @JaeGeeTee - 尝试显式使用 /bin/ping?
  • 并没有真正回答如何使用 call 或 check_call 进行非常小的输出的问题。
猜你喜欢
  • 2015-04-07
  • 1970-01-01
  • 2018-11-01
  • 2015-04-26
  • 2013-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多