2011-07-31 10 views
8

मैं मानक इनपुट पढ़ने और स्ट्रीमिंग फैशन में मानक आउटपुट लिखने वाली प्रक्रिया के साथ संवाद करने के लिए पायथन में subprocess मॉड्यूल का उपयोग करने का प्रयास कर रहा हूं। मैं एक पुनरावर्तक से subprocess पढ़ने लाइनों को चाहते हैं जो इनपुट उत्पन्न करता है, और फिर उपप्रोसेसर से आउटपुट लाइनों को पढ़ता है। इनपुट और आउटपुट लाइनों के बीच एक-से-एक पत्राचार नहीं हो सकता है। मैं एक मनमानी इटरेटर से एक उपप्रोसेसर कैसे खिला सकता हूं जो स्ट्रिंग देता है?मैं पाइथन इटरेटर से उपप्रोसेस के मानक इनपुट को कैसे खिला सकता हूं?

यहाँ कुछ उदाहरण कोड है कि एक साधारण परीक्षण का मामला देता है, और कुछ तरीकों मैंने कोशिश की है कि किसी न किसी कारण के लिए काम नहीं करते:

#!/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") 

तो मेरी उपप्रक्रिया एक से पढ़ा कि कैसे मैं कर सकते हैं रेखा से इटरेटर लाइन जब मैं लाइन से अपनी stdout लाइन से पढ़ा?

उत्तर

5

आसान तरीका कांटा लग रहा है और बाल प्रक्रिया से इनपुट हैंडल फ़ीड करता है। क्या कोई ऐसा करने के किसी भी संभावित डाउनसाइड्स पर विस्तार कर सकता है? या क्या पाइथन मॉड्यूल हैं जो इसे आसान और सुरक्षित बनाते हैं?

#!/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, 
+1

यदि आप बच्चे की प्रक्रिया में उपयोगकर्ता से बाहर निकलें() ', 'SystemExit' उठाया गया है। इसके बजाय ['os._exit (0)'] (https://docs.python.org/2/library/os.html#os._exit) – hakanc

+1

का उपयोग करना चाहिए [os.fork के बजाय 'थ्रेड() 'का उपयोग करें() '] (http://stackoverflow.com/a/32331150/4279) पोर्टेबिलिटी के लिए और समस्याओं को डीबग करने के लिए विभिन्न कठिन से बचने के लिए। यहां 'os.fork() 'के साथ संभावित मुद्दों का एक उदाहरण दिया गया है: [मानक पुस्तकालय में ताले कांटा पर स्वच्छ होना चाहिए] (http://bugs.python.org/issue6721) – jfs

0

this recipe का पालन करें यह उप-प्रोसेस पर एक ऐड-ऑन है जो एसिंक्रोनस I/O का समर्थन करता है। यह अभी भी आवश्यक है कि आपका सबप्रोसेस प्रत्येक इनपुट लाइन या लाइनों के समूह को इसके आउटपुट के हिस्से के साथ प्रतिक्रिया दे।

+1

मैं गारंटी नहीं दे सकता कि कार्यक्रम इनपुट की प्रत्येक पंक्ति के लिए उत्पादन का उत्पादन करेगा। वास्तव में, यह शायद नहीं होगा। –

+0

क्षमा करें, मैं सटीक नहीं था: मेरा मतलब यह था कि आपकी मुख्य प्रक्रिया कुछ उपज उत्पन्न करने के लिए आपके उपप्रोसेस में पर्याप्त इनपुट खाने में सक्षम होनी चाहिए, इस आउटपुट को पढ़ें, सबप्रोसेस को कुछ और इनपुट खिलाएं, और इसी तरह पाश। यदि यह मामला है, तो मेरे लिंक द्वारा इंगित नुस्खा आपकी मदद कर सकता है। मुख्य बिंदु यह है कि आपका सबप्रोसेस सभी इनपुट को देखने से पहले उत्पादन उत्पन्न करना शुरू कर सकता है। –

+0

हम्म। मेरी पाइपलाइन (विकल्पों के आधार पर) में संभावित रूप से एक तरह का कदम है, इसलिए संभवतः यह अधिकांश आउटपुट उत्पन्न नहीं करेगा जब तक कि यह सभी इनपुट प्राप्त नहीं हो जाता है। –

2

एक अजगर पुनरावर्तक से एक उपप्रक्रिया के मानक इनपुट फ़ीड करने के लिए:

#!/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())) 
संबंधित मुद्दे