พิมพ์เอาต์พุตกระบวนการย่อยอย่างต่อเนื่องขณะที่กระบวนการกำลังทำงาน


204

ในการเปิดโปรแกรมจากสคริปต์ Python ของฉันฉันใช้วิธีการต่อไปนี้:

def execute(command):
    process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    output = process.communicate()[0]
    exitCode = process.returncode

    if (exitCode == 0):
        return output
    else:
        raise ProcessException(command, exitCode, output)

ดังนั้นเมื่อฉันเริ่มกระบวนการเช่นProcess.execute("mvn clean install")นี้โปรแกรมของฉันจะรอจนกระทั่งกระบวนการเสร็จสิ้นและหลังจากนั้นฉันก็จะได้ผลลัพธ์ที่สมบูรณ์ของโปรแกรมของฉัน สิ่งนี้น่ารำคาญถ้าฉันใช้กระบวนการที่ใช้เวลาสักครู่จึงจะเสร็จสิ้น

ฉันสามารถให้โปรแกรมของฉันเขียนบรรทัดผลลัพธ์ของกระบวนการทีละบรรทัดโดยโพลผลลัพธ์ของกระบวนการก่อนที่มันจะเสร็จสิ้นในลูปหรือบางอย่าง

** [แก้ไข] ขออภัยฉันค้นหาไม่ค่อยดีก่อนโพสต์คำถามนี้ การทำเกลียวเป็นกุญแจสำคัญ พบตัวอย่างที่นี่ซึ่งแสดงวิธีการทำ: ** Python Subprocess.Popen จากเธรด


ด้ายแทน subprocess ฉันคิดว่า
Ant

9
ไม่คุณไม่ต้องการกระทู้ แนวคิดการวางท่อทั้งหมดทำงานได้เพราะคุณสามารถอ่าน / เขียนจากกระบวนการในขณะที่กำลังทำงานอยู่
tokland

คำตอบ:


266

คุณสามารถใช้เราเตอร์lines = iter(fd.readline, "")กับเส้นกระบวนการเร็วที่สุดเท่าที่คำสั่งจะแสดงให้พวกเขา: นี่คือตัวอย่างเต็มรูปแบบที่แสดงกรณีการใช้งานทั่วไป (ขอบคุณ @jfs สำหรับความช่วยเหลือ):

from __future__ import print_function # Only Python 2.x
import subprocess

def execute(cmd):
    popen = subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines=True)
    for stdout_line in iter(popen.stdout.readline, ""):
        yield stdout_line 
    popen.stdout.close()
    return_code = popen.wait()
    if return_code:
        raise subprocess.CalledProcessError(return_code, cmd)

# Example
for path in execute(["locate", "a"]):
    print(path, end="")

24
ฉันได้ลองใช้รหัสนี้ (ด้วยโปรแกรมที่ใช้เวลาในการทำงานนานมาก) และสามารถยืนยันได้ว่าจะส่งออกสายตามที่ได้รับแทนที่จะรอให้การดำเนินการเสร็จสมบูรณ์ นี่คือคำตอบที่เหนือกว่าของ IMO
Andrew Martin

11
หมายเหตุ: งูหลาม 3 for line in popen.stdout: print(line.decode(), end='')คุณสามารถใช้ ให้การสนับสนุนทั้งงูหลาม 2 และ 3 ใช้ไบต์ตัวอักษร: b''มิฉะนั้นlines_iteratorจะสิ้นสุดลงในหลาม 3. ไม่เคย
JFS

3
ปัญหาด้วยวิธีนี้คือถ้ากระบวนการหยุดสักครู่โดยไม่ต้องเขียนอะไรไป stdout ไม่มีข้อมูลให้อ่านอีกต่อไป คุณจะต้องวนซ้ำเพื่อตรวจสอบว่ากระบวนการเสร็จสิ้นแล้วหรือไม่ ฉันลองใช้ subprocess32 ใน python 2.7
Har

7
มันควรจะทำงาน การขัดมันคุณสามารถเพิ่มbufsize=1(มันอาจช่วยปรับปรุงประสิทธิภาพการทำงานบนหลาม 2) ปิดpopen.stdoutท่ออย่างชัดเจน (โดยไม่ต้องรอการเก็บขยะที่จะดูแลมัน) และเพิ่มsubprocess.CalledProcessError(ชอบcheck_call(), check_output()ทำ) printคำสั่งที่แตกต่างกันเกี่ยวกับงูหลามที่ 2 และที่ 3: คุณสามารถใช้สับ softspace print line,(หมายเหตุ: เครื่องหมายจุลภาค) เพื่อหลีกเลี่ยงการเสแสร้งการขึ้นบรรทัดใหม่ทั้งหมดเช่นรหัสของคุณไม่และผ่านuniversal_newlines=Trueในหลาม 3 จะได้รับข้อความแทน bytes- คำตอบที่เกี่ยวข้อง
jfs

6
@binzhang นั่นไม่ใช่ข้อผิดพลาด stdout จะถูกบัฟเฟอร์ตามค่าเริ่มต้นของสคริปต์ Python (เช่นเดียวกับเครื่องมือ Unix จำนวนมาก) ลองexecute(["python", "-u", "child_thread.py"])ดู ข้อมูลเพิ่มเติม: stackoverflow.com/questions/14258500/…
tokland

84

ตกลงฉันจัดการเพื่อแก้ปัญหาได้โดยไม่ต้องกระทู้ (คำแนะนำใด ๆ ว่าทำไมการใช้กระทู้จะดีกว่าชื่นชม) โดยใช้ตัวอย่างจากคำถามนี้สกัด stdout ของกระบวนการย่อยในขณะที่มันกำลังทำงาน

def execute(command):
    process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

    # Poll process for new output until finished
    while True:
        nextline = process.stdout.readline()
        if nextline == '' and process.poll() is not None:
            break
        sys.stdout.write(nextline)
        sys.stdout.flush()

    output = process.communicate()[0]
    exitCode = process.returncode

    if (exitCode == 0):
        return output
    else:
        raise ProcessException(command, exitCode, output)

3
ผสานรหัส ifischer ฯ และ tokland ทำงานค่อนข้างดี (ฉันมีการเปลี่ยนแปลงprint line,ไปsys.stdout.write(nextline); sys.stdout.flush()มิฉะนั้นมันจะพิมพ์ออกมาทุกสองเส้นจากนั้นอีกครั้งนี้จะใช้อินเตอร์เฟซโน๊ตบุ๊ค IPython ดังนั้นอาจจะเป็นอย่างอื่นที่เกิดขึ้น -.. โดยไม่คำนึงถึงอย่างชัดเจนเรียกflush()ผลงาน.
eacousineau

3
นายคุณเป็นผู้ช่วยชีวิตของฉัน !! แปลกจริงๆที่ชนิดของสิ่งนี้จะไม่ได้สร้างในห้องสมุดในตัวเอง .. สาเหตุถ้าผมเขียน cliapp ฉันต้องการที่จะแสดงให้ทุกอย่างสิ่งที่ประมวลผลในวงทันที .. s'rsly ..
Holms

3
สามารถแก้ปัญหานี้ได้รับการแก้ไขอย่างต่อเนื่องพิมพ์ทั้งการส่งออกและข้อผิดพลาด? ถ้าฉันเปลี่ยนstderr=subprocess.STDOUTเป็นstderr=subprocess.PIPEแล้วโทรprocess.stderr.readline()จากภายในลูปฉันดูเหมือนจะวิ่งออกมาจากการหยุดชะงักอย่างมากที่ได้รับการเตือนเกี่ยวกับในเอกสารประกอบสำหรับsubprocessโมดูล
davidrmcharles

7
@ DavidCharles ฉันคิดว่าสิ่งที่คุณกำลังมองหาคือstdout=subprocess.PIPE,stderr=subprocess.STDOUTstderr นี้และฉันเชื่อว่า (แต่ฉันไม่ได้ทดสอบ) ว่ามันยังจับ stdin
Andrew Martin

ขอบคุณที่รอรหัสทางออก ไม่ทราบวิธีการทำงานออกมา
Vitaly Isaev

70

หากต้องการพิมพ์เอาต์พุตย่อยต่อบรรทัดของกระบวนการย่อยทันทีที่บัฟเฟอร์ stdout ถูกล้างใน Python 3:

from subprocess import Popen, PIPE, CalledProcessError

with Popen(cmd, stdout=PIPE, bufsize=1, universal_newlines=True) as p:
    for line in p.stdout:
        print(line, end='') # process line here

if p.returncode != 0:
    raise CalledProcessError(p.returncode, p.args)

ข้อสังเกต: คุณไม่ต้องการp.poll()- ลูปสิ้นสุดลงเมื่อถึง eof และคุณไม่ต้องการiter(p.stdout.readline, '')- บั๊กแบบอ่านล่วงหน้าได้รับการแก้ไขใน Python 3

ดูยังหลาม: อ่านสตรีมข้อมูลจาก subprocess.communicate ()


3
วิธีนี้ใช้ได้ผลสำหรับฉัน ทางออกที่ได้รับการยอมรับที่ระบุไว้ข้างต้นเป็นเพียงการพิมพ์บรรทัดว่างสำหรับฉัน
ชื่อรหัส

3
ฉันต้องเพิ่ม sys.stdout.flush () เพื่อพิมพ์ทันที
ชื่อรหัส

3
@Codename: คุณไม่จำเป็นต้องมีsys.stdout.flush()ใน parent - stdout นั้นจะถูก line-buffered หากมันไม่ได้ถูกเปลี่ยนเส้นทางไปยังไฟล์ / ไปป์ดังนั้นการพิมพ์จะlineล้างบัฟเฟอร์โดยอัตโนมัติ คุณไม่จำเป็นต้องsys.stdout.flush()มีลูกด้วยเช่นกัน - ผ่าน-uตัวเลือกบรรทัดคำสั่งแทน
jfs

1
@Codename: ถ้าคุณต้องการที่จะใช้ทำงานแล้ว> python -u your-script.py > some-fileหมายเหตุ: -uตัวเลือกที่ฉันได้กล่าวถึงข้างต้น (ไม่จำเป็นต้องใช้sys.stdout.flush())
jfs

1
@mvidelgauz ไม่จำเป็นต้องโทรp.wait()- มันถูกเรียกเมื่อออกจากwithบล็อก p.returncodeใช้
jfs

8

จริงๆแล้วมีวิธีที่ง่ายมากในการทำเช่นนี้เมื่อคุณต้องการพิมพ์ผลลัพธ์:

import subprocess
import sys

def execute(command):
    subprocess.check_call(command, stdout=sys.stdout, stderr=subprocess.STDOUT)

ที่นี่เราเพียงแค่ชี้ subprocess ไปยัง stdout ของเราเองและใช้ความสำเร็จที่มีอยู่หรือ api ข้อยกเว้น


1
วิธีนี้ง่ายและสะอาดกว่าโซลูชันของ @ tokland สำหรับ Python 3.6 ฉันสังเกตเห็นว่าไม่จำเป็นต้องใช้อาร์กิวเมนต์ shell = True
Good Will

จับดีความตั้งใจดี ลบออกshell=True
Andrew Ring

น่าเวทนามากและทำงานได้อย่างสมบูรณ์แบบด้วยรหัสน้อย บางทีคุณควรเปลี่ยนเส้นทาง subder stderr ไปยัง sys.stderr ด้วย?
มนู

มนูคุณสามารถ ฉันไม่ได้ที่นี่เพราะความพยายามในคำถามเปลี่ยนเส้นทาง stderr ไปยัง stdout
Andrew Ring

คุณสามารถอธิบายความแตกต่างระหว่าง sys.stdout และ subprocess.STDOUT ได้อย่างไร
Ron Serruya

7

@tokland

ลองใช้รหัสของคุณและแก้ไขให้ถูกต้องสำหรับ 3.4 และ windows dir.cmd เป็นคำสั่ง dir แบบง่ายบันทึกเป็นไฟล์ cmd

import subprocess
c = "dir.cmd"

def execute(command):
    popen = subprocess.Popen(command, stdout=subprocess.PIPE,bufsize=1)
    lines_iterator = iter(popen.stdout.readline, b"")
    while popen.poll() is None:
        for line in lines_iterator:
            nline = line.rstrip()
            print(nline.decode("latin"), end = "\r\n",flush =True) # yield line

execute(c)

3
คุณสามารถลดความซับซ้อนของรหัสของคุณ iter()และend='\r\n'ไม่จำเป็น Python ใช้โหมดการขึ้นบรรทัดใหม่แบบสากลโดยค่าเริ่มต้นคือโหมดใดก็ได้ที่'\n'ถูกแปลเป็น'\r\n'ระหว่างการพิมพ์ 'latin'อาจเป็นการเข้ารหัสที่ไม่ถูกต้องคุณสามารถใช้universal_newlines=Trueเพื่อรับเอาท์พุทข้อความใน Python 3 (ถอดรหัสโดยใช้การเข้ารหัสที่ต้องการของโลแคล) อย่าหยุดบน.poll()อาจมีข้อมูลที่ยังไม่ได้อ่านบัฟเฟอร์ หากสคริปต์ Python ทำงานในคอนโซลเอาต์พุตของมันจะเป็นแบบ line-buffered คุณสามารถบังคับให้บัฟเฟอร์บรรทัดโดยใช้-uตัวเลือก - คุณไม่ต้องการflush=Trueที่นี่
jfs

5

ในกรณีที่มีคนต้องการอ่านจากทั้งสองstdoutและstderrในเวลาเดียวกันโดยใช้หัวข้อนี่คือสิ่งที่ฉันมาด้วย:

import threading
import subprocess
import Queue

class AsyncLineReader(threading.Thread):
    def __init__(self, fd, outputQueue):
        threading.Thread.__init__(self)

        assert isinstance(outputQueue, Queue.Queue)
        assert callable(fd.readline)

        self.fd = fd
        self.outputQueue = outputQueue

    def run(self):
        map(self.outputQueue.put, iter(self.fd.readline, ''))

    def eof(self):
        return not self.is_alive() and self.outputQueue.empty()

    @classmethod
    def getForFd(cls, fd, start=True):
        queue = Queue.Queue()
        reader = cls(fd, queue)

        if start:
            reader.start()

        return reader, queue


process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(stdoutReader, stdoutQueue) = AsyncLineReader.getForFd(process.stdout)
(stderrReader, stderrQueue) = AsyncLineReader.getForFd(process.stderr)

# Keep checking queues until there is no more output.
while not stdoutReader.eof() or not stderrReader.eof():
   # Process all available lines from the stdout Queue.
   while not stdoutQueue.empty():
       line = stdoutQueue.get()
       print 'Received stdout: ' + repr(line)

       # Do stuff with stdout line.

   # Process all available lines from the stderr Queue.
   while not stderrQueue.empty():
       line = stderrQueue.get()
       print 'Received stderr: ' + repr(line)

       # Do stuff with stderr line.

   # Sleep for a short time to avoid excessive CPU use while waiting for data.
   sleep(0.05)

print "Waiting for async readers to finish..."
stdoutReader.join()
stderrReader.join()

# Close subprocess' file descriptors.
process.stdout.close()
process.stderr.close()

print "Waiting for process to exit..."
returnCode = process.wait()

if returnCode != 0:
   raise subprocess.CalledProcessError(returnCode, command)

ฉันแค่อยากจะแบ่งปันสิ่งนี้เมื่อฉันลงเอยกับคำถามนี้ที่พยายามทำสิ่งที่คล้ายกัน แต่ไม่มีคำตอบใดที่แก้ปัญหาของฉันได้ หวังว่ามันจะช่วยให้ใครบางคน!

Popen()ทราบว่าในกรณีการใช้งานของฉันกระบวนการภายนอกฆ่ากระบวนการที่เรา


1
ฉันต้องใช้บางสิ่งบางอย่างเช่น python2 ในขณะที่บางสิ่งเช่นนี้ควรมีให้ใน python2 แต่มันก็ไม่ได้เป็นเช่นนั้น
Stuart Axon

4

ใน Python> = 3.5 การใช้subprocess.runงานได้สำหรับฉัน:

import subprocess

cmd = 'echo foo; sleep 1; echo foo; sleep 2; echo foo'
subprocess.run(cmd, shell=True)

(การเอาท์พุทระหว่างการประมวลผลยังทำงานได้โดยไม่ต้องใช้shell=True) https://docs.python.org/3/library/subprocess.html#subprocess.run


2
นี่ไม่ใช่ "ระหว่างการดำเนินการ" การsubprocess.run()โทรจะส่งคืนเฉพาะเมื่อกระบวนการย่อยเสร็จสิ้นการทำงาน
tripleee

1
คุณช่วยอธิบายได้อย่างไรว่ามันไม่ใช่ "ระหว่างการประมวลผล"? บางอย่างที่>>> import subprocess; subprocess.run('top')ดูเหมือนว่าจะพิมพ์ "ระหว่างการดำเนินการ" (และด้านบนไม่เคยเสร็จสิ้น) บางทีฉันไม่เข้าใจความแตกต่างที่ลึกซึ้งบ้างไหม?
user7017793

หากคุณเปลี่ยนเส้นทางผลลัพธ์กลับไปที่ Python เช่นstdout=subprocess.PIPEคุณสามารถอ่านได้หลังจากtopเสร็จสิ้นเท่านั้น โปรแกรม Python ของคุณถูกบล็อกระหว่างการประมวลผล subprocess
tripleee

1
ถูกต้องแล้วที่เหมาะสม runวิธีการยังคงทำงานได้หากคุณสนใจเฉพาะในการมองเห็นการส่งออกที่มีการสร้างขึ้น หากคุณต้องการทำบางสิ่งบางอย่างกับเอาต์พุตในไพ ธ อนแบบอะซิงโครนัสคุณถูกต้องว่ามันไม่ทำงาน
user7017793

3

สำหรับใครก็ตามที่พยายามหาคำตอบสำหรับคำถามนี้เพื่อรับ stdout จากสคริปต์ Python โปรดทราบว่า Python บัฟเฟอร์ stdout ของมันและดังนั้นจึงอาจใช้เวลาสักครู่เพื่อดู stdout

สิ่งนี้สามารถแก้ไขได้โดยการเพิ่มสิ่งต่อไปนี้หลังจากแต่ละ stdout เขียนในสคริปต์เป้าหมาย:

sys.stdout.flush()

1
แต่การใช้งาน Python ในฐานะ subprocess ของ Python นั้นบ้าคลั่งในตอนแรก สคริปต์ของคุณควรimportเป็นสคริปต์อื่น ดูmultiprocessingหรือthreadingถ้าคุณต้องการการดำเนินการแบบขนาน
tripleee

3
@triplee มีหลายสถานการณ์ที่ใช้ Python ในการประมวลผลของ Python ที่เหมาะสม ฉันมีสคริปต์ชุดหลามจำนวนหนึ่งที่ฉันต้องการเรียกใช้ตามลำดับทุกวัน สิ่งเหล่านี้สามารถจัดทำโดยสคริปต์ Python หลักที่เริ่มต้นการดำเนินการและส่งอีเมลถึงฉันหากสคริปต์ลูกล้มเหลว แต่ละสคริปต์ถูกแซนด์บ็อกซ์จากที่อื่น - ไม่มีข้อขัดแย้งในการตั้งชื่อ ฉันไม่ขนานกันดังนั้นมัลติโพรเซสเซอร์และเธรดไม่เกี่ยวข้อง
user1379351

คุณสามารถเริ่มโปรแกรมไพ ธ อนอื่น ๆ โดยใช้ไพ ธ อนที่แตกต่างจากโปรแกรมที่ไพ ธ อนทำงานอยู่เช่นsubprocess.run("/path/to/python/executable", "pythonProgramToRun.py")
Kyle Bridenstine

3

เพื่อตอบคำถามเดิมวิธีที่ดีที่สุดคือการเปลี่ยนเส้นทาง subprocess stdoutโดยตรงกับโปรแกรมของคุณstdout(เลือกเดียวกันสามารถทำได้stderrเช่นในตัวอย่างด้านล่าง)

p = Popen(cmd, stdout=sys.stdout, stderr=sys.stderr)
p.communicate()

3
ไม่ได้ระบุสิ่งใด ๆstdoutและstderrทำสิ่งเดียวกันโดยใช้รหัสน้อย แม้ว่าฉันคิดว่าชัดเจนดีกว่าโดยนัย
tripleee

1

PoC นี้อ่านเอาต์พุตจากกระบวนการอย่างต่อเนื่องและสามารถเข้าถึงได้เมื่อจำเป็น เฉพาะผลลัพธ์สุดท้ายเท่านั้นที่ถูกเก็บไว้เอาต์พุตอื่น ๆ ทั้งหมดจะถูกยกเลิกดังนั้นจึงป้องกันไม่ให้ PIPE โตขึ้นจากหน่วยความจำ:

import subprocess
import time
import threading
import Queue


class FlushPipe(object):
    def __init__(self):
        self.command = ['python', './print_date.py']
        self.process = None
        self.process_output = Queue.LifoQueue(0)
        self.capture_output = threading.Thread(target=self.output_reader)

    def output_reader(self):
        for line in iter(self.process.stdout.readline, b''):
            self.process_output.put_nowait(line)

    def start_process(self):
        self.process = subprocess.Popen(self.command,
                                        stdout=subprocess.PIPE)
        self.capture_output.start()

    def get_output_for_processing(self):
        line = self.process_output.get()
        print ">>>" + line


if __name__ == "__main__":
    flush_pipe = FlushPipe()
    flush_pipe.start_process()

    now = time.time()
    while time.time() - now < 10:
        flush_pipe.get_output_for_processing()
        time.sleep(2.5)

    flush_pipe.capture_output.join(timeout=0.001)
    flush_pipe.process.kill()

print_date.py

#!/usr/bin/env python
import time

if __name__ == "__main__":
    while True:
        print str(time.time())
        time.sleep(0.01)

เอาต์พุต: คุณสามารถเห็นได้อย่างชัดเจนว่ามีเอาต์พุตเพียงช่วงเวลา ~ 2.5s เท่านั้น

>>>1520535158.51
>>>1520535161.01
>>>1520535163.51
>>>1520535166.01

0

ทำงานได้อย่างน้อยใน Python3.4

import subprocess

process = subprocess.Popen(cmd_list, stdout=subprocess.PIPE)
for line in process.stdout:
    print(line.decode().strip())

1
นี่เป็นปัญหาที่บล็อกในลูปจนกว่ากระบวนการจะเสร็จสิ้นการทำงาน
tripleee

0

ไม่มีคำตอบใด ๆ ที่ตรงกับความต้องการของฉันทั้งหมด

  1. ไม่มีเธรดสำหรับ stdout (ไม่มีคิว ฯลฯ )
  2. ไม่มีการปิดกั้นเนื่องจากฉันต้องตรวจสอบสิ่งอื่น ๆ ที่เกิดขึ้น
  3. ใช้ PIPE ตามที่ฉันต้องการเพื่อทำสิ่งต่าง ๆ เช่นสตรีมเอาต์พุตเขียนไปยังล็อกไฟล์และส่งคืนสำเนาสตริงของเอาต์พุต

พื้นหลังเล็กน้อย: ฉันใช้ ThreadPoolExecutor เพื่อจัดการกลุ่มของเธรดแต่ละตัวเรียกใช้กระบวนการย่อยและเรียกใช้พร้อมกัน (ใน Python2.7 แต่มันควรจะทำงานใน 3.x ที่ใหม่กว่าด้วย) ฉันไม่ต้องการใช้เธรดสำหรับการรวบรวมเอาต์พุตเนื่องจากฉันต้องการให้มีมากที่สุดเท่าที่จะเป็นไปได้สำหรับสิ่งอื่น ๆ (กลุ่มของกระบวนการ 20 จะใช้ 40 เธรดเพื่อเรียกใช้ 1 สำหรับเธรดกระบวนการและ 1 สำหรับ stdout ... และอื่น ๆ ถ้าคุณต้องการ stderr ฉันเดา)

ฉันกำลังอ่านข้อยกเว้นจำนวนมากและเช่นนี้ที่นี่เพื่อให้เป็นไปตามโค้ดที่ใช้งานได้จริง หวังว่าฉันไม่ได้ทำลายมันในการคัดลอกและวาง นอกจากนี้ข้อเสนอแนะยินดีต้อนรับอย่างมาก!

import time
import fcntl
import subprocess
import time

proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

# Make stdout non-blocking when using read/readline
proc_stdout = proc.stdout
fl = fcntl.fcntl(proc_stdout, fcntl.F_GETFL)
fcntl.fcntl(proc_stdout, fcntl.F_SETFL, fl | os.O_NONBLOCK)

def handle_stdout(proc_stream, my_buffer, echo_streams=True, log_file=None):
    """A little inline function to handle the stdout business. """
    # fcntl makes readline non-blocking so it raises an IOError when empty
    try:
        for s in iter(proc_stream.readline, ''):   # replace '' with b'' for Python 3
            my_buffer.append(s)

            if echo_streams:
                sys.stdout.write(s)

            if log_file:
                log_file.write(s)
    except IOError:
        pass

# The main loop while subprocess is running
stdout_parts = []
while proc.poll() is None:
    handle_stdout(proc_stdout, stdout_parts)

    # ...Check for other things here...
    # For example, check a multiprocessor.Value('b') to proc.kill()

    time.sleep(0.01)

# Not sure if this is needed, but run it again just to be sure we got it all?
handle_stdout(proc_stdout, stdout_parts)

stdout_str = "".join(stdout_parts)  # Just to demo

ฉันแน่ใจว่ามีการเพิ่มค่าใช้จ่ายที่นี่ แต่มันไม่ได้เป็นข้อกังวลในกรณีของฉัน ในทางปฏิบัติมันทำสิ่งที่ฉันต้องการ สิ่งเดียวที่ฉันยังไม่ได้แก้ไขคือเหตุผลที่ทำงานได้อย่างสมบูรณ์แบบสำหรับข้อความบันทึก แต่ฉันเห็นprintข้อความบางข้อความปรากฏขึ้นในภายหลังและทั้งหมดในครั้งเดียว


-2

ใน Python 3.6 ฉันใช้สิ่งนี้:

import subprocess

cmd = "command"
output = subprocess.call(cmd, shell=True)
print(process)

1
นี่ไม่ใช่คำตอบสำหรับคำถามนี้โดยเฉพาะ รอให้กระบวนการย่อยเสร็จสิ้นก่อนที่จะได้รับผลลัพธ์โดยเฉพาะและแม่นยำในสิ่งที่ OP พยายามหลีกเลี่ยง ฟังก์ชั่นเก่าแบบเก่าsubprocess.call()มีหูดบางตัวซึ่งได้รับการแก้ไขโดยฟังก์ชั่นที่ใหม่กว่า ใน Python 3.6 คุณมักจะใช้subprocess.run()สิ่งนี้ เพื่อความสะดวกยิ่งขึ้นฟังก์ชั่น wrapper รุ่นเก่าsubprocess.check_output()ยังคงมีอยู่ - ซึ่งจะส่งคืนเอาต์พุตจริงจากกระบวนการ (โค้ดนี้จะส่งคืนโค้ดออกเท่านั้นเท่านั้น
tripleee
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.