Как я могу передать стандартный ввод подпроцесса от итератора Python? - PullRequest
8 голосов
/ 01 августа 2011

Я пытаюсь использовать модуль subprocess в Python для связи с процессом, который читает стандартный ввод и записывает стандартный вывод в потоковом режиме.Я хочу, чтобы подпроцесс считывал строки из итератора, который производит ввод, а затем считывал выходные строки из подпроцесса.Может не быть взаимно однозначного соответствия между входной и выходной линиями.Как я могу передать подпроцесс от произвольного итератора, который возвращает строки?

Вот пример кода, который дает простой тестовый пример, и некоторые методы, которые я пробовал, которые по тем или иным причинам не работают:

#!/usr/bin/python
from subprocess import *
# A really big iterator
input_iterator = ("hello %s\n" % x for x in xrange(100000000))

# I thought that stdin could be any iterable, but it actually wants a
# filehandle, so this fails with an error.
subproc = Popen("cat", stdin=input_iterator, stdout=PIPE)

# This works, but it first sends *all* the input at once, then returns
# *all* the output as a string, rather than giving me an iterator over
# the output. This uses up all my memory, because the input is several
# hundred million lines.
subproc = Popen("cat", stdin=PIPE, stdout=PIPE)
output, error = subproc.communicate("".join(input_iterator))
output_lines = output.split("\n")

Итак, как мне построчно читать мой подпроцесс из итератора, а построчно читать из его стандартного вывода?

Ответы [ 3 ]

5 голосов
/ 01 августа 2011

Кажется, что простой способ - это разветвить и передать дескриптор ввода из дочернего процесса. Кто-нибудь может уточнить возможные недостатки этого? Или есть модули Python, которые делают его проще и безопаснее?

#!/usr/bin/python
from subprocess import *
import os

def fork_and_input(input, handle):
    """Send input to handle in a child process."""
    # Make sure input is iterable before forking
    input = iter(input)
    if os.fork():
        # Parent
        handle.close()
    else:
        # Child
        try:
            handle.writelines(input)
            handle.close()
        # An IOError here means some *other* part of the program
        # crashed, so don't complain here.
        except IOError:
            pass
        os._exit()

# A really big iterator
input_iterator = ("hello %s\n" % x for x in xrange(100000000))

subproc = Popen("cat", stdin=PIPE, stdout=PIPE)
fork_and_input(input_iterator, subproc.stdin)

for line in subproc.stdout:
    print line,
2 голосов
/ 03 января 2016

Для подачи стандартного ввода подпроцесса от итератора Python:

#!/usr/bin/env python3 
from subprocess import Popen, PIPE

with Popen("sink", stdin=PIPE, bufsize=-1) as process:
    for chunk in input_iterator:
        process.stdin.write(chunk)

Если вы хотите прочитать вывод одновременно, вам нужно threads или async.io:

#!/usr/bin/env python3
import asyncio
import sys
from asyncio.subprocess import PIPE
from contextlib import closing

async def writelines(writer, lines):
    # NOTE: can't use writer.writelines(lines) here because it tries to write
    # all at once
    with closing(writer):
        for line in lines:
            writer.write(line)
            await writer.drain()

async def main():
    input_iterator = (b"hello %d\n" % x for x in range(100000000))
    process = await asyncio.create_subprocess_exec("cat", stdin=PIPE, stdout=PIPE)
    asyncio.ensure_future(writelines(process.stdin, input_iterator))
    async for line in process.stdout:
        sys.stdout.buffer.write(line)
    return await process.wait()

if sys.platform == 'win32':
    loop = asyncio.ProactorEventLoop()  # for subprocess' pipes on Windows
    asyncio.set_event_loop(loop)
else:
    loop = asyncio.get_event_loop()
with closing(loop):
    sys.exit(loop.run_until_complete(main()))
0 голосов
/ 01 августа 2011

Follow этот рецепт Это дополнение к подпроцессу, которое поддерживает асинхронный ввод / вывод.Это все еще требует, чтобы ваш подпроцесс отвечал на каждую входную строку или группу строк частью своего вывода, хотя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...