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

以下にリンクした記事にも同じ問題があるようです。私はそれを自分の状況に適応させる方法を理解しようとしていましたか?

プログレスバー(PyQt4、stdout)で使用されるffmpegからのリアルタイム出力の取得

Python初心者なので、サンプルコードは大歓迎です!!!

4

2 に答える 2

2

そのようなp.stdoutを使用することはできません。「stdout全体」を要求した場合、これはプロセスの終了時(またはパイプバッファーの充填時、長時間かかる可能性があります)にのみ使用可能になります。

プロセスのstdoutから1行ずつ読み取る必要があります。

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をラインバッファリングに設定できれば(通常は可能な限り完全にバッファリングされます)、これも最適ですが、これは呼び出されたプログラム内からのみ実行できると思います。

ここで考慮すべき2つのバッファーがあります。1つはCプログラムの出力バッファです。これは、存在しない(バッファリングされていない出力)、ラインバッファリングされている、または完全にバッファリングされている可能性があります(1K、4K、または8Kがいくつかの可能なサイズです)。

プログラム内では、「printf()」が呼び出されます。出力は次のようになります。

  • バッファリングされていない場合
  • バッファに; 次に、行がバッファリングされている場合、バッファ内のすべての改行で終了する行が出力されます。
  • バッファに; 次に、4Kバッファで完全にバッファリングされ、バッファが4Kよりもいっぱいの場合、最初の4Kが出力されます。

これで、出力がPythonのパイプに入ります。これも完全にバッファリングされる(stdout)か、ラインバッファリングされる(readline)可能性があります。したがって、出力は次のようになります。

  • パイプラインに完全な改行で終了する行が1つあり、readlineを使用している場合は、Pythonプログラムのロジックに
  • パイプラインに4K未満があり、「for ln in stdout」を使用している場合は、バッファに追加します。

この最後のケースでは、バッファーは4KチャンクでPythonロジックに送られます。

ここで、Pythonプログラムに毎秒1K文字の長さの1行を出力する行バッファーCプログラムを想像してみましょう(Cプログラムが完全にバッファーされている場合、実行できることはあまりありません!)

サイクルでstdoutを読み取ると、(forループ内で)次のようになります。

  • t =0...何もありません
  • t = 1 ...なし(バッファが50%いっぱいです)
  • t = 2 ...なし(バッファが75%いっぱいです)
  • t = 3...4行の出力
  • t =4...何も..。

readlineを読むと、次のようになります。

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

ここでは、2秒間隔でローカルホストに3つのパケットを取得するために、「ping -c 3-i2127.0.0.1」を実行します。1回のpingの実行には約6秒かかります。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 に答える