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

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (520)

我的目标是subprocess.Popen在python中打开一个进程,并让这个进程将其stdout和stderr传递给我编写的自定义RingBuffer类,允许我定期检查缓冲区的内容,从同一个空间我实例化子进程。这很重要,我知道有一些方法可以创建一个单独的程序,将子stdin进程的输出传递给该ringbuffer程序,但是我必须手动检查一些包含环形缓冲区内容等的底层文件,等等。理想的做法是将子进程的输出连接到我有权访问的对象。

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

stdin,stdout和stderr分别指定执行程序的标准输入,标准输出和标准错误文件句柄。有效值为PIPE,现有文件描述符(正整数),现有文件对象和无。PIPE表示应该创建一个新的子管道。使用默认设置None,不会发生重定向; 子项的文件句柄将从父项继承。另外,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()符合文件界面的功能。这是问题所在的地方..为什么需要一个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将我的write()函数用于我的file类似对象,而不是试图直接写入从不存在的fileno()函数返回的文件句柄?如果没有办法做到这一点..有什么办法可以完成我想要的东西吗?

我知道理论上我可以创建一些文件,/tmp/ringlog.txt然后在类的实例化中打开该文件,然后让程序写入该文件,并让我的程序定期查看该文件并max_size使用类似的ringbuffer算法保留它,但那太乱了。

另一个选择是创建一个程序,读取stdin,写入文件,并响铃内容以保持文件在一定的大小,但然后我仍然处理实际的文件,我只是想将内容保存在内存和可以从调用python环境访问。

提问于
用户回答回答于

正如jasonharper指出的那样,这是一个愚蠢的问题。我不能说服一些任意的其他进程将其输出定向到我的内存空间中的python对象。你能告诉周五吗?

用户回答回答于

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

如果要将子进程的输出写入类似文件的对象,该对象不表示操作系统可以视为文件的内容,则必须通过管道接收输出并将其写入类文件对象你自己。您可以为此生成一个工作线程(并确保同步对该对象的访问,如果您计划在工作程序终止之前从中读取它),但是直接与管道交互可能更简单。

扫码关注云+社区

领取腾讯云代金券