Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/341.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/283.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用python读取子进程的输出 上下文_Python_Buffer_Subprocess_Pipe_Flush - Fatal编程技术网

使用python读取子进程的输出 上下文

使用python读取子进程的输出 上下文,python,buffer,subprocess,pipe,flush,Python,Buffer,Subprocess,Pipe,Flush,我正在使用子流程模块从python启动一个流程。我希望能够在写入/缓冲输出后立即访问它(stdout、stderr) 解决方案必须支持Windows 7。我也需要Unix系统的解决方案,但我怀疑Windows的问题更难解决 该解决方案应该支持Python 2.6。我目前仅限于使用Python2.6,但仍然欢迎使用更高版本的Python的解决方案 解决方案不应使用第三方库。理想情况下,我会喜欢使用标准库的解决方案,但我愿意接受建议 解决方案必须适用于任何流程。假设对正在执行的流程没有控制权 子

我正在使用
子流程
模块从python启动一个流程。我希望能够在写入/缓冲输出后立即访问它(stdout、stderr)

  • 解决方案必须支持Windows 7。我也需要Unix系统的解决方案,但我怀疑Windows的问题更难解决
  • 该解决方案应该支持Python 2.6。我目前仅限于使用Python2.6,但仍然欢迎使用更高版本的Python的解决方案
  • 解决方案不应使用第三方库。理想情况下,我会喜欢使用标准库的解决方案,但我愿意接受建议
  • 解决方案必须适用于任何流程。假设对正在执行的流程没有控制权
子进程 例如,假设我想通过
子进程运行一个名为
counter.py
的python文件。
counter.py
的内容如下:

import sys

for index in range(10):

    # Write data to standard out.
    sys.stdout.write(str(index))

    # Push buffered data to disk.
    sys.stdout.flush()
import subprocess

command = ['python', 'counter.py']

process = subprocess.Popen(
    cmd,
    bufsize=1,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    ) 
父进程 负责执行
counter.py
示例的父进程如下所示:

import sys

for index in range(10):

    # Write data to standard out.
    sys.stdout.write(str(index))

    # Push buffered data to disk.
    sys.stdout.flush()
import subprocess

command = ['python', 'counter.py']

process = subprocess.Popen(
    cmd,
    bufsize=1,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    ) 
问题 使用
counter.py
示例,我可以在流程完成之前访问数据。这太棒了!这正是我想要的。但是,删除
sys.stdout.flush()
调用会阻止在需要时访问数据。这太糟糕了!这正是我不想要的。我的理解是,
flush()
调用强制将数据写入磁盘,在数据写入磁盘之前,它只存在于缓冲区中。请记住,我希望能够运行任何进程。我不希望流程执行这种刷新,但我仍然希望数据能够实时(或接近实时)可用。有没有办法做到这一点

关于父进程的简要说明。您可能会注意到我正在使用
bufsize=0
进行行缓冲。我希望这会导致每一行的磁盘刷新,但它似乎不是这样工作的。这个论点是如何运作的

您还会注意到我正在使用
subprocess.PIPE
。这是因为它似乎是在父进程和子进程之间生成IO对象的唯一值。我是通过查看
子流程
模块中的
Popen.\u get\u handles
方法得出这个结论的(我在这里指的是Windows定义)。有两个重要变量,
c2pread
c2pwrite
,它们是根据传递给
Popen
构造函数的
stdout
值设置的。例如,如果未设置
stdout
,则不会设置
c2pread
变量。使用文件描述符和类似文件的对象时也是如此。我真的不知道这是否重要,但我的直觉告诉我,我想要读和写IO对象来实现我想要实现的目标-这就是为什么我选择了
subprocess.PIPE
。如果有人能更详细地解释这一点,我将不胜感激。同样,如果有令人信服的理由使用除
subprocess.PIPE
之外的其他方法,我会洗耳恭听

用于从子进程检索数据的方法 在这里,我正在执行从线程中的子进程中读取标准的逻辑。这允许在数据可用之前读取被阻塞的场景。我们不必等待很长的时间,而是检查是否有可用的数据,在超时时间内读取,如果没有,则继续循环

我还尝试了另一种使用非阻塞读取的方法。此方法使用
ctypes
模块访问Windows系统调用。请注意,我并不完全理解我在这里所做的事情——我只是试图理解我在其他帖子中看到的一些示例代码。在任何情况下,以下代码段都不能解决缓冲问题。我的理解是,这只是另一种对抗潜在的长阅读时间的方法

import os
import subprocess

import ctypes
import ctypes.wintypes
import msvcrt

cmd = ['python', 'counter.py']

process = subprocess.Popen(
    cmd,
    bufsize=1,
    stdout=subprocess.PIPE,
    )


def read_output_non_blocking(stream):
    data = ''
    available_bytes = 0

    c_read = ctypes.c_ulong()
    c_available = ctypes.c_ulong()
    c_message = ctypes.c_ulong()

    fileno = stream.fileno()
    handle = msvcrt.get_osfhandle(fileno)

    # Read available data.
    buffer_ = None
    bytes_ = 0
    status = ctypes.windll.kernel32.PeekNamedPipe(
        handle,
        buffer_,
        bytes_,
        ctypes.byref(c_read),
        ctypes.byref(c_available),
        ctypes.byref(c_message),
        )

    if status:
        available_bytes = int(c_available.value)

    if available_bytes > 0:
        data = os.read(fileno, available_bytes)
        print data

    return data

while True:

    # Read standard out for child process.
    stdout = read_output_non_blocking(process.stdout)
    print stdout

    # Check whether child process is still active.
    if process.poll() != None:

        # Process is no longer active.
        break
非常感谢您的评论


这里讨论的是子进程的缓冲。您的
子流程
代码已经可以很好地工作,但是如果您有一个子流程缓冲其输出,那么
子流程
管道对此无能为力

这一点我再强调也不过分:您看到的缓冲延迟是子进程的责任,而它如何处理缓冲与
子进程
模块无关

你已经发现了这一点;这就是为什么在子进程中添加
sys.stdout.flush()
,可以更快地显示数据;子进程使用缓冲I/O(用于收集写入数据的内存缓存),然后将其发送到
sys.stdout
管道1

sys.stdout
连接到终端时,Python自动使用行缓冲;每当写入换行符时,缓冲区就会刷新。使用管道时,
sys.stdout
未连接到终端,而是使用固定大小的缓冲区

现在,可以告诉Python子进程以不同的方式处理缓冲;您可以设置环境变量或使用命令行开关来更改它对
sys.stdout
(和
sys.stderr
sys.stdin
)使用缓冲的方式。从:


强制stdin、stdout和stderr完全无缓冲。在重要的系统上,也将stdin、stdout和stderr置于二进制模式

[……]


如果将其设置为非空字符串,则相当于指定-u选项

如果您处理的子进程不是Python进程,并且遇到缓冲问题,那么您需要查看这些进程的文档,看看它们是否可以切换到使用无缓冲I/O,或者切换到更理想的缓冲策略

有一件事你可以试着告诉我们