检索subprocess.call()的输出

如何获得使用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'
>>>
573505 次浏览

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()方法。仅仅使用任何类似文件的对象都不能达到目的。

更多信息请参见在这里

我最近才知道如何做到这一点,这里有一些来自我当前项目的示例代码:

#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 ...

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

如果你有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以获得相同的结果。

正如下面所评论的,你可以在另一个答案中找到更详细的解释。

我有如下的解决方案。它捕获被执行的外部命令的退出码、stdout和stderr:

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)

我也有一个关于它的博客文章在这里

编辑:解决方案被更新到一个新的,不需要写入temp. files。

Ipython shell中:

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

根据sargue的回答。这要归功于sargue。

对于python 3.5+,建议使用从子进程模块运行函数。这将返回一个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)

关键是要使用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")