簡體   English   中英

將子進程輸出顯示到標准輸出並將其重定向

[英]Displaying subprocess output to stdout and redirecting it

我正在通過 Python 的 subprocess 模塊運行腳本。 目前我使用:

p = subprocess.Popen('/path/to/script', stdout=subprocess.PIPE, stderr=subprocess.PIPE)
result = p.communicate()

然后我將結果打印到標准輸出。 這一切都很好,但由於腳本需要很長時間才能完成,我還希望從腳本實時輸出到標准輸出。 我管道輸出的原因是因為我想解析它。

將子進程的標准輸出保存到變量以供進一步處理並在子進程到達時運行時顯示它

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

with Popen('/path/to/script', stdout=PIPE, bufsize=1,
           universal_newlines=True) as p, StringIO() as buf:
    for line in p.stdout:
        print(line, end='')
        buf.write(line)
    output = buf.getvalue()
rc = p.returncode

保存子進程的 stdout 和 stderr 更復雜,因為您應該同時使用兩個流以避免死鎖

stdout_buf, stderr_buf = StringIO(), StringIO()
rc =  teed_call('/path/to/script', stdout=stdout_buf, stderr=stderr_buf,
                universal_newlines=True)
output = stdout_buf.getvalue()
...

其中teed_call()在這里定義


更新:這是一個更簡單的asyncio版本


舊版本:

這是一個基於tulip child_process.py示例的單線程解決方案:

import asyncio
import sys
from asyncio.subprocess import PIPE

@asyncio.coroutine
def read_and_display(*cmd):
    """Read cmd's stdout, stderr while displaying them as they arrive."""
    # start process
    process = yield from asyncio.create_subprocess_exec(*cmd,
            stdout=PIPE, stderr=PIPE)

    # read child's stdout/stderr concurrently
    stdout, stderr = [], [] # stderr, stdout buffers
    tasks = {
        asyncio.Task(process.stdout.readline()): (
            stdout, process.stdout, sys.stdout.buffer),
        asyncio.Task(process.stderr.readline()): (
            stderr, process.stderr, sys.stderr.buffer)}
    while tasks:
        done, pending = yield from asyncio.wait(tasks,
                return_when=asyncio.FIRST_COMPLETED)
        assert done
        for future in done:
            buf, stream, display = tasks.pop(future)
            line = future.result()
            if line: # not EOF
                buf.append(line)    # save for later
                display.write(line) # display in terminal
                # schedule to read the next line
                tasks[asyncio.Task(stream.readline())] = buf, stream, display

    # wait for the process to exit
    rc = yield from process.wait()
    return rc, b''.join(stdout), b''.join(stderr)

該腳本運行'/path/to/script命令並同時逐行讀取其 stdout 和 stderr。 這些行被相應地打印到父級的 stdout/stderr 並保存為字節串以供將來處理。 要運行read_and_display()協程,我們需要一個事件循環:

import os

if os.name == 'nt':
    loop = asyncio.ProactorEventLoop() # for subprocess' pipes on Windows
    asyncio.set_event_loop(loop)
else:
    loop = asyncio.get_event_loop()
try:
    rc, *output = loop.run_until_complete(read_and_display("/path/to/script"))
    if rc:
        sys.exit("child failed with '{}' exit code".format(rc))
finally:
    loop.close()

p.communicate()等待子p.communicate()完成,然后立即返回其整個輸出。

你有沒有嘗試過這樣的事情,在那里你逐行讀取子進程輸出?

p = subprocess.Popen('/path/to/script', stdout=subprocess.PIPE, stderr=subprocess.PIPE)
for line in p.stdout:
  # do something with this individual line
  print line

Popen.communicate 文檔明確指出:

Note: The data read is buffered in memory, so do not use this method if the data size is large or unlimited.

https://docs.python.org/2/library/subprocess.html#subprocess.Popen.communicate

所以如果你需要實時輸出,你需要使用這樣的東西:

stream_p = subprocess.Popen('/path/to/script', stdout=subprocess.PIPE, stderr=subprocess.PIPE)

while stream_line in stream_p:
    #Parse it the way you want
    print stream_line

這會將 stdout 和 stderr 打印到終端,並將 stdout 和 stderr 保存到變量中:

from subprocess import Popen, PIPE, STDOUT

with Popen(args, stdout=PIPE, stderr=STDOUT, text=True, bufsize=1) as p:
    output = "".join([print(buf, end="") or buf for buf in p.stdout])

但是,根據您的具體操作,這可能很重要,請注意:通過使用stderr=STDOUT ,我們無法再區分 stdout 和 stderr 並且調用print ,您的輸出將始終打印到標准輸出,不會不管它來自標准輸出還是標准錯誤。

對於 Python < 3.7,您將需要使用universal_newlines而不是text

3.7 新版功能:添加文本作為通用換行符的更易讀的別名。

來源: https : //docs.python.org/3/library/subprocess.html#subprocess.Popen

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM