簡體   English   中英

Python:subprocess32 process.stdout.readline()等待時間

[英]Python: subprocess32 process.stdout.readline() waiting time

如果我使用例如“ls -Rlah /”運行以下函數“run”,我會立即通過print語句獲得輸出

import subprocess32 as subprocess
def run(command):
    process = subprocess.Popen(command,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.STDOUT)
    try:
        while process.poll() == None:
            print process.stdout.readline()
    finally:
        # Handle the scenario if the parent
        # process has terminated before this subprocess
        if process.poll():
            process.kill()

但是,如果我使用下面的python示例程序,它似乎停留在process.poll()或process.stdout.readline()上,直到程序完成。 我認為它是stdout.readline(),因為如果我將輸出的字符串數量從10增加到10000(在示例程序中)或者在每次打印后添加到sys.stdout.flush()中,則在運行中打印函數確實被執行了。

如何使子進程的輸出更加實時?

注意:我剛剛發現python示例程序在輸出時不執行sys.stdout.flush(),是否有一種方法讓子進程的調用者以某種方式強制執行此操作?

每5秒輸出10個字符串的示例程序。

#!/bin/env python
import time

if __name__ == "__main__":

    i = 0
    start = time.time()
    while True:
        if time.time() - start >= 5:
            for _ in range(10):
                print "hello world" + str(i)
            start = time.time()
            i += 1
        if i >= 3:
            break

在大多數系統上,命令行程序行緩沖區或塊緩沖區,具體取決於stdout是終端還是管道。 在unixy系統上,即使子節點並非真正從終端運行,父進程也可以創建一個偽終端來獲得類似終端的行為。 您可以使用pty模塊創建偽終端或使用pexpect模塊,以便輕松訪問交互式程序。

正如評論中所提到的,使用poll來讀取行會導致數據丟失。 一個例子是當進程終止時在stdout管道中留下的數據。 讀取pty與管道有點不同,你會發現當孩子關閉時需要捕獲一個IOError,以使其全部正常工作,如下例所示。

try:
    import subprocess32 as subprocess
except ImportError:
    import subprocess
import pty
import sys
import os
import time
import errno

print("running %s" % sys.argv[1])

m,s = (os.fdopen(pipe) for pipe in pty.openpty())
process = subprocess.Popen([sys.argv[1]],
                           stdin=s,
                           stdout=s,
                           stderr=subprocess.STDOUT)
s.close()

try:
    graceful = False
    while True:
        line = m.readline()
        print line.rstrip()
except IOError, e:
    if e.errno != errno.EIO:
        raise
    graceful = True
finally:
    # Handle the scenario if the parent
    # process has terminated before this subprocess
    m.close()
    if not graceful:
        process.kill()
    process.wait()

您應該在腳本中刷新標准輸出:

print "hello world" + str(i)
sys.stdout.flush()

當標准輸出是終端時,stdout是行緩沖的。 但是當它不是時,stdout是塊緩沖的,你需要顯式刷新它。

如果無法更改腳本的源代碼,可以使用Python的-u選項(在子進程中):

-u     Force stdin, stdout and stderr to be totally unbuffered. 

你的命令應該是: ['python', '-u', 'script.py']

通常,這種緩沖發生在用戶空間中。 沒有通用的方法來強制應用程序刷新其緩沖區:某些應用程序支持命令行選項(如Python),其他應用程序支持信號,其他應用程序不支持任何內容。

一種解決方案可能是模擬偽終端,給程序提供“提示”,它們應該在行緩沖模式下運行。 盡管如此,這並不是一個適用於所有情況的解決方案。

對於除python以外的東西,你可以嘗試使用unbuffer

unbuffer禁用從非交互式程序重定向程序輸出時發生的輸出緩沖。 例如,假設您通過運行od然后更多來查看fifo的輸出。 od -c / tmp / fifo | 更多在產生整頁輸出之前,您不會看到任何內容。 您可以按如下方式禁用此自動緩沖:

unbuffer od -c /tmp/fifo | more

通常,unbuffer不會從stdin讀取。 這在某些情況下簡化了unbuffer的使用。 要在管道中使用unbuffer,請使用-p標志。 示例:process1 | unbuffer -p process2 | process3

所以在你的情況下:

run(["unbuffer",cmd]) 

文檔中列出了一些警告,但這是另一種選擇。

暫無
暫無

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

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