【问题标题】:Real-time piping with python subprocess.check_output or workaround?使用 python subprocess.check_output 实时管道或解决方法?
【发布时间】:2018-07-26 15:17:41
【问题描述】:

我有一个大型项目的一部分,在 python 中包含一个部分,如下所示:

failcount = 0
done = False
while done == False:
    try:
        result = subprocess.check_output(program)
        done = True
    except subprocess.CalledProcessError as e:
        failcount += 1
        logwrite('logfile.txt', 'Failed. Counter = {0}\nError message: {1}\n-'.format(failcount, e.returncode))
        if failcount == 20:
            print 'It failed 20 times, aborting...'
            quit()

这意味着从命令行运行“程序”。 “程序”是一个大型计算化学包,有时会失败,所以我在这里循环运行它。如果它失败了 20 次,那么我的 python 脚本就会终止。这工作得很好,它可以达到预期的效果。但是,我的问题是每次尝试我的化学包大约需要三个小时,我想随时监控它。

如果我从命令行手动运行它,我可以简单地执行“程序 > 日志文件”,然后使用 tail -f 来观察日志文件。但是,您似乎无法在 python 中执行以下操作:

subprocess.check_output(['program', '>', 'logfile'])

有没有办法让 python 在填充时打印出 subprocess.check_output 的内容?我认为 subprocess.check_output 只包含标准输出中的任何内容。我可以以某种方式在 python 和管道之间克隆它吗?

可能的解决方法:我制作了一个名为 run_program.sh 的 bash 脚本,它只执行上面列出的程序 > 日志文件,然后我使用 python 的子进程来执行 run_program.sh。这种方式我可以根据需要进行监控,但现在程序的输出在一个文件中,而不是在 python 中,所以我必须让 python 读取一个大的日志文件,并在需要时捕获错误消息,所以如果可以的话,我会更喜欢避免这样的事情。

【问题讨论】:

    标签: python subprocess pipe


    【解决方案1】:

    您可以使用subprocess.Popen,而不是使用subprocess.check_output。该对象代表您的子进程,并具有您可以读取的 stdout 和 stderr 属性。如果您的子进程仅使用标准输出,您可能只需在循环中调用Popen.stdout.readline()。但是,如果子进程写入其他管道,您可能会遇到死锁(有关详细信息,请参阅文档)。在这种情况下,我推荐http://stefaanlippens.net/python-asynchronous-subprocess-pipe-reading/ 中描述的consume 函数,它可以让您安全地打印stdout 和stderr,因为它是从子进程输出的。

    或者,如果您将shell=True 传递给check_output 函数,您使用subprocess.check_output(['program', '>', 'logfile']) 的方法应该可以工作。 > 是一个 shell 指令,如果您将其作为独立命令运行,则无法识别它。

    编辑:以上内容不会返回任何输出供您的 Python 程序使用。相反,subprocess.check_output('program | tee logfile', shell=True)

    如果使用 shell=True,请注意您可以完全控制 check_output 的参数。为了安全起见,绝不允许将任何用户或网络输入传递给 shell。请参阅this warning 了解原因。

    【讨论】:

    • 澄清一下:shell 重定向和管道由 Unix shell 直接处理(就底层系统调用而言:open()、dup()、pipe() 等)。这一切都是在调用 execve() 之前由一个子 shell 完成的。如果可能,Python subprocess.Popen() 将直接调用 execv(),但如果 shell=True,将创建一个 shell 并传递一个解析命令。前一种(默认)方法更安全,因为 shell 命令解析的许多功能都可以通过微妙甚至晦涩的方式加以利用。
    • 如果我使用 shell=true,你似乎对 result = subprocess.check_output(['program', '>', 'logfile']) 的工作是正确的;但是,如果必须读取输出文件,则会导致问题。如果我这样做,“结果”似乎是空的。如果我使用 Popen(这就是我在解决方法 bash 脚本中使用的),也会发生同样的事情。我希望我的程序的输出都通过管道传输到文件并同时输入到 python 字符串中。也许这是不可能的,谁知道呢。对我来说,之后在日志文件中读取 python 可能会更好......
    • @iammax 您可以使用tee 将输出通过管道传输到文件,同时仍将其发送到标准输出。即program | tee logfile 会将program 的输出写入标准输出和文件。
    • 该答案的问题:如果我这样做: result = subprocess.check_output('program | tee logfile', shell=True) 并且程序在终端中被杀死(通过使用 pkill 程序,如果我在日志文件中看到它处于循环中,我必须杀死它),它应该返回一个失败代码。但它没有,并且检查输出不会出现异常。它将程序的不完整输出放入“结果”中,然后使我解析结果的其他函数崩溃。它确实会按预期报告失败,但是,如果我离开 | tee 日志文件部分。是因为程序的行为,还是因为 tee 的行为?
    • 添加到前面的评论:如果我只是将它通过管道传输到带有 > 的文件,它也会“正确失败”。但不是与 tee
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-04
    • 2021-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多