【问题标题】:Subproces.popen - slave write failed: Broken pipe; abortingSubprocess.popen - 从属写入失败:管道损坏;中止
【发布时间】:2016-10-05 13:22:57
【问题描述】:

TLDR:坚持https://code.google.com/archive/p/byte-unixbench/issues/1

尝试使用subprocess.popen() 运行 UnixBench,同时捕获输出并实时打印出来。

这是我想出的子程序:

def run_and_print(command, cwd=None, catch_stderr = False):
    if catch_stderr:
        err_pipe = subprocess.PIPE
    else:
        err_pipe = subprocess.STDOUT

    p = subprocess.Popen(command, stdout=subprocess.PIPE, bufsize=1, cwd=cwd, stderr=err_pipe)
    r = ''
    while True:
        if catch_stderr:
            out = p.stderr.read(1)
        else:
            out = p.stdout.read(1)
        if out == "" and p.poll() != None:
            break
        sys.stdout.write(out)
        sys.stdout.flush()
        r += out

    return r

它适用于除 UnixBench 之外的所有用途。 Unixbench 过了一会儿就死了:

unixbench = run_and_print(['./Run'])

...

1 x 管道吞吐量 1 2 3 4 5 6 7 8 9 10

1 x 基于管道的上下文切换 1 2 3 4


运行:“基于管道的上下文切换”:slave write failed: Broken pipe;中止

Google 并没有太大帮助。我得到的唯一有意义的结果是 https://code.google.com/archive/p/byte-unixbench/issues/1 并建议创建 java 应用程序的解决方案对我不起作用,因为我需要以尽可能少的依赖项运行脚本。

我会感谢任何解决方案或解决方法。我正在测试的系统是 Ubuntu 14.04.4 x64

【问题讨论】:

  • 可能无济于事,但你试过p.communicate()吗?一种可能的技巧:不要尝试在 Python 中捕获输出;相反,通过将 stderr 和 stdout 重定向到不同的文本文件来运行命令。然后通过 Python 读取这些文件。
  • @FMc 关于写入文件的好主意,如果它被重定向到文件,不确定如何实时打印标准输出。您如何建议使用 p.communicate()?我的理解是,使用communicate 在处理完成之前我不会得到任何输出。感谢您的回复。
  • @user1556912,你的理解是正确的,如果你想立即输出,你不能使用通信。

标签: python subprocess


【解决方案1】:

该错误与'yes' reporting error with subprocess communicate() 相关,它提供了修复:使用preexec_fn(或使用Python 3)在子进程中重新启用SIGPIPE 信号。


无关:如果catch_stderr 为真并且p.stderrp.stdout 不完全同步,您的代码可能会死锁。

否则catch_stderr 无效(忽略缓冲):您的代码无论如何都会捕获 stderr。你可以简化它:

#!/usr/bin/env python
from shutil import copyfileobj
from subprocess import Popen, PIPE, STDOUT

def run_and_print(command, cwd=None):
    p = Popen(command, stdout=PIPE, stderr=STDOUT, bufsize=-1, cwd=cwd,
              preexec_fn=restore_signals)
    with p.stdout:
        tee = Tee()
        copyfileobj(p.stdout, tee)        
    return p.wait(), tee.getvalue()

其中Tee() 是一个类似文件的对象,它写入两个位置:stdout 和StringIO()

import sys
from io import BytesIO

class Tee:
   def __init__(self):
       self.file = BytesIO()
   def write(self, data):
       getattr(sys.stdout, 'buffer', sys.stdout).write(data)
       self.file.write(data)
   def getvalue(self):
       return self.file.getvalue()

restore_signals() is defined here.


如果您想在命令打印后立即在屏幕上看到输出;您可以内联Teecopyfileobj()use os.read(),以避免在将其写入stdout 之前阅读完整的length

chunks = []
with p.stdout:
    for chunk in iter(lambda: os.read(p.stdout.fileno(), 1 << 13), b''):
        getattr(sys.stdout, 'buffer', sys.stdout).write(chunk)
        sys.stdout.flush()
        chunks.append(chunk)
return p.wait(), b''.join(chunks)

要在子进程中禁用内部块缓冲,您可以尝试run it using stdbuf or pass pseudo-tty instead of the pipe

【讨论】:

  • 感谢您的所有建议!我会在测试它们后立即报告。至于 catch_stderr 没有效果,有时我会运行打印到stderr 而不是stdout 的命令(以dd 为例)。我会看看你的代码是否也能解决这个问题。非常感谢。
  • 我可以确认preexec_fn 修复了 UnixBench 的问题。它成功完成了所有基准测试。但是,我无法使用 Tee 制作您的版本以实时输出(原始函数的工作方式),但我可以修改我的代码以使其正常工作。我仍然需要找出解决您指出的潜在死锁问题的方法。再次感谢!你是最棒的!
  • 哦,我注意到你的更新太晚了。感谢实时输出的实现。今晚会试试的!所以需要实现“发送啤酒”按钮:)
  • @Anton:答案中的代码像您的代码一样捕获了标准输出/标准错误,但没有死锁的风险。您可以订阅问题提要(右下方)
猜你喜欢
  • 2011-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-08
相关资源
最近更新 更多