2012年10月7日日曜日

python - 他のプログラムを起動して標準出力をノンブロックで監視する

Non-blocking read on a subprocess.PIPE in python

(更新)
上のリンクに書いてあるソースにコメントを加え、結果を利用する main 関数を追加。

#!/usr/bin/env python
# -*- coding: sjis -*-

import os
import sys
from subprocess import Popen, PIPE
import threading
import time

try:
    from Queue import Queue, Empty
except ImportError:
    from queue import Queue, Empty  # python 3.x


# posix モジュールはオペレーティングシステムの機能のうち、C 言語標準 および POSIX 標準 (Unix インタフェースをほんの少し隠蔽した) で標準化されている機能に対するアクセス機構を提供する
# 非 Unix オペレーティングシステムでは posix モジュール を使うことはできない→要するに Unix 系か Windows かを判定している
ON_POSIX = 'posix' in sys.builtin_module_names

def enqueue_output(out, queue):
    # iter は第2引数があると、1番目の関数の返り値と2番目の返り値が一致した際に iteration が終わる
    for line in iter(out.readline, b''):
        queue.put(line.rstrip())
    out.close()
    print 'enqueue_output finished'

if __name__=='__main__':
    argv = sys.argv
    argc = len(argv)
 
    if argc != 3:
        sys.exit(1)
 
    if not argv[2].isdigit():
        sys.exit(1)
 
    keyword = argv[1]
    time_limit = int(argv[2])
 
    print 'Keyword    : %s' % keyword
    print 'Time limit : %d' % time_limit
 
    # stdout=PIPE : 新しいパイプが子プロセスに向けて作られる
    # bufsize = 1 : 行毎にバッファ
    # close_fds : Unix 系 の場合、 true にすると子プロセス実行前に 0, 1, 2 以外のファイルディスクリプタが閉じられる。
    # Windowsの場合、 true にすると子プロセスにハンドルが継承されない。 Windows の場合は close_fds を true にしながら、 stdin, stdout, stderr を利用して標準ハンドルをリダイレクトすることはできない

    p = Popen(['python','outputter.py'], stdout=PIPE, bufsize=1, close_fds=ON_POSIX)
    q = Queue()
    t = threading.Thread(target=enqueue_output, args=(p.stdout, q))
    t.daemon = True # プログラムの終了と同時に終了する
    t.start()
 
    startTime = time.time()
 
    keyword_found = False
 
    while True:

        if time.time() - startTime > time_limit:
            print 'Time out. Keyword %s not found...' % keyword
            keyword_found = False
            break

        try:
            line = q.get_nowait() # タイムアウト無しで取得。何もなければ Empty 例外を出す
        except Empty:
            continue
            # print('no output yet')
        else: # got line
            if line == keyword:
                print 'Keyword %s found' % keyword
                keyword_found = True
                break
            else:
                print line
    try:
        p.kill()
    except OSError:
        pass
    if keyword_found:
        print 'Success'
    else:
        print 'False'

0 件のコメント:

コメントを投稿