सबप्रोसेस डॉट कॉम्यूनिकेट () से स्ट्रीमिंग इनपुट पढ़ें


84

मैं पायथन का उपयोग subprocess.communicate()एक ऐसी प्रक्रिया से स्टडआउट को पढ़ने के लिए कर रहा हूं जो लगभग एक मिनट तक चलती है।

मैं stdoutएक स्ट्रीमिंग फ़ैशन में उस प्रक्रिया की प्रत्येक पंक्ति को कैसे प्रिंट कर सकता हूं , ताकि मैं आउटपुट को उसी तरह देख पाऊं जैसे वह उत्पन्न होता है, लेकिन फिर भी जारी रखने से पहले समाप्त होने वाली प्रक्रिया पर रोक लगा सकता है?

subprocess.communicate() एक ही बार में सभी आउटपुट देने के लिए प्रकट होता है।


संबंधित:
सबप्रोसेस

जवाबों:


44

कृपया ध्यान दें, मुझे लगता है कि जेएफ सेबेस्टियन की विधि (नीचे) बेहतर है।


यहाँ एक सरल उदाहरण है (त्रुटियों की जाँच नहीं):

import subprocess
proc = subprocess.Popen('ls',
                       shell=True,
                       stdout=subprocess.PIPE,
                       )
while proc.poll() is None:
    output = proc.stdout.readline()
    print output,

यदि lsबहुत तेज़ी से समाप्त होता है, तो जब आप सभी डेटा पढ़ चुके होते हैं, तो लूप समाप्त हो सकता है।

आप शेष को इस तरह से पकड़ सकते हैं:

output = proc.communicate()[0]
print output,

1
क्या यह योजना बफ़र ब्लॉकिंग समस्या का शिकार है जो अजगर डॉक्टर को संदर्भित करता है?
हेनरिक श्मेटेरिंग

@ हेनरिक, बफर अवरोधन समस्या कुछ ऐसी नहीं है जिसे मैं अच्छी तरह से समझता हूं। मेरा मानना ​​है कि (चारों ओर से गुगली करने से) यह समस्या केवल तब होती है जब आप लूप के अंदर stdout (और stderr?) से नहीं पढ़ते हैं। इसलिए मुझे लगता है कि उपरोक्त कोड ठीक है, लेकिन मैं निश्चित रूप से नहीं कह सकता।
अनुतु

1
यह वास्तव में एक अवरुद्ध समस्या से ग्रस्त है, कुछ साल पहले मुझे उस परेशानी का कोई अंत नहीं था जहां रीडलाइन ब्लॉक हो जाएगी, अगर खरीद समाप्त हो गई थी, तो भी इसे एक नई पंक्ति मिल जाएगी। मुझे समाधान याद नहीं है, लेकिन मुझे लगता है कि यह एक कार्यकर्ता धागा पर पढ़ता है और सिर्फ लूपिंग while proc.poll() is None: time.sleep(0)या उस प्रभाव के लिए कुछ करने के साथ कुछ करना था। मूल रूप से- आपको या तो यह सुनिश्चित करने की ज़रूरत है कि आउटपुट न्यूलाइन अंतिम चीज़ है जो प्रक्रिया करती है (क्योंकि आप दुभाषिया को फिर से लूप देने का समय नहीं दे सकते हैं) या आपको कुछ करने की ज़रूरत है "फैंसी।"
डैश-टॉम-बैंग

@ हेनरिक: एलेक्स मार्टेली लिखते हैं कि इस गतिरोध से कैसे बचा जाए: stackoverflow.com/questions/1445627/…
unutbu

6
बफर अवरोधक कभी-कभी लगता है की तुलना में सरल है: माता-पिता बच्चे के बाहर निकलने की प्रतीक्षा कर रहे हैं + बाल ब्लॉक माता-पिता को पढ़ने के लिए इंतजार कर रहे हैं और संचार पाइप में कुछ जगह खाली कर रहे हैं जो पूर्ण = गतिरोध है। यह इतना आसान है। पाइप जितना छोटा होगा उतना अधिक होगा।
मरख

160

जैसे ही सबप्रोसेस अपने स्टडआउट बफर को फ्लश करके लाइन से सबप्रोसेस 'आउटपुट लाइन प्राप्त करने के लिए:

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

p = Popen(["cmd", "arg1"], stdout=PIPE, bufsize=1)
with p.stdout:
    for line in iter(p.stdout.readline, b''):
        print line,
p.wait() # wait for the subprocess to exit

iter()पाइथन 2 में रीड-फ़ॉरवर्ड बग को वर्कअराउंड करने के लिए लिखे जाते ही लाइनों को पढ़ने के लिए उपयोग किया जाता है ।

यदि सबप्रोसेस 'स्टडआउट गैर-संवादात्मक मोड में एक लाइन बफ़रिंग के बजाय ब्लॉक बफ़रिंग का उपयोग करता है (जो कि बच्चे के बफर पूर्ण होने तक या बच्चे द्वारा स्पष्ट रूप से फ्लश होने तक उत्पादन में देरी की ओर जाता है) तो आप एक असंबद्ध आउटपुट का उपयोग करने के लिए मजबूर करने का प्रयास कर सकते हैं pexpect, ptyमॉड्यूल या unbuffer, stdbuf, scriptउपयोगिताओं , देख क्यों नहीं सिर्फ एक पाइप का उपयोग करें (popen ()): क्यू?


यहाँ पायथन 3 कोड है:

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

with Popen(["cmd", "arg1"], stdout=PIPE, bufsize=1,
           universal_newlines=True) as p:
    for line in p.stdout:
        print(line, end='')

नोट: पाइथन 2 के विपरीत जो उपप्रकार के बाइटस्ट्रेस का उत्पादन करता है; पायथन 3 पाठ मोड का उपयोग करता है ( locale.getpreferredencoding(False)एन्कोडिंग का उपयोग करके cmd का आउटपुट डिकोड किया गया है )।


b '' का क्या अर्थ है?
हारून

4
b''bytesपायथन 2.7 में एक शाब्दिक है और पायथन 3.
18

2
@JinghaoShi: bufsize=1यदि आप उपप्रणालियों को भी लिखते हैं (उपयोग करते हैं p.stdin), तो इससे अंतर हो सकता है , यह एक संवादात्मक ( pexpectजैसे) विनिमय करते समय एक गतिरोध से बचने में मदद कर सकता है - यह मानते हुए कि बाल प्रक्रिया में कोई बफ़रिंग समस्याएँ नहीं हैं। यदि आप केवल पढ़ रहे हैं तो जैसा कि मैंने कहा कि अंतर केवल प्रदर्शन में है: यदि ऐसा नहीं है तो क्या आप इसे दिखाते हुए न्यूनतम पूर्ण कोड उदाहरण प्रदान कर सकते हैं?
jfs

1
@ एलियन: हाँ। इसके लिए ऐसी तकनीकों की आवश्यकता होती है, जो तब तक stdout / stderr को पढ़ सकती हैं, जब तक आप stderr को stdout (पास stderr=subprocess.STDOUTकरके Popen()) में विलय नहीं कर देते । यह भी देखें, थ्रेडिंग या asyncio समाधान वहाँ से जुड़े।
JFS

2
@saulspatz अगर stdout=PIPEआउटपुट को कैप्चर नहीं करता है (आप अभी भी इसे स्क्रीन पर देखते हैं) तो आपका प्रोग्राम इसके बजाय सीधे या सीधे टर्मिनल पर प्रिंट हो सकता है। Stdout और stderr को मर्ज करने के लिए, पास करें stderr=subprocess.STDOUT(मेरी पिछली टिप्पणी देखें)। अपने tty को सीधे प्रिंट किए गए आउटपुट को कैप्चर करने के लिए, आप pexpect, pty सॉल्यूशंस का उपयोग कर सकते हैं। । यहां एक अधिक जटिल कोड उदाहरण है
jfs

6

मेरा मानना ​​है कि स्ट्रीमिंग फैशन में एक प्रक्रिया से आउटपुट एकत्र करने का सबसे सरल तरीका इस प्रकार है:

import sys
from subprocess import *
proc = Popen('ls', shell=True, stdout=PIPE)
while True:
    data = proc.stdout.readline()   # Alternatively proc.stdout.read(1024)
    if len(data) == 0:
        break
    sys.stdout.write(data)   # sys.stdout.buffer.write(data) on Python 3.x

readline()याread() समारोह केवल EOF पर एक खाली स्ट्रिंग लौटना चाहिए, के बाद प्रक्रिया समाप्त कर दिया है - अन्यथा यह अगर वहाँ पढ़ने के लिए कुछ भी नहीं है (है अवरुद्ध कर देगा readline(), न्यू लाइन भी शामिल है तो खाली तर्ज पर है, यह रिटर्न "\ n")। यह communicate()लूप के बाद एक अजीब अंतिम कॉल की आवश्यकता से बचा जाता है ।

read()अधिकतम मेमोरी उपयोग को कम करने के लिए बहुत लंबी लाइनों वाली फाइलों पर - इसके लिए दी गई संख्या मनमानी है, लेकिन इसे छोड़कर पूरे पाइप आउटपुट को एक बार में पढ़ना संभव नहीं है।


4
data = proc.stdout.read()सभी डेटा को पढ़ने तक ब्लॉक करता है। आप इससे भ्रमित हो os.read(fd, maxsize)सकते हैं कि पहले वापस आ सकते हैं (जैसे ही कोई डेटा उपलब्ध हो)।
JFS

आप सही हैं, मुझसे गलती हुई। हालाँकि अगर एक उचित संख्या में बाइट्स को एक तर्क के रूप में पारित किया जाता है read()तो यह ठीक काम करता है, और इसी तरह readline()ठीक काम करता है जब तक कि अधिकतम लाइन की लंबाई उचित हो। तदनुसार मेरा उत्तर अपडेट करें।
D Coetzee

3

यदि आप एक गैर-अवरुद्ध दृष्टिकोण चाहते हैं, तो उपयोग न करें process.communicate()। आप सेट करते हैं subprocess.Popen()तर्क stdoutके लिए PIPE, आप से पढ़ सकते हैं process.stdoutऔर देखें कि क्या प्रक्रिया अभी भी का उपयोग कर चलाता है process.poll()



3

यदि आप केवल वास्तविक समय में आउटपुट पास करने की कोशिश कर रहे हैं, तो इससे अधिक सरल होना मुश्किल है:

import subprocess

# This will raise a CalledProcessError if the program return a nonzero code.
# You can use call() instead if you don't care about that case.
subprocess.check_call(['ls', '-l'])

Subprocess.check_call () के लिए डॉक्स देखें ।

यदि आपको आउटपुट को संसाधित करने की आवश्यकता है, तो निश्चित रूप से, उस पर लूप। लेकिन अगर आप ऐसा नहीं करते हैं, तो बस इसे सरल रखें।

संपादित करें: JF सेबस्टियन दोनों बताते हैं कि stdout और stderr मापदंडों के लिए चूक sys.stdout और sys.stderr से होकर गुजरती हैं, और अगर sys.stdout और sys.stderr को बदल दिया गया है, तो यह विफल हो जाएगा (आउटपुट को कैप्चर करने के लिए आउटपुट का कहना है) परीक्षण)।


यह काम नहीं करेगा sys.stdoutया sys.stderrफ़ाइल-जैसी वस्तुओं से प्रतिस्थापित किया जाता है जिनमें कोई वास्तविक फाइलो () नहीं है। तो sys.stdout, sys.stderrतो प्रतिस्थापित नहीं कर रहे हैं तो यह और भी सरल है: subprocess.check_call(args)
jfs

धन्यवाद! मुझे sys.stdout / stderr को बदलने की योनि का एहसास हुआ, लेकिन किसी तरह कभी भी महसूस नहीं किया कि यदि आप तर्कों को छोड़ देते हैं, तो यह stdout और stderr को सही स्थानों पर भेज देता है। मुझे पसंद call()से अधिक check_call()है जब तक कि मैं चाहता हूँ CalledProcessError
नट

python -mthis: "त्रुटियों को कभी भी चुपचाप पास नहीं करना चाहिए। जब ​​तक कि स्पष्ट रूप से चुप न हो जाए।" यही कारण है कि उदाहरण कोड को check_call()अधिक पसंद करना चाहिए call()
jfs

हे। बहुत सारे कार्यक्रम जो मैंने किए call()हैं वे गैर-त्रुटि स्थितियों में नॉनजरो एरर कोड लौटाते हैं, क्योंकि वे भयानक हैं। तो हमारे मामले में, एक गैर-त्रुटि त्रुटि कोड वास्तव में एक त्रुटि नहीं है।
नट

हाँ। ऐसे कार्यक्रम हैं जैसे grepकि कोई त्रुटि न होने पर भी गैर-शून्य निकास स्थिति वापस कर सकते हैं - वे अपवाद हैं। डिफ़ॉल्ट रूप से शून्य निकास स्थिति सफलता का संकेत देती है।
jfs

1
myCommand="ls -l"
cmd=myCommand.split()
# "universal newline support" This will cause to interpret \n, \r\n and \r     equally, each as a newline.
p = subprocess.Popen(cmd, stderr=subprocess.PIPE, universal_newlines=True)
while True:    
    print(p.stderr.readline().rstrip('\r\n'))

1
यह समझाने के लिए हमेशा अच्छा होता है कि आपका समाधान लोगों को बेहतर समझने के लिए क्या करता है
डाफॉइस

2
आपको shlex.split(myCommand)इसके बजाय उपयोग करने पर विचार करना चाहिए myCommand.split()। यह उद्धृत तर्कों में रिक्त स्थान का सम्मान करता है, साथ ही साथ।
यूटाहजहेड

0

कुछ छोटे बदलावों के साथ एक और python3 समाधान जोड़ना:

  1. आपको शेल प्रक्रिया के निकास कोड को पकड़ने की अनुमति देता है (मैं उपयोग करते समय निकास कोड प्राप्त करने में असमर्थ रहा हूं with निर्माण )
  2. इसके अलावा वास्तविक समय में पाइप से बाहर निकलता है
import subprocess
import sys
def subcall_stream(cmd, fail_on_error=True):
    # Run a shell command, streaming output to STDOUT in real time
    # Expects a list style command, e.g. `["docker", "pull", "ubuntu"]`
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1, universal_newlines=True)
    for line in p.stdout:
        sys.stdout.write(line)
    p.wait()
    exit_code = p.returncode
    if exit_code != 0 and fail_on_error:
        raise RuntimeError(f"Shell command failed with exit code {exit_code}. Command: `{cmd}`")
    return(exit_code)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.