सबप्रोसेस स्टैडआउट लाइन को लाइन से पढ़ें


235

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

#fake_utility.py, just generates lots of output over time
import time
i = 0
while True:
   print hex(i)*512
   i += 1
   time.sleep(0.5)

#filters output
import subprocess
proc = subprocess.Popen(['python','fake_utility.py'],stdout=subprocess.PIPE)
for line in proc.stdout:
   #the real code does filtering here
   print "test:", line.rstrip()

मैं वास्तव में जो व्यवहार चाहता हूं वह फ़िल्टर स्क्रिप्ट के लिए प्रत्येक पंक्ति को प्रिंट करने के लिए है क्योंकि यह सबप्रोसेस से प्राप्त होता है। क्रमबद्ध जैसे कि क्या teeकरता है लेकिन अजगर कोड के साथ।

मैं क्या खो रहा हूँ? क्या यह भी संभव है?


अपडेट करें:

यदि a sys.stdout.flush()को fake_utility.py में जोड़ा जाता है, तो कोड में अजगर 3.1 में वांछित व्यवहार है। मैं अजगर 2.6 का उपयोग कर रहा हूँ। आपको लगता है कि उपयोग proc.stdout.xreadlines()py3k के समान ही काम करेगा, लेकिन ऐसा नहीं है।


अपडेट 2:

यहाँ न्यूनतम काम कोड है।

#fake_utility.py, just generates lots of output over time
import sys, time
for i in range(10):
   print i
   sys.stdout.flush()
   time.sleep(0.5)

#display out put line by line
import subprocess
proc = subprocess.Popen(['python','fake_utility.py'],stdout=subprocess.PIPE)
#works in python 3.0+
#for line in proc.stdout:
for line in iter(proc.stdout.readline,''):
   print line.rstrip()

4
आप print line,इसके बजाय उपयोग कर सकते हैं print line.rstrip()(नोट: अंत में अल्पविराम)।
jfs


2
अपडेट 2 बताता है कि यह अजगर 3.0+ के साथ काम करता है लेकिन पुराने प्रिंट स्टेटमेंट का उपयोग करता है, इसलिए यह अजगर 3.0+ के साथ काम नहीं करता है।
रॉकी

यहाँ सूचीबद्ध किसी भी उत्तर ने मेरे लिए काम नहीं किया, लेकिन stackoverflow.com/questions/5411780/… ने किया!
बॉक्सिंग

जवाबों:


179

मुझे अजगर के साथ काम किए हुए काफी समय हो चुका है, लेकिन मुझे लगता है कि समस्या उस कथन के साथ है for line in proc.stdout, जो इस पर प्रसारित करने से पहले पूरे इनपुट को पढ़ता है। समाधान readline()इसके बजाय उपयोग करना है:

#filters output
import subprocess
proc = subprocess.Popen(['python','fake_utility.py'],stdout=subprocess.PIPE)
while True:
  line = proc.stdout.readline()
  if not line:
    break
  #the real code does filtering here
  print "test:", line.rstrip()

बेशक आपको अभी भी सबप्रोसेस की बफरिंग से निपटना है।

नोट: दस्तावेज़ीकरण के अनुसार, एक इट्रेटर के साथ समाधान readline()रीड-फॉरवर्ड बफर को छोड़कर, उपयोग करने के बराबर होना चाहिए , लेकिन (या ठीक इसी वजह से) प्रस्तावित परिवर्तन ने मेरे लिए अलग-अलग परिणाम उत्पन्न किए (विंडोज एक्सपी पर पायथन 2.5)।


11
के लिए file.readline()बनाम for line in fileदेखने bugs.python.org/issue3907 (संक्षेप में: यह python3 पर काम करता है, उपयोग io.open()अजगर पर 2.6+)
JFS

5
पीईपी 8 ( python.org/dev/peps/pep-0008 ) में "प्रोग्रामिंग अनुशंसाओं" के अनुसार एक ईओएफ के लिए अधिक पायथोनिक परीक्षण 'यदि लाइन नहीं है' होगा।
जेसन मॉक

14
@naxa: पाइप के लिए for line in iter(proc.stdout.readline, '')::।
jfs

3
@ जन-फिलिपग्रही: हाँ। 1. आप for line in proc.stdoutपायथन 3 का उपयोग कर सकते हैं (कोई रीड-फॉरवर्ड बग नहीं है) 2. '' != b''पायथन 3 पर - कोड को नेत्रहीन रूप से कॉपी-पेस्ट न करें - सोचें कि यह क्या करता है और यह कैसे काम करता है।
jfs

2
@JFSebastian: निश्चित रूप से, iter(f.readline, b'')समाधान बल्कि स्पष्ट है (और अगर कोई दिलचस्पी रखता है तो पायथन 2 पर भी काम करता है)। मेरी टिप्पणी का मुद्दा आपके समाधान को दोष देना नहीं था (क्षमा करें यदि ऐसा प्रतीत होता है, तो मैंने पढ़ा कि अभी, भी!), लेकिन लक्षणों की सीमा का वर्णन करने के लिए, जो इस मामले में काफी गंभीर हैं (अधिकांश Py2 /) 3 मुद्दों के नतीजे अपवाद हैं, जबकि यहाँ एक अच्छी तरह से व्यवहार किया गया लूप अंतहीन हो गया है, और कचरा संग्रह संघर्ष नई निर्मित वस्तुओं की बाढ़ से जूझ रहा है, लंबी अवधि और बड़े आयाम के साथ स्मृति उपयोग दोलनों का उत्पादन करता है)।
डॉ। जन-फिलिप गेर्के

45

पार्टी में थोड़ी देर हो गई, लेकिन मुझे यह देखकर आश्चर्य नहीं हुआ कि मुझे क्या लगता है कि इसका सबसे सरल समाधान क्या है:

import io
import subprocess

proc = subprocess.Popen(["prog", "arg"], stdout=subprocess.PIPE)
for line in io.TextIOWrapper(proc.stdout, encoding="utf-8"):  # or another encoding
    # do something with line

(इसके लिए अजगर की आवश्यकता है 3.)


25
मैं इस उत्तर का उपयोग करना चाहता हूं, लेकिन मुझे मिल रहा है: AttributeError: 'file' object has no attribute 'readable' py2.7
Dan Garthwaite

3
अजगर 3 के साथ काम करता है
Matanster

स्पष्ट रूप से यह कोड कई कारणों से मान्य नहीं है py3 / py3 संगतता और वास्तविक मूल्य प्राप्त करने का वास्तविक जोखिम: I / O ऑपरेशन बंद फाइल पर
sorin

3
@sorin उन चीजों में से कोई भी इसे "वैध नहीं" बनाता है। यदि आप एक पुस्तकालय लिख रहे हैं जिसे अभी भी पायथन 2 का समर्थन करने की आवश्यकता है, तो इस कोड का उपयोग न करें। लेकिन कई लोगों के पास एक दशक पहले की तुलना में हाल ही में जारी किए गए सॉफ़्टवेयर का उपयोग करने में सक्षम है। यदि आप एक बंद फ़ाइल पर पढ़ने की कोशिश करते हैं, तो आप उस अपवाद को प्राप्त करेंगे चाहे आप इसका उपयोग करें TextIOWrapperया नहीं। आप बस अपवाद को संभाल सकते हैं।
jbg

1
आपको पार्टी में शायद देर हो गई हो, लेकिन आप जवाब देते हैं कि पायथन के वर्तमान संस्करण के साथ तारीख तक, ty
Dusan Gligoric

20

वास्तव में, यदि आपने पुनरावृत्त छांटा है तो बफरिंग अब आपकी समस्या हो सकती है। आप अजगर को उप-प्रक्रिया में बता सकते हैं कि इसके आउटपुट को बफर न करें।

proc = subprocess.Popen(['python','fake_utility.py'],stdout=subprocess.PIPE)

हो जाता है

proc = subprocess.Popen(['python','-u', 'fake_utility.py'],stdout=subprocess.PIPE)

अजगर के भीतर से अजगर को बुलाते समय मुझे इसकी आवश्यकता थी।


14

आप इन अतिरिक्त मापदंडों को पास करना चाहते हैं subprocess.Popen:

bufsize=1, universal_newlines=True

फिर आप अपने उदाहरण के रूप में पुनरावृति कर सकते हैं। (पायथन 3.5 के साथ परीक्षण किया गया)


2
@nicoulaj यह काम करना चाहिए अगर सबप्रोसेस 32 पैकेज का उपयोग किया जाए।
क्वांटम

4

एक फ़ंक्शन जो वास्तविक समय में, रेखा से stdoutऔर stderrसमवर्ती दोनों को पुनरावृत्त करने की अनुमति देता है

मामले में आपको दोनों के लिए आउटपुट स्ट्रीम प्राप्त करने की आवश्यकता होती है stdoutऔर stderrएक ही समय में, आप निम्न फ़ंक्शन का उपयोग कर सकते हैं।

फ़ंक्शन दोनों पॉपेन पाइप को एक एकल पुनरावृत्ति में विलय करने के लिए क्युस का उपयोग करता है।

यहाँ हम फंक्शन बनाते हैं read_popen_pipes():

from queue import Queue, Empty
from concurrent.futures import ThreadPoolExecutor


def enqueue_output(file, queue):
    for line in iter(file.readline, ''):
        queue.put(line)
    file.close()


def read_popen_pipes(p):

    with ThreadPoolExecutor(2) as pool:
        q_stdout, q_stderr = Queue(), Queue()

        pool.submit(enqueue_output, p.stdout, q_stdout)
        pool.submit(enqueue_output, p.stderr, q_stderr)

        while True:

            if p.poll() is not None and q_stdout.empty() and q_stderr.empty():
                break

            out_line = err_line = ''

            try:
                out_line = q_stdout.get_nowait()
            except Empty:
                pass
            try:
                err_line = q_stderr.get_nowait()
            except Empty:
                pass

            yield (out_line, err_line)

read_popen_pipes() उपयोग में:

import subprocess as sp


with sp.Popen(my_cmd, stdout=sp.PIPE, stderr=sp.PIPE, text=True) as p:

    for out_line, err_line in read_popen_pipes(p):

        # Do stuff with each line, e.g.:
        print(out_line, end='')
        print(err_line, end='')

    return p.poll() # return status-code

2

आप w / o लूप की पंक्तियाँ भी पढ़ सकते हैं। Python3.6 में काम करता है।

import os
import subprocess

process = subprocess.Popen(command, stdout=subprocess.PIPE)
list_of_byte_strings = process.stdout.readlines()

1
या तार में बदलने के लिए:list_of_strings = [x.decode('utf-8').rstrip('\n') for x in iter(process.stdout.readlines())]
ndtreviv

1

मैं python3 के साथ यह कोशिश की और यह काम किया, स्रोत

def output_reader(proc):
    for line in iter(proc.stdout.readline, b''):
        print('got line: {0}'.format(line.decode('utf-8')), end='')


def main():
    proc = subprocess.Popen(['python', 'fake_utility.py'],
                            stdout=subprocess.PIPE,
                            stderr=subprocess.STDOUT)

    t = threading.Thread(target=output_reader, args=(proc,))
    t.start()

    try:
        time.sleep(0.2)
        import time
        i = 0

        while True:
        print (hex(i)*512)
        i += 1
        time.sleep(0.5)
    finally:
        proc.terminate()
        try:
            proc.wait(timeout=0.2)
            print('== subprocess exited with rc =', proc.returncode)
        except subprocess.TimeoutExpired:
            print('subprocess did not terminate in time')
    t.join()

1

राउतुलो के उत्तर का निम्नलिखित संशोधन मेरे लिए पायथन 2 और 3 (2.7.12 और 3.6.1) पर काम करता है:

import os
import subprocess

process = subprocess.Popen(command, stdout=subprocess.PIPE)
while True:
  line = process.stdout.readline()
  if line != '':
    os.write(1, line)
  else:
    break

0

डननो जब यह सबप्रोसेस मॉड्यूल में जोड़ा गया है, लेकिन पायथन 3 के साथ आपको उपयोग के साथ ठीक होना चाहिए proc.stdout.splitlines():

for line in proc.stdout.splitlines():
   print "stdout:", line
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.