2

我想从我正在启动的 C 程序中捕获输出,如下所示:

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

唯一的问题是在 C 程序完成之前我没有得到输出,而实际上我需要在程序运行时逐行获取输出。为了使事情进一步复杂化,我必须解析每一行(我只需要行中的某些数据)。

例如,这里是一些示例输出:(我需要捕获“Thread on 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新手,非常感谢示例代码!!!

4

2 回答 2

3

您需要使用的原因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])))
于 2012-07-19T16:40:02.533 回答
2

你不能像那样使用 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 程序每秒向 Python 程序输出一行,长度为 1K 字符(如果 C 程序是完全缓冲的,那么可以做的事情并不多!)

在循环中读取标准输出,我们会看到(在 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
于 2012-07-19T15:16:53.087 回答