使用subprocess重定向shell输出

1 投票
1 回答
632 浏览
提问于 2025-04-16 20:44

我有一个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 的时候,你会把子进程的输出捕获起来,这样就不会显示在你的标准输出上。你之所以在子进程结束时看到输出,是因为你自己打印了出来。

如果你想在程序运行时看到输出,而不是捕获它,或者想把所有输出都捕获起来然后最后再处理,你可以通过将 stdoutstderr 设置为 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的数据是可以接受的,那就没什么好担心的。

撰写回答