将子进程的标准输出导入到一个变量中

116

我想在Python中使用子进程模块运行一个命令,并将输出存储在变量中。但是,我不希望命令的输出被打印到终端上。 对于以下代码:

def storels():
   a = subprocess.Popen("ls",shell=True)
storels()

我在终端中获取了目录列表,但并没有将其存储在a中。我也尝试过:

 def storels():
       subprocess.Popen("ls > tmp",shell=True)
       a = open("./tmp")
       [Rest of Code]
 storels()

这也会将ls的输出打印到我的终端。我甚至尝试过使用稍微有些过时的os.system方法执行此命令,因为在终端中运行ls > tmp并不会将ls打印到终端上,而是存储在tmp中。但是,同样的事情发生了。

编辑:

按照marcog的建议后,当运行更复杂的命令时,我遇到了以下错误:cdrecord --help。Python输出如下:

Traceback (most recent call last):
  File "./install.py", line 52, in <module>
    burntrack2("hi")
  File "./install.py", line 46, in burntrack2
    a = subprocess.Popen("cdrecord --help",stdout = subprocess.PIPE)
  File "/usr/lib/python2.6/subprocess.py", line 633, in __init__
    errread, errwrite)
  File "/usr/lib/python2.6/subprocess.py", line 1139, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory

2
只是一点提示,Python文档中不建议使用shell=true。http://docs.python.org/2/library/subprocess.html#frequently-used-arguments - SamHuckaby
请使用此链接 https://dev59.com/P2025IYBdhLWcg3wW0kc#75175057 - henrry
3个回答

162

要获取ls的输出,使用stdout=subprocess.PIPE

>>> proc = subprocess.Popen('ls', stdout=subprocess.PIPE)
>>> output = proc.stdout.read()
>>> print output
bar
baz
foo

命令cdrecord --help输出到stderr,因此您需要使用pipe。您还应该将命令拆分为标记列表,如下所示,或者选择传递shell=True参数,但这会启动完整的shell,如果您不控制命令字符串的内容,这可能很危险。

>>> proc = subprocess.Popen(['cdrecord', '--help'], stderr=subprocess.PIPE)
>>> output = proc.stderr.read()
>>> print output
Usage: wodim [options] track1...trackn
Options:
    -version    print version information and exit
    dev=target  SCSI target to use as CD/DVD-Recorder
    gracetime=# set the grace time before starting to write to #.
...

如果你有一个同时输出到stdout和stderr的命令,并且希望将它们合并,你可以通过将stderr导入stdout再捕获stdout来实现。

subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

正如Chris Morgan所提到的,你应该使用proc.communicate()而不是proc.read()

>>> proc = subprocess.Popen(['cdrecord', '--help'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
>>> out, err = proc.communicate()
>>> print 'stdout:', out
stdout: 
>>> print 'stderr:', err
stderr:Usage: wodim [options] track1...trackn
Options:
    -version    print version information and exit
    dev=target  SCSI target to use as CD/DVD-Recorder
    gracetime=# set the grace time before starting to write to #.
...

好的,这对于“ls”命令有效,但后来当我尝试运行另一个命令时遇到了不同的问题。当我使用该方法尝试运行“cdrecord --help”时,会出现回溯错误。 - Insomaniacal
非常感谢!搞定了。如果您不介意,您会如何区分使用stdout方法和这个stderr方法? - Insomaniacal
1
在这种情况下,我知道通常会将使用情况转储到stderr。但一般来说,一个简单的测试是 cmd > /dev/null,如果仍然看到输出,则它将进入stderr。通过将stderr管道传输到 cmd 2> /dev/null,它应该消失到 /dev/null 中以进行确认。 - moinudin
1
@Insomaniacal:通常情况下,会使用“stdout”,但有时也会使用“stderr”;一般来说,如果“stdout”可用,则使用它,否则尝试使用“stderr”。我相信在某些终端中可以安排使“stderr”以不同的颜色显示,这样您就可以很明显地区分它们。 - Chris Morgan
4
在Python3下,output不会是字符串,而是一个字节序列。尝试使用output.decode(encoding='utf-8')output.decode(encoding='latin-1')来获取一个字符串。 - Joachim W
显示剩余4条评论

48

如果你正在使用Python 2.7或更高版本,最简单的方法是使用subprocess.check_output() 命令。以下是一个示例:

output = subprocess.check_output('ls')

要同时重定向标准错误输出,您可以使用以下命令:

output = subprocess.check_output('ls', stderr=subprocess.STDOUT)



如果你想给命令传递参数,你可以使用一个列表或者调用一个shell并使用单个字符串。

output = subprocess.check_output(['ls', '-a'])
output = subprocess.check_output('ls -a', shell=True)

3
按照要求 ("我不希望命令的输出被打印到终端上"), 这不会禁止输出。 - Emre
使用stderr=subprocess.STDOUT来捕获STDOUT和STDERR时,这确实会压制我所有在终端上的输出。您是否有一个它不起作用的示例命令? - amicitas
Emre,我曾经在Python 2.6中看到过这种行为。 - Yuriy Vasylenko
这对我也没有抑制输出(使用Python 3.9并运行OpenSSL命令)。 - Michael K

15

使用a = subprocess.Popen(['cdrecord', '--help'], stdout=subprocess.PIPE) 或者 shell=True 两种方式都可以。前者更为推荐。

a = subprocess.Popen(['cdrecord', '--help'], stdout=subprocess.PIPE)

a = subprocess.Popen('cdrecord --help', shell=True, stdout=subprocess.PIPE)

另外,你应该使用.communicate()而不是使用Popen.stdout.read/Popen.stderr.read,关于原因请参考subprocess文档。

proc = subprocess.Popen(['cdrecord', '--help'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = proc.communicate()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接