9

我正在使用subprocess包从 python 脚本调用一些外部控制台命令,我需要将文件处理程序传递给它以分别获取stdoutstderr。代码大致如下所示:

import subprocess

stdout_file = file(os.path.join(local_path, 'stdout.txt'), 'w+')
stderr_file = file(os.path.join(local_path, 'stderr.txt'), 'w+')

subprocess.call(["somecommand", "someparam"], stdout=stdout_file, stderr=stderr_file)

这可以正常工作,并且正在创建具有相关输出的 txt 文件。然而,在内存中处理这些输出而不创建文件会更好。所以我使用 StringIO 包来处理它:

import subprocess
import StringIO

stdout_file = StringIO.StringIO()
stderr_file = StringIO.StringIO()

subprocess.call(["somecommand", "someparam"], stdout=stdout_file, stderr=stderr_file)

但这不起作用。失败:

  File "./test.py", line 17, in <module>
    subprocess.call(["somecommand", "someparam"], stdout=stdout_file, stderr=stderr_file)
  File "/usr/lib/python2.7/subprocess.py", line 493, in call
    return Popen(*popenargs, **kwargs).wait()
  File "/usr/lib/python2.7/subprocess.py", line 672, in __init__
    errread, errwrite) = self._get_handles(stdin, stdout, stderr)
  File "/usr/lib/python2.7/subprocess.py", line 1063, in _get_handles
    c2pwrite = stdout.fileno()
AttributeError: StringIO instance has no attribute 'fileno'

我看到它缺少本机文件对象的某些部分并因此而失败。

所以这个问题比实际更具教育意义 - 为什么 StringIO 缺少文件接口的这些部分,是否有任何原因无法实现?

4

2 回答 2

7

正如您在评论中所说,PopenPopen.communicate是正确的解决方案。

一点背景知识:真实的文件对象有文件描述符,也就是缺少fileno属性对象。StringIO它们只是普通的整数:您可能熟悉文件描述符 0、1 和 2,它们分别是stdinstdoutstderr。如果一个进程打开更多文件,它们会被分配 3、4、5 等。您可以使用 . 查看进程的当前文件描述符lsof -p

那么,为什么StringIO对象不能有文件描述符呢?为了得到一个,它需要打开一个文件或打开一个管道*。打开文件没有意义,因为打开文件是StringIO首先使用的重点。

打开管道也没有任何意义,即使它们像StringIO对象一样存在于内存中。它们用于通信,而不是 storage: seek, truncate,并且len对于管道根本没有意义,read并且write它们的行为与它们对文件的行为非常不同。当您read从管道中删除时,返回的数据将从管道的缓冲区中删除,如果在您尝试时该(相对较小的)缓冲区已满write,您的进程将挂起,直到read管道中的某些内容释放缓冲区空间。

因此,如果您想将字符串用作或用于子流程,stdin则不会削减它,但它是完美的。如上所述(并在的文档中警告过),正确读取和写入管道很复杂。为您处理这种复杂性。stdoutstderrStringIOPopen.communicatesubprocessPopen

* 我想我理论上可以想象第三种文件描述符对应于进程之间共享的内存区域?不确定为什么不存在。但是,呃,我不是内核开发人员,所以我确信这是有原因的。

于 2016-02-03T23:51:27.707 回答
-1

我认为您期望其他一些进程知道如何从主进程读取内存作为流。也许如果您可以将流传输到标准输入并将标准输出传输到流中,您可能会成功。

于 2013-10-16T17:02:47.730 回答