Subproces.popen - 从站写入失败:管道损坏;中止

Subproces.popen - slave write failed: Broken pipe; aborting

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 Pipe Throughput 1 2 3 4 5 6 7 8 9 10

1 x Pipe-based Context Switching 1 2 3 4


Run: "Pipe-based Context Switching": slave write failed: Broken pipe; aborting

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

如果有任何解决方案或解决方法,我将不胜感激。我正在测试的系统是 Ubuntu 14.04.4 x64

该错误与 '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() and 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