繁体   English   中英

强制subprocess.Popen使用write()函数而不是fileno()将stdout / stderr写入python中的文件状对象

[英]Forcing subprocess.Popen to write stdout/stderr to file-like object in python using write() function and not fileno()

我的目标是使用python中的subprocess.Popen打开一个进程,并使该进程将其stdout和stderr传递给我编写的自定义RingBuffer类,从而使我能够从实例化的同一空间中定期检查缓冲区的内容来自的子流程。 这很重要,我知道有一些方法可以制作一个单独的程序,将子进程的输出通过管道传送到该环形缓冲区程序的stdin ,但是然后我必须手动检查一些包含环形缓冲区内容的底层文件,等等,理想的情况是将子流程的输出连接到我可以访问的某个对象。

首先,从subprocess文档(python 2.X)( https://docs.python.org/2/library/subprocess.html

stdin,stdout和stderr分别指定执行程序的标准输入,标准输出和标准错误文件句柄。 有效值为PIPE,现有文件描述符(正整数),现有文件对象和无。 PIPE指示应创​​建到子级的新管道。 使用默认设置无,将不会发生重定向。 子级的文件句柄将从父级继承。 此外,stderr可以是STDOUT,这表示子进程的stderr数据应捕获到与stdout相同的文件句柄中。

“现有的文件对象”,因此我假设如果我创建一个符合file接口的类,它应该可以工作,对吗?

假设我上了这样的课

class RingBuffer(object):

    def __init__(max_size=1024*1024):
      self.max_size = max_size
      self.current_size = 0


    def write(self, data):
        self.current_size += len(data)
        self.data.append(data)
        if self.current_size >= self.max_size_bytes:
            while self.current_size >= self.trim_size_bytes:
                try:
                    popped = self.data.pop()
                    self.current_size -= len(popped)
                except IndexError as e:
                    break

def writelines(self, sequence):
    for item in sequence:
        self.write(item)

def dumps(self):
    ret = [line for line in self.data]
    return '\n'.join(ret)

def clear(self):
    self.data.clear()
    self.current_size = 0

可以肯定的是,该程序中可能存在错误,但是您可以理解,它公开了write()函数,并将数据写入循环缓冲区,如果出现错误,则将缓冲区调整为一定大小,并在出现以下情况时让用户恢复数据他们想要使用dumps()函数。

现在,如果我尝试这样的事情

r = RingBuffer()
pr = subprocess.Popen(["timeout", "15", "yes"], stdout=r, stderr=subprocess.STDOUT)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 382, in __init__
    errread, errwrite), to_close = self._get_handles(stdin, stdout, stderr)
  File "/usr/local/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 818, in _get_handles
    c2pwrite = stdout.fileno()
AttributeError: 'RingBuffer' object has no attribute 'fileno'

好的,所以我的“类似于文件”的对象缺少符合文件接口的fileno()函数。 这就是问题所在。.为什么它需要文件号? 为什么不能仅使用提供的write()函数? 我假设它会绕过我的write功能,而不是使用fileno直接写入文件?

假设我添加了该函数的存根

def fileno()
    return None

然后这发生

r = RingBuffer()
pr = subprocess.Popen(["timeout", "15", "yes"], stdout=r, stderr=subprocess.STDOUT)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 390, in __init__
    errread, errwrite)
  File "/usr/local/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 1024, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory

所以我的问题是:如何强制subprocess.Popen对类似file的对象使用我的write()函数,而不是尝试直接写入从不存在的fileno()函数返回的文件句柄? 如果没有办法做到这一点..有什么办法可以完成我想要的吗?

我知道理论上我可以制作一些文件/tmp/ringlog.txt ,并在类实例化时打开该文件,然后让程序写入该文件,并让我的程序定期查看该文件并将其保存在使用类似的环形缓冲区算法的max_size ,但是很混乱。

另一个选择是制作一个程序,该程序可以读取stdin,写入文件并对内容进行环形缓冲,以将文件保持在一定的大小以下,但是我仍在处理实际文件,我只想将内容保留在内存中,可从调用python环境访问。

子进程将使用标准OS级别的文件写入调用写入其stdout,这意味着它需要与这些调用兼容的东西。 子进程无法查看Python的内存或对Python对象的调用方法。

如果您想将子进程的输出写入一个文件状的对象,而该对象不代表操作系统可以将其视为文件,那么您将必须通过管道接收输出并将其写入该文件状的对象你自己 您可以为此生成一个工作线程(并且,如果您打算在工作线程终止之前从中读取对象,请确保同步对该对象的访问),但是直接与管道进行交互可能更简单。

如果您要使用subprocess.Popen,那么我建议您了解使用管道带来的问题,通常在死锁方面。

参见: https : //thraxil.org/users/anders/posts/2008/03/13/Subprocess-Hanging-PIPE-is-your-enemy/

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM