【问题标题】:python StringIO doesn't work as file with subrpocess.call()python StringIO 不能作为文件使用 subrpocess.call()
【发布时间】:2013-10-24 21:33:55
【问题描述】:

我正在使用 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 中缺少文件接口的这些部分,是否有任何原因导致无法实现?

【问题讨论】:

  • 这就是subprocess.check_output 的用途。
  • hmm,事情是subprocess.check_output 将所有输出作为一个字符串抛出,而我需要将 stdoutstderr 分开
  • hmm,找到了使用 Popen 而不是子进程的解决方法。这里解释stackoverflow.com/questions/10103551/…
  • selffix - Popen 是子进程的一部分,所以不是 subprocess.call() 或 subprocess.check_output()
  • stackoverflow.com/questions/5903501/…。是的,这是标准库中的一个错误。

标签: python file console stringio


【解决方案1】:

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

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

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

而且打开管道也没有任何意义,即使它们像StringIO 对象一样存在于内存中。它们用于通信,而不是存储:seektruncatelen 对管道毫无意义,readwrite 的行为与文件的行为截然不同。当您从管道read 时,返回的数据将从管道的缓冲区中删除,如果当您尝试 write 时该(相对较小的)缓冲区已满,您的进程将挂起,直到管道中出现 reads释放缓冲区空间。

因此,如果您想将字符串用作stdinstdoutstderr 用于子进程,StringIO 不会删除它,但Popen.communicate 是完美的。如上所述(并在subprocess 的文档中警告过),正确读取和写入管道很复杂。 Popen 为您处理这种复杂性。

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

【讨论】:

  • The documentation 具有误导性。它说stdout 的值可以是“一个现有的file object”,更不用说它要求文件对象有一个fileno
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 2013-06-01
    • 1970-01-01
    • 2018-09-13
    • 1970-01-01
    • 2016-06-13
    • 1970-01-01
    • 2013-10-07
    • 2021-11-21
    • 1970-01-01
    相关资源
    最近更新 更多