簡體   English   中英

使用Python子進程在stdout上捕獲C程序的輸出? Pexpect的?

[英]Capture output from C program on stdout using Python subprocess? Pexpect?

我想從這樣啟動的C程序中捕獲輸出:

p = subprocess.Popen(["make", "run_pci"],
                     stdout=subprocess.PIPE,
                     cwd="/home/ecorbett/hello_world_pthread")
for ln in p.stdout:

唯一的問題是,直到C程序完成,我才獲得輸出,而實際上我需要在程序運行時逐行獲得輸出。 為了使事情更加復雜,我必須解析每一行(我只需要行中的某些數據)。

例如,這是一些示例輸出:(我需要捕獲“ Tile#上的線程”)

blahblah blah Thread blahblah blah tile 1: On 
blahblah blah Thread blahblah blah tile 2: OFF 
blahblah blah Thread blahblah blah tile 3 : Disable

我注意到我在下面鏈接的文章似乎也有同樣的問題。 我試圖弄清楚如何使其適應我的情況?

從ffmpeg獲取實時輸出以用於進度條(PyQt4,stdout)

Python新手,因此示例代碼大受贊賞!!!

您需要使用pexpect的原因是,如果程序的stdio未連接到tty,它將使用塊緩沖。 pexpect使用偽tty(pty),因此stdio將使用行緩沖,並且您可以在輸出行時訪問它們。

將您的代碼更改為:

p = pexpect.spawn('make', ['run_pci'], cwd="/home/ecorbett/hello_world_pthread")
for ln in p:
    ...

您可以使用pexpect.spawn.expect來獲取您感興趣的輸出:

while p.expect('Thread on Tile (\d+):', pexpect.EOF) == 0:
    print("Tile {0}".format(int(p.group[1])))

您不能像這樣使用p.stdout; 如果您要求“整個標准輸出”,則僅在過程終止(或填充管道緩沖區,這可能需要很長時間)后才可用。

您需要逐行從流程的標准輸出中讀取。

while True:
    ln = p.stdout.readline()
    if '' == ln:
        break
    m = re.search("Thread (?P<id>\d+)", ln);
    if m:
        # use m.group() to extract information
        # e.g. m.group('id') will hold the 12345 from "Thread 12345"

最好將stdout設置為行緩沖(通常在可能的情況下完全緩沖),但是我認為這只能在被調用的程序中完成。

這里有兩個要考慮的緩沖區。 一種是C程序的輸出緩沖區。 這可能不存在(無緩沖輸出),行緩沖或完全緩沖(1K,4K或8K是一些可能的大小)。

在程序中,將調用“ printf()”。 輸出為:

  • 出,如果沒有緩沖
  • 進入緩沖區 然后,如果緩沖了行,則輸出緩沖區中所有以換行符結尾的行;
  • 進入緩沖區 如果使用4K緩沖區完全緩沖並且該緩沖區大於4K,則輸出第一個4K。

現在,輸出進入Python的管道。 再次可以完全緩沖(stdout)或行緩沖(readline)。 因此輸出為:

  • 按照python程序的邏輯,如果管道中有一條完整的以換行符結尾的行,而我們正在使用readline
  • 如果緩沖區中的流水線小於4K,我們將使用“ for ln in stdout”。

在后一種情況下,緩沖區將以4K塊的形式進入Python邏輯。

現在讓我們想象一個行緩沖的 C程序,它每秒將一條長1K字符的行輸出到Python程序(如果C程序被完全緩沖,那么將無能為力!)

循環讀取stdout,我們將看到(在for循環內):

  • t = 0 ...無
  • t = 1 ...無(緩沖區已滿50%)
  • t = 2 ...無(緩沖區已滿75%)
  • t = 3 ...四行輸出
  • t = 4 ...無...

通過readline閱讀,我們將獲得:

  • t = 0 ...一行
  • t = 1 ...一行
  • t = 2 ...一行
  • t = 3 ...一行

在這里,我運行“ ping -c 3 -i 2 127.0.0.1”,以便以兩秒的間隔將三個數據包發送到本地主機。 一次ping需要大約六秒鍾。 我從ping讀取了輸出,並打印了一個時間戳。 ping的整個輸出足夠小,可以放入Python的完整緩沖區中。

#!/usr/bin/python

import subprocess
from time import gmtime, strftime

p = subprocess.Popen(["ping", "-c", "3", "-i", "2", "127.0.0.1"],
                 stdout=subprocess.PIPE)

for ln in p.stdout:
    print strftime("%H:%M:%S", gmtime()) + " received " + ln

# Now I start the same process again, reading the input the other way.

p = subprocess.Popen(["ping", "-c", "3", "-i", "2", "127.0.0.1"],
                 stdout=subprocess.PIPE)

while True:
    ln = p.stdout.readline()
    if '' == ln:
            break
    print strftime("%H:%M:%S", gmtime()) + " received " + ln

我在Linux機器上收到的輸出是預期的:

(nothing for the first six seconds)
15:40:10 received PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
15:40:10 received 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.037 ms
15:40:10 received 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.034 ms
15:40:10 received 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.031 ms
15:40:10 received
15:40:10 received --- 127.0.0.1 ping statistics ---
15:40:10 received 3 packets transmitted, 3 received, 0% packet loss, time 3998ms
15:40:10 received rtt min/avg/max/mdev = 0.031/0.034/0.037/0.002 ms

15:40:10 received PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
15:40:10 received 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.041 ms
15:40:12 received 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.039 ms
15:40:14 received 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.035 ms
15:40:14 received
15:40:14 received --- 127.0.0.1 ping statistics ---
15:40:14 received 3 packets transmitted, 3 received, 0% packet loss, time 3999ms
15:40:14 received rtt min/avg/max/mdev = 0.035/0.038/0.041/0.005 ms

暫無
暫無

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

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