使用subprocess重定向shell输出
我有一个Python脚本,它会调用很多shell命令。这个脚本可以在终端中直接运行,这样我希望能立刻看到输出;或者它也可以通过crontab定时运行,这种情况下我希望能把错误信息发到邮箱里。
为调用shell命令,我写了一个辅助函数:
import subprocess
import shlex
import sys
def shell(cmdline, interactive=True):
args = shlex.split(cmdline.encode("ascii"))
proc = subprocess.Popen(args, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
val = proc.communicate()
if interactive is True:
if proc.returncode:
print "returncode " + str(proc.returncode)
print val[1]
sys.exit(1)
else:
print val[0]
else:
if proc.returncode:
print ""
# send email with val[0] + val[1]
if __name__ == "__main__":
# example of command that produces non-zero returncode
shell("ls -z")
我遇到的问题有两个。
1) 在交互模式下,当shell命令执行得很慢(比如几分钟)时,我在命令完成之前什么都看不到,因为communicate()会把输出缓存起来。我想知道有没有办法能实时显示输出,而不是等命令完全执行完再显示?我还需要检查返回码,所以我才用communicate()。
2) 有些我调用的shell命令可能会产生很多输出(比如2MB)。关于communicate()的文档上说“如果数据量很大或没有限制,就不要使用这个方法。”有人知道什么算是“很大”吗?
1 个回答
2
1) 当你使用 communicate 的时候,你会把子进程的输出捕获起来,这样就不会显示在你的标准输出上。你之所以在子进程结束时看到输出,是因为你自己打印了出来。
如果你想在程序运行时看到输出,而不是捕获它,或者想把所有输出都捕获起来然后最后再处理,你可以通过将 stdout 和 stderr 设置为 None 来改变它的工作方式。这会让子进程使用和你的程序一样的输出流。你还需要把调用 communicate 的地方换成调用 wait:
if interactive is True:
proc = subprocess.Popen(args)
proc.wait()
if proc.returncode:
print "returncode " + str(proc.returncode)
sys.exit(1)
else:
proc = subprocess.Popen(args, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
val = proc.communicate()
if proc.returncode:
print ""
# send email with val[0] + val[1]
2) “太大”是指“太大到无法存储在内存中”,所以这完全取决于很多因素。如果在你的情况下,暂时在内存中存储2MB的数据是可以接受的,那就没什么好担心的。