Python:如何在运行os.system后获取标准输出?

137

在运行os.system调用后,我希望将stdout存储到变量中。

让我们以这行代码为例:

batcmd="dir"
result = os.system(batcmd)

result将包含错误代码(在Windows下为stderr 0,在某些Linux系统中为1)。

如何在不使用执行命令重定向的情况下获取上述命令的stdout


11
可以使用 subprocess 模块代替 os.system,因为 os.system 无法访问进程的输入或输出。 - Martijn Pieters
@MartijnPieters 这是唯一的方法吗? - Eduard Florinescu
3
不,但 subprocess 模块比你能做到的更好地封装了 Windows 和 POSIX 系统之间的差异。为什么要重新发明轮子呢? - Martijn Pieters
4
相信我,我之前已经重新发明了那个轮子。作为一个具备 C 语言背景的人,使用 fork/exec/pipe 等传统方法似乎是很自然的,但是 @MartijnPieters 是正确的,只要使用 subprocess 模块就可以了事。 - izak
1
Python 2.7 添加了 subprocess.check_output() 函数,这可能已经足够满足您的需求,但除此之外,这两个目标是几乎等价的。 - Martijn Pieters
显示剩余3条评论
6个回答

170

如果您只需要stdout输出,那么可以看一下subprocess.check_output()

import subprocess

batcmd="dir"
result = subprocess.check_output(batcmd, shell=True)
因为您正在使用os.system(),所以您需要设置shell=True才能获得相同的行为。但是,您确实需要注意有关将不受信任的参数传递给 shell 的安全问题
如果您还需要捕获stderr,只需在调用中添加stderr=subprocess.STDOUT即可:
result = subprocess.check_output([batcmd], stderr=subprocess.STDOUT)
将错误输出重定向到默认输出流。
如果您知道输出是文本,请添加text=True以使用平台默认编码对返回的字节值进行解码;如果该编解码器不适用于接收到的数据,则改为使用encoding="..."

在Windows中,只有在运行cmd.exe时才能工作:result = subprocess.check_output(["cmd.exe", batcmd]) - Eduard Florinescu
3
或者使用shell=True;这在subprocess文档中提到过,但我在这里明确指出。 - Martijn Pieters
在Jupyter Notebook中有没有办法做到这一点?看起来它只返回一个空字符串。 - Louis Yang
@LouisYang:是的,这在Jupyter笔记本中也可以工作。但请注意Python子进程的当前工作目录可能会有所不同,您可能需要将cwd参数设置为subprocess.check_output() - Martijn Pieters

25

这些答案对我没用。我不得不使用以下方法:

import subprocess
p = subprocess.Popen(["pwd"], stdout=subprocess.PIPE)
out = p.stdout.read()
print out

或者作为一个函数(在Python 2.6.7上需要使用shell=True,而check_output直到2.7版本才被添加,因此在这里无法使用):

def system_call(command):
    p = subprocess.Popen([command], stdout=subprocess.PIPE, shell=True)
    return p.stdout.read()

谢谢。可悲的是,我们中的一些人仍在使用2.6! - Robert Lugg

22
import subprocess
string="echo Hello world"
result=subprocess.getoutput(string)
print("result::: ",result)

7
你可能希望在这个答案中添加一个解释,说明为什么这个答案比当前排名较高的答案更好。如果是一个新的API,或者某些内容被弃用了,这一点非常重要,这样其他人就可以区分优缺点了。 - Randy
2
需要注意的是,这确实使用了Python 2中命令模块的传统遗留问题,但在此处找到的API文档中并未标记为已弃用:https://docs.python.org/3/library/subprocess.html#subprocess.getoutput 这对我非常有帮助,因为我有一个相当古老的进程,在成功调用时不返回0,这意味着无法使用check_output,并且call()不能按预期工作。点赞! - root

12

由于在处理较大的任务时subprocess会出现内存错误,因此我不得不使用os.system。关于这个问题的参考资料在这里。为了获得os.system命令的输出,我使用了以下解决方法:

import os

batcmd = 'dir'
result_code = os.system(batcmd + ' > output.txt')
if os.path.exists('output.txt'):
    fp = open('output.txt', "r")
    output = fp.read()
    fp.close()
    os.remove('output.txt')
    print(output)

1
os.system()同样在底层使用fork()/clone(),那么这有什么优势呢? - Charles Duffy
我知道的是一个方法有效,而另一个则无效。我正在使用Ubuntu和以下命令: out = os.system('ffmpeg -i ' + converted_filename + ' -ss ' + str(thumbnail_frame_time) + ' -vf scale=254:152 -vframes 1 -vcodec png -an -y ' + file_name + '.png') - Edhowler
这是非常不安全的。在将文件名替换为UNIX命令之前,请使用shlex.quote()(或其Python 2.x等效物pipes.quote())对所有文件名进行转义,否则,如果有人在您的上传文件夹中放置一个名为$(rm -rf ~)'$(rm -rf ~)'.mpg的文件,那么您可能会度过非常糟糕的一天。 - Charles Duffy
你是对的,谢谢。 - Edhowler

9

我希望为Windows解决方案提供更多说明。使用Python 2.7.5的IDLE,当我运行文件Expts.py时,会出现以下代码:

import subprocess
r = subprocess.check_output('cmd.exe dir',shell=False) 
print r

在Python Shell中,我只会得到与“cmd.exe”相对应的输出;“dir”部分被忽略了。然而,当我添加一个开关,比如/K或/C...

import subprocess
r = subprocess.check_output('cmd.exe /K dir',shell=False) 
print r

然后在Python Shell中,我得到了我期望的一切,包括目录列表。太棒了!

现在,如果我在DOS Python命令窗口中尝试任何相同的操作,没有使用开关或使用/K开关,它似乎会使窗口挂起,因为它正在运行一个子进程cmd.exe并等待进一步的输入-键入'exit'然后按[enter]以释放。但是使用/K开关,它可以完美地工作,并返回到python提示符。好吧。

更进一步......我认为这很酷......当我在Expts.py中执行以下操作时:

import subprocess
r = subprocess.call("cmd.exe dir",shell=False) 
print r

当我运行"dir"命令时,会弹出一个新的DOS窗口,只显示"cmd.exe"的结果而不是"dir"的结果。当我添加/C开关时,DOS窗口会非常快地打开和关闭,我来不及看到任何东西(因为/C在完成后终止)。相反,当我添加/K开关时,DOS窗口会弹出并保持打开,并且我得到了所有预期的输出,包括目录列表。
如果我尝试从DOS Python命令窗口执行相同的操作(使用subprocess.call而不是subprocess.check_output),则所有输出都在同一个窗口中,没有弹出窗口。没有开关时,"dir"部分被忽略,并且提示符从python提示符更改为DOS提示符(因为在python中运行了cmd.exe子进程;再次键入'exit',您将恢复到python提示符)。添加/K开关会打印出目录列表并将提示符从python更改为DOS,因为/K不会终止子进程。将开关更改为/C会给我们所有预期的输出,并返回到python提示符,因为子进程遵守/C终止。
抱歉回答这么冗长,但我对这个论坛上那些简短的“答案”感到沮丧,最好的情况下它们不起作用(似乎因为它们没有经过测试——就像在我的回答之前的Eduard F的回答中缺少开关一样),或者更糟的是,它们太简洁了,根本没有什么帮助(例如,“尝试使用subprocess而不是os.system”...好吧,现在呢?)。相比之下,我提供了经过测试的解决方案,并展示了它们之间的微妙差别。花费了很多时间,但是......希望这可以帮到您。

你的解决方案(subprocess.call("cmd.exe dir",shell=False))在Ubuntu 12.04下工作,除了shell=True。谢谢! - JohnnyLinTW

5

commands 也可以使用。

import commands
batcmd = "dir"
result = commands.getoutput(batcmd)
print result

它适用于Linux和Python 2.7。

4
请注意,这已经自Python 2.6起被弃用 - RjOllos
废弃的 == 不会再对该库进行代码更改/修复 - mbhargav294
...并且有可能在未来版本中被移除。 - Charles Duffy

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