Pythonのsubprocess.PIPEでの非ブロッキング読み取り


507

サブプロセスモジュールを使用してサブプロセスを開始し、その出力ストリーム(stdout)に接続しています。stdoutで非ブロッキング読み取りを実行できるようにしたい。.readlineを非ブロック化する方法、または呼び出す前にストリームにデータがあるかどうかを確認する方法はあり.readlineますか?これを移植可能にするか、少なくともWindowsとLinuxで動作させたいです。

ここに私が今それを行う方法があります(利用.readline可能なデータがない場合はブロックしています):

p = subprocess.Popen('myprogram.exe', stdout = subprocess.PIPE)
output_str = p.stdout.readline()

14
(Googleからですか?)PIPEのバッファーの1つがいっぱいになり、読み取られなくなると、すべてのPIPEがデッドロックします。たとえば、stderrがいっぱいになったときのstdoutデッドロック。読むつもりのないパイプは絶対に渡さないでください。
Nasser Al-Wohaibi 2014年

@ NasserAl-Wohaibiこれは常にファイルを作成する方が良いということですか?
チャーリーパーカー、

私は理解して好奇心てきた何かが、私はコメントを見てきましたので、最初の場所でのブロッキングが...私が求めている理由である:To avoid deadlocks: careful to: add \n to output, flush output, use readline() rather than read()
チャーリー・パーカー

これは、「仕様による」入力の受信を待機しています。
MathieuPagé19年

回答:


403

fcntlselectasyncprocこの場合には助けにはなりません。

オペレーティングシステムに関係なく、ブロックせずにストリームを読み取るための信頼できる方法は、以下を使用することQueue.get_nowait()です。

import sys
from subprocess import PIPE, Popen
from threading  import Thread

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

ON_POSIX = 'posix' in sys.builtin_module_names

def enqueue_output(out, queue):
    for line in iter(out.readline, b''):
        queue.put(line)
    out.close()

p = Popen(['myprogram.exe'], stdout=PIPE, bufsize=1, close_fds=ON_POSIX)
q = Queue()
t = Thread(target=enqueue_output, args=(p.stdout, q))
t.daemon = True # thread dies with the program
t.start()

# ... do other things here

# read line without blocking
try:  line = q.get_nowait() # or q.get(timeout=.1)
except Empty:
    print('no output yet')
else: # got line
    # ... do something with line

6
はい、これは私にとってはうまくいきますが、私はたくさん削除しました。これには良い習慣が含まれていますが、常に必要というわけではありません。Python 3.x 2.X compatおよびclose_fdsは省略できますが、引き続き機能します。しかし、すべてが何をしているかに注意し、たとえそれがうまくいくとしても、それを盲目的にコピーしないでください!(実際に最も簡単な解決策は、Sebのようにスレッドを使用してreadlineを実行することです。Qeuesはデータを取得する簡単な方法です。他にもあります。スレッドが答えです!)
Aki

3
スレッド内では、スレッドout.readlineとメインスレッドをブロックする呼び出しがあり、他のすべてが続行する前にreadlineが戻るまで待機する必要があります。簡単な方法はありますか?(私は私のプロセスから複数の行を読み取っています。これは、DBや物事を実行している別の.pyファイルでもあります)
Justin

3
@Justin: 'out.readline'は、別のスレッドで実行されるメインスレッドをブロックしません。
jfs 2012

4
サブプロセスのシャットダウンに失敗した場合はどうなりますか。例外のため?stdout-readerスレッドは停止せず、メインスレッドが終了してもpythonがハングしますよね。どうすればこれを回避できますか?python 2.xはスレッドの強制終了をサポートしていません。さらに悪いことに、スレッドの中断をサポートしていません。:(サブプロセスが確実にシャットダウンされるように例外を処理する必要がありますが、シャットダウンしない場合はどうすればよいですか?)
n611x007

3
私はパッケージshelljob pypi.python.org/pypi/shelljob
edA-qa mort-ora-y

77

私はよく似たような問題を抱えていました。私が頻繁に作成するPythonプログラムは、いくつかの主要な機能を実行すると同時に、コマンドライン(stdin)からのユーザー入力を受け入れることができる必要があります。ユーザー入力処理機能を別のスレッドに配置するだけでは問題が解決されませんreadline()。これは、ブロックされてタイムアウトがないためです。主要な機能が完了し、ユーザー入力を待つ必要がなくなった場合、通常はプログラムを終了させたいのですがreadline()、は行を待っている他のスレッドでまだブロックしているため、終了できません。この問題の解決策は、fcntlモジュールを使用してstdinを非ブロッキングファイルにすることです。

import fcntl
import os
import sys

# make stdin a non-blocking file
fd = sys.stdin.fileno()
fl = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)

# user input handling thread
while mainThreadIsRunning:
      try: input = sys.stdin.readline()
      except: continue
      handleInput(input)

私の意見では、これはselectまたはsignalモジュールを使用してこの問題を解決するよりも少しクリーンですが、UNIXでのみ機能します...


1
ドキュメントによると、fcntl()は、ファイル記述子、または.fileno()メソッドを持つオブジェクトを受け取ることができます。
DenilsonSáMaia

10
ジェシーの答えは正しくありません。Guidoによると、readlineは非ブロッキングモードでは正しく機能せず、Python 3000以前でも機能しません。bugs.python.org/ issue1175#msg56041 fcntlを使用してファイルを非ブロッキングモードに設定する場合は、下位レベルのos.read()を使用して、自分で行を分離する必要があります。ラインバッファリングを実行する高レベルの呼び出しとfcntlを混在させると問題が発生します。
anonnn

2
readlineの使用は、Python 2を参照してくださいanonnnの回答に間違っているようだstackoverflow.com/questions/375427/...
カタリンIacob

10
ビジーループは使用しないでください。データを待機するには、タイムアウトを指定してpoll()を使用します。
Ivo Danihelka、2011

@ステファノは何buffer_sizeとして定義されていますか?
猫、

39

Python 3.4では、非同期IO用の新しい暫定API- asyncioモジュールが導入されています

このアプローチはtwisted、@ Bryan Wardによる-based answerに似ています。プロトコルを定義し、データの準備が整い次第、そのメソッドが呼び出されます。

#!/usr/bin/env python3
import asyncio
import os

class SubprocessProtocol(asyncio.SubprocessProtocol):
    def pipe_data_received(self, fd, data):
        if fd == 1: # got stdout data (bytes)
            print(data)

    def connection_lost(self, exc):
        loop.stop() # end loop.run_forever()

if os.name == 'nt':
    loop = asyncio.ProactorEventLoop() # for subprocess' pipes on Windows
    asyncio.set_event_loop(loop)
else:
    loop = asyncio.get_event_loop()
try:
    loop.run_until_complete(loop.subprocess_exec(SubprocessProtocol, 
        "myprogram.exe", "arg1", "arg2"))
    loop.run_forever()
finally:
    loop.close()

docsの「サブプロセス」を参照してください。

コルーチンを使用して行を非同期に読み取ることができるオブジェクトasyncio.create_subprocess_exec()を返す高レベルのインターフェースがあります (/ Python 3.5+構文を使用)。ProcessStreamReader.readline()asyncawait

#!/usr/bin/env python3.5
import asyncio
import locale
import sys
from asyncio.subprocess import PIPE
from contextlib import closing

async def readline_and_kill(*args):
    # start child process
    process = await asyncio.create_subprocess_exec(*args, stdout=PIPE)

    # read line (sequence of bytes ending with b'\n') asynchronously
    async for line in process.stdout:
        print("got line:", line.decode(locale.getpreferredencoding(False)))
        break
    process.kill()
    return await process.wait() # wait for the child process to exit


if sys.platform == "win32":
    loop = asyncio.ProactorEventLoop()
    asyncio.set_event_loop(loop)
else:
    loop = asyncio.get_event_loop()

with closing(loop):
    sys.exit(loop.run_until_complete(readline_and_kill(
        "myprogram.exe", "arg1", "arg2")))

readline_and_kill() 次のタスクを実行します。

  • サブプロセスを開始し、そのstdoutをパイプにリダイレクトします
  • サブプロセスのstdoutから非同期で行を読み取る
  • サブプロセスを強制終了
  • 終了するのを待ちます

必要に応じて、各ステップをタイムアウト秒数で制限できます。


Python 3.4コルーチンを使用してこのようなことを試みると、スクリプト全体が実行されて初めて出力が得られます。サブプロセスが行を印刷したらすぐに、出力の行が印刷されるのを確認したいのですが。これが私が持っているものです:pastebin.com/qPssFGep
flutefreak7

1
@ flutefreak7:バッファリングの問題は現在の質問とは無関係です。可能な解決策については、リンクに従ってください。
jfs

ありがとう!print(text, flush=True)印刷されたテキストがウォッチャーの呼び出しですぐに利用できるようにするために、単に使用することによって私のスクリプトの問題を解決しましたreadline。実際にラップ/ウォッチしたいFortranベースの実行可能ファイルでテストしたところ、出力はバッファリングされないため、期待どおりに動作します。
flutefreak7

サブプロセスが持続し、さらに読み取り/書き込み操作を実行できるようにすることは可能ですか?readline_and_kill、2番目のスクリプトでは、subprocess.comunicate1回の読み取り/書き込み操作の後にプロセスを終了するという点で非常によく似ています。またstdout、サブプロセスが非ブロッキングとして処理する単一のパイプを使用していることもわかります。両方を使用しようとするstdoutと、stderr 最終的にブロックされます。
Carel

@Carel回答のコードは、回答で明示的に説明されているように意図したとおりに機能します。必要に応じて、他の動作を実装することができます。両方のパイプが使用されている場合、両方のパイプは等しくノンブロッキングです。両方のパイプから同時に読み取る方法の例を次に示します
jfs

19

asyncprocモジュールを試してください。例えば:

import os
from asyncproc import Process
myProc = Process("myprogram.app")

while True:
    # check to see if process has ended
    poll = myProc.wait(os.WNOHANG)
    if poll != None:
        break
    # print any new output
    out = myProc.read()
    if out != "":
        print out

モジュールは、S.Lottによって提案されたすべてのスレッドを処理します。


1
絶対に素晴らしい。生のサブプロセスモジュールよりもはるかに簡単です。Ubuntuで私にとって完璧に動作します。
セリン

12
asyncprocはWindowsでは機能せず、Windowsはos.WNOHANGをサポートしていません:-(
Bryan Oakley

26
asyncprocはGPLであり、その使用をさらに制限します:-(
Bryan Oakley

ありがとう。小さなことの1つ:asyncproc.pyでタブを8つのスペースに置き換えることです:)
12

asyncprocモジュールを介して起動したプロセスの戻りコードを取得できないようです。それが生成した出力のみ。
grayaii 2015年

17

これはTwistedで非常に簡単に行うことができます。既存のコードベースによっては、これは使いにくいかもしれませんが、ツイストアプリケーションを構築している場合、このようなことはほとんど簡単になります。ProcessProtocolクラスを作成し、outReceived()メソッドをオーバーライドします。ツイスト(使用するリアクターによって異なります)は通常select()、異なるファイル記述子(多くの場合、ネットワークソケット)からのデータを処理するためにインストールされたコールバックを備えた大きなループです。したがって、このoutReceived()メソッドは、からのデータを処理するためのコールバックをインストールするだけSTDOUTです。この動作を示す簡単な例は次のとおりです。

from twisted.internet import protocol, reactor

class MyProcessProtocol(protocol.ProcessProtocol):

    def outReceived(self, data):
        print data

proc = MyProcessProtocol()
reactor.spawnProcess(proc, './myprogram', ['./myprogram', 'arg1', 'arg2', 'arg3'])
reactor.run()

ツイストドキュメントはこの上でいくつかの良い情報を持っています。

Twistedを中心にアプリケーション全体を構築すると、ローカルまたはリモートの他のプロセスとの非同期通信が、このように非常にエレガントになります。一方、プログラムがTwistedの上に構築されていない場合、これはそれほど役に立ちません。特定のアプリケーションに適用できない場合でも、これが他の読者に役立つことを願っています。


駄目だ。ドキュメントselectによると、ファイル記述子を含むウィンドウでは機能しないはずです
n611x007

2
@naxa select()彼が言っているのはあなたと同じだとは思いません。TwistedWindowsで動作するため、これを想定しています...
notbad.jpeg


1
「ツイスト(使用するリアクターによって異なります)は通常、大きなselect()ループです」とは、いくつかのリアクターから選択できることを意味します。select()一つはのUnixおよびUNIX同類上で最もポータブル一つですが、二つの原子炉は、Windowsで利用可能もありますtwistedmatrix.com/documents/current/core/howto/...
clacke

14

select&read(1)を使用します。

import subprocess     #no new requirements
def readAllSoFar(proc, retVal=''): 
  while (select.select([proc.stdout],[],[],0)[0]!=[]):   
    retVal+=proc.stdout.read(1)
  return retVal
p = subprocess.Popen(['/bin/ls'], stdout=subprocess.PIPE)
while not p.poll():
  print (readAllSoFar(p))

readline()の場合:

lines = ['']
while not p.poll():
  lines = readAllSoFar(p, lines[-1]).split('\n')
  for a in range(len(lines)-1):
    print a
lines = readAllSoFar(p, lines[-1]).split('\n')
for a in range(len(lines)-1):
  print a

6
駄目だ。ドキュメントselectによると、ファイル記述子を含むウィンドウでは機能しないはずです
n611x007

ああ、神様。メガバイト、または場合によってはギガバイトを1文字ずつ読み取る...これは私が長い間見た中で最悪の考えです...言うまでもなくproc.stdout.read()、引数がどれほど小さいかに関わらず、このコードは機能しません。ブロッキング呼び出し。
wvxvw

OSError: [WinError 10093] Either the application has not called WSAStartup, or WSAStartup failed
nmz787

8

1つの解決策は、プロセスの読み取りを実行する別のプロセスを作成するか、タイムアウトのあるプロセスのスレッドを作成することです。

タイムアウト関数のスレッドバージョンは次のとおりです。

http://code.activestate.com/recipes/473878/

ただし、入ってくるstdoutを読む必要がありますか?別の解決策は、出力をファイルにダンプし、p.wait()を使用してプロセスが完了するのを待つことです。

f = open('myprogram_output.txt','w')
p = subprocess.Popen('myprogram.exe', stdout=f)
p.wait()
f.close()


str = open('myprogram_output.txt','r').read()

タイムアウト後にrecpieのスレッドが終了せず、それを殺すことはサブプロセスを殺すことができるかどうかに依存するようです(たとえば、この点で無関係です)それは読み取ります(あなたができるはずですが、できない場合に備えて..) 。
n611x007 2013

7

免責事項:これは竜巻に対してのみ機能します

これを行うには、fdを非ブロッキングに設定し、ioloopを使用してコールバックを登録します。これをtornado_subprocessという名前の卵にパッケージ化しました。PyPI経由でインストールできます。

easy_install tornado_subprocess

今、あなたはこのようなことをすることができます:

import tornado_subprocess
import tornado.ioloop

    def print_res( status, stdout, stderr ) :
    print status, stdout, stderr
    if status == 0:
        print "OK:"
        print stdout
    else:
        print "ERROR:"
        print stderr

t = tornado_subprocess.Subprocess( print_res, timeout=30, args=[ "cat", "/etc/passwd" ] )
t.start()
tornado.ioloop.IOLoop.instance().start()

RequestHandlerと一緒に使用することもできます

class MyHandler(tornado.web.RequestHandler):
    def on_done(self, status, stdout, stderr):
        self.write( stdout )
        self.finish()

    @tornado.web.asynchronous
    def get(self):
        t = tornado_subprocess.Subprocess( self.on_done, timeout=30, args=[ "cat", "/etc/passwd" ] )
        t.start()

素敵な機能をありがとう!明確にするために、なぜthreading.Thread新しい非ブロッキングプロセスの作成に使用できないのでしょうか。on_messageTornado websocketインスタンスでそれを使用したところ、問題なく動作しました。
VisioN

1
竜巻ではスレッディングはお勧めできません。小さくて実行時間が短い関数には問題ありません。あなたはそれについてここで読むことができます: stackoverflow.com/questions/7846323/tornado-web-and-threads github.com/facebook/tornado/wiki/Threading-and-concurrency
Vukasin Toroman

@VukasinToromanあなたは本当にこれでここに私を救った。tornado_subprocessモジュールに感謝します:)
James Gentes 2013年

これはWindowsで機能しますか?(select、ファイル記述子を使用した場合そうではないことに注意してください)
n611x007

このlibはselect呼び出しを使用しません。私はWindowsでこれを試していませんが、libがfcntlモジュールを使用しているため、おそらく問題が発生します。つまり、これはおそらくWindowsでは機能しません。
Vukasin Toroman 2013

6

既存のソリューションではうまくいきませんでした(詳細は以下)。最終的に機能したのは、read(1)を使用してreadlineを実装することでした(この回答に基づいて)。後者はブロックしません:

from subprocess import Popen, PIPE
from threading import Thread
def process_output(myprocess): #output-consuming thread
    nextline = None
    buf = ''
    while True:
        #--- extract line using read(1)
        out = myprocess.stdout.read(1)
        if out == '' and myprocess.poll() != None: break
        if out != '':
            buf += out
            if out == '\n':
                nextline = buf
                buf = ''
        if not nextline: continue
        line = nextline
        nextline = None

        #--- do whatever you want with line here
        print 'Line is:', line
    myprocess.stdout.close()

myprocess = Popen('myprogram.exe', stdout=PIPE) #output-producing process
p1 = Thread(target=process_output, args=(dcmpid,)) #output-consuming thread
p1.daemon = True
p1.start()

#--- do whatever here and then kill process and thread if needed
if myprocess.poll() == None: #kill process; will automatically stop thread
    myprocess.kill()
    myprocess.wait()
if p1 and p1.is_alive(): #wait for thread to finish
    p1.join()

既存のソリューションが機能しない理由:

  1. readline(Queueベースのものを含む)を必要とするソリューションは常にブロックします。readlineを実行するスレッドを強制終了することは困難(不可能?)です。それを作成したプロセスが終了したときだけ殺されますが、出力を生成するプロセスが殺されたときは殺されません。
  2. anonnnが指摘したように、低レベルのfcntlと高レベルのreadline呼び出しを混在させると正しく機能しない場合があります。
  3. select.poll()の使用は素晴らしいですが、PythonのドキュメントによるとWindowsでは動作しません。
  4. サードパーティのライブラリを使用すると、このタスクにはやり過ぎになり、依存関係が追加されます。

1
1. q.get_nowait()私の答えから、これをブロックすることはありません、それがそれを使用するポイントです。2. readline(enqueue_output()function)を実行するスレッドはEOFで終了します。たとえば、出力生成プロセスが強制終了された場合も含まれます。あなたがそうではないと信じるなら; 別の方法で(おそらく新しい質問として)示す最小限の完全なコード例を提供しください。
jfs 2013

1
@sebastian最小限の例を考え出すために1時間以上費やしました。結局、私はあなたの答えがすべてのケースを処理することに同意しなければなりません。出力生成プロセスを強制終了しようとしたときに、すでに強制終了されており、デバッグが困難なエラーが発生したため、以前はうまくいきませんでした。最小限の例を考えながら、より簡単な解決策を考え出すことができたので、時間はよく費やされました。
Vikram Pudi 2013

簡単な解決策も投稿していただけませんか?:)(それがセバスチャンのものと異なる場合)
n611x007

@ danger89:と思いますdcmpid = myprocess
ViFI 2016年

状態では、読み取り後の()(ちょうど真しばらく)を呼び出す:アウトあなたは、少なくとも文字列/ 1の長さでバイト読みするので、空の文字列になることはありませんでしょう
sergzach

6

これは、部分的な行を含む、サブプロセスASAPからのすべての出力をキャッチするために使用される私のコードです。それはほぼ同時に正しい順序でstdoutとstderrを同時にポンプします。

テスト済みで、Python 2.7 LinuxおよびWindowsで正しく動作しました。

#!/usr/bin/python
#
# Runner with stdout/stderr catcher
#
from sys import argv
from subprocess import Popen, PIPE
import os, io
from threading import Thread
import Queue
def __main__():
    if (len(argv) > 1) and (argv[-1] == "-sub-"):
        import time, sys
        print "Application runned!"
        time.sleep(2)
        print "Slept 2 second"
        time.sleep(1)
        print "Slept 1 additional second",
        time.sleep(2)
        sys.stderr.write("Stderr output after 5 seconds")
        print "Eol on stdin"
        sys.stderr.write("Eol on stderr\n")
        time.sleep(1)
        print "Wow, we have end of work!",
    else:
        os.environ["PYTHONUNBUFFERED"]="1"
        try:
            p = Popen( argv + ["-sub-"],
                       bufsize=0, # line-buffered
                       stdin=PIPE, stdout=PIPE, stderr=PIPE )
        except WindowsError, W:
            if W.winerror==193:
                p = Popen( argv + ["-sub-"],
                           shell=True, # Try to run via shell
                           bufsize=0, # line-buffered
                           stdin=PIPE, stdout=PIPE, stderr=PIPE )
            else:
                raise
        inp = Queue.Queue()
        sout = io.open(p.stdout.fileno(), 'rb', closefd=False)
        serr = io.open(p.stderr.fileno(), 'rb', closefd=False)
        def Pump(stream, category):
            queue = Queue.Queue()
            def rdr():
                while True:
                    buf = stream.read1(8192)
                    if len(buf)>0:
                        queue.put( buf )
                    else:
                        queue.put( None )
                        return
            def clct():
                active = True
                while active:
                    r = queue.get()
                    try:
                        while True:
                            r1 = queue.get(timeout=0.005)
                            if r1 is None:
                                active = False
                                break
                            else:
                                r += r1
                    except Queue.Empty:
                        pass
                    inp.put( (category, r) )
            for tgt in [rdr, clct]:
                th = Thread(target=tgt)
                th.setDaemon(True)
                th.start()
        Pump(sout, 'stdout')
        Pump(serr, 'stderr')

        while p.poll() is None:
            # App still working
            try:
                chan,line = inp.get(timeout = 1.0)
                if chan=='stdout':
                    print "STDOUT>>", line, "<?<"
                elif chan=='stderr':
                    print " ERROR==", line, "=?="
            except Queue.Empty:
                pass
        print "Finish"

if __name__ == '__main__':
    __main__()

必ずしも改行で終わらないものを読むことができる数少ない回答の1つ。
totaam 2014年

5

この問題を追加して、いくつかのsubprocess.Popen stdoutを読み取ります。これが私の非ブロッキング読み取りソリューションです:

import fcntl

def non_block_read(output):
    fd = output.fileno()
    fl = fcntl.fcntl(fd, fcntl.F_GETFL)
    fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
    try:
        return output.read()
    except:
        return ""

# Use example
from subprocess import *
sb = Popen("echo test && sleep 1000", shell=True, stdout=PIPE)
sb.kill()

# sb.stdout.read() # <-- This will block
non_block_read(sb.stdout)
'test\n'

5
docsによると、fcntlはWindowsでは機能しません。
n611x007 2013

@anatolytechtonik msvcrt.kbhit()代わりに使用

4

このバージョンのノンブロッキングリード特別なモジュールを必要とせず、ほとんどのLinuxディストリビューションでそのまま使用できます。

import os
import sys
import time
import fcntl
import subprocess

def async_read(fd):
    # set non-blocking flag while preserving old flags
    fl = fcntl.fcntl(fd, fcntl.F_GETFL)
    fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
    # read char until EOF hit
    while True:
        try:
            ch = os.read(fd.fileno(), 1)
            # EOF
            if not ch: break                                                                                                                                                              
            sys.stdout.write(ch)
        except OSError:
            # waiting for data be available on fd
            pass

def shell(args, async=True):
    # merge stderr and stdout
    proc = subprocess.Popen(args, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    if async: async_read(proc.stdout)
    sout, serr = proc.communicate()
    return (sout, serr)

if __name__ == '__main__':
    cmd = 'ping 8.8.8.8'
    sout, serr = shell(cmd.split())

3

以下は、スレッドに基づく簡単なソリューションです。

  • LinuxとWindowsの両方で動作します(に依存しませんselect)。
  • 両方stdoutstderr非同期に読み取ります。
  • 任意の待機時間でのアクティブなポーリングに依存しません(CPUフレンドリー)。
  • 使用しませんasyncio(他のライブラリと競合する可能性があります)。
  • 子プロセスが終了するまで実行されます。

printer.py

import time
import sys

sys.stdout.write("Hello\n")
sys.stdout.flush()
time.sleep(1)
sys.stdout.write("World!\n")
sys.stdout.flush()
time.sleep(1)
sys.stderr.write("That's an error\n")
sys.stderr.flush()
time.sleep(2)
sys.stdout.write("Actually, I'm fine\n")
sys.stdout.flush()
time.sleep(1)

reader.py

import queue
import subprocess
import sys
import threading


def enqueue_stream(stream, queue, type):
    for line in iter(stream.readline, b''):
        queue.put(str(type) + line.decode('utf-8'))
    stream.close()


def enqueue_process(process, queue):
    process.wait()
    queue.put('x')


p = subprocess.Popen('python printer.py', stdout=subprocess.PIPE, stderr=subprocess.PIPE)
q = queue.Queue()
to = threading.Thread(target=enqueue_stream, args=(p.stdout, q, 1))
te = threading.Thread(target=enqueue_stream, args=(p.stderr, q, 2))
tp = threading.Thread(target=enqueue_process, args=(p, q))
te.start()
to.start()
tp.start()

while True:
    line = q.get()
    if line[0] == 'x':
        break
    if line[0] == '2':  # stderr
        sys.stdout.write("\033[0;31m")  # ANSI red color
    sys.stdout.write(line[1:])
    if line[0] == '2':
        sys.stdout.write("\033[0m")  # reset ANSI code
    sys.stdout.flush()

tp.join()
to.join()
te.join()

2

WindowsとUnixで非ブロッキングパイプを設定する機能を提供するため、ここにこの回答を追加します。

ctypes詳細はすべて@techtonikの回答のおかげです。

UnixシステムとWindowsシステムの両方で使用するために若干変更されたバージョンがあります。

  • Python3互換(わずかな変更のみが必要)
  • posixバージョンを含み、どちらかに使用する例外を定義します。

これにより、UnixとWindowsのコードで同じ関数と例外を使用できます。

# pipe_non_blocking.py (module)
"""
Example use:

    p = subprocess.Popen(
            command,
            stdout=subprocess.PIPE,
            )

    pipe_non_blocking_set(p.stdout.fileno())

    try:
        data = os.read(p.stdout.fileno(), 1)
    except PortableBlockingIOError as ex:
        if not pipe_non_blocking_is_error_blocking(ex):
            raise ex
"""


__all__ = (
    "pipe_non_blocking_set",
    "pipe_non_blocking_is_error_blocking",
    "PortableBlockingIOError",
    )

import os


if os.name == "nt":
    def pipe_non_blocking_set(fd):
        # Constant could define globally but avoid polluting the name-space
        # thanks to: /programming/34504970
        import msvcrt

        from ctypes import windll, byref, wintypes, WinError, POINTER
        from ctypes.wintypes import HANDLE, DWORD, BOOL

        LPDWORD = POINTER(DWORD)

        PIPE_NOWAIT = wintypes.DWORD(0x00000001)

        def pipe_no_wait(pipefd):
            SetNamedPipeHandleState = windll.kernel32.SetNamedPipeHandleState
            SetNamedPipeHandleState.argtypes = [HANDLE, LPDWORD, LPDWORD, LPDWORD]
            SetNamedPipeHandleState.restype = BOOL

            h = msvcrt.get_osfhandle(pipefd)

            res = windll.kernel32.SetNamedPipeHandleState(h, byref(PIPE_NOWAIT), None, None)
            if res == 0:
                print(WinError())
                return False
            return True

        return pipe_no_wait(fd)

    def pipe_non_blocking_is_error_blocking(ex):
        if not isinstance(ex, PortableBlockingIOError):
            return False
        from ctypes import GetLastError
        ERROR_NO_DATA = 232

        return (GetLastError() == ERROR_NO_DATA)

    PortableBlockingIOError = OSError
else:
    def pipe_non_blocking_set(fd):
        import fcntl
        fl = fcntl.fcntl(fd, fcntl.F_GETFL)
        fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
        return True

    def pipe_non_blocking_is_error_blocking(ex):
        if not isinstance(ex, PortableBlockingIOError):
            return False
        return True

    PortableBlockingIOError = BlockingIOError

不完全なデータの読み取りを回避するために、私は自分のreadlineジェネレータ(各行のバイト文字列を返す)を作成しました。

そのジェネレーターなので、たとえば...

def non_blocking_readlines(f, chunk=1024):
    """
    Iterate over lines, yielding b'' when nothings left
    or when new data is not yet available.

    stdout_iter = iter(non_blocking_readlines(process.stdout))

    line = next(stdout_iter)  # will be a line or b''.
    """
    import os

    from .pipe_non_blocking import (
            pipe_non_blocking_set,
            pipe_non_blocking_is_error_blocking,
            PortableBlockingIOError,
            )

    fd = f.fileno()
    pipe_non_blocking_set(fd)

    blocks = []

    while True:
        try:
            data = os.read(fd, chunk)
            if not data:
                # case were reading finishes with no trailing newline
                yield b''.join(blocks)
                blocks.clear()
        except PortableBlockingIOError as ex:
            if not pipe_non_blocking_is_error_blocking(ex):
                raise ex

            yield b''
            continue

        while True:
            n = data.find(b'\n')
            if n == -1:
                break

            yield b''.join(blocks) + data[:n + 1]
            data = data[n + 1:]
            blocks.clear()
        blocks.append(data)

(1)このコメントは、Python 2のreadline()非ブロックパイプ(を使用して設定するなどfcntl)では機能しないことを示しています-もはや正しくないと思いますか?(私の回答にはfcntl同じ情報を提供するリンク()が含まれていますが、現在は削除されているようです)。(2)multiprocessing.connection.Pipe使用方法を見るSetNamedPipeHandleState
jfs

これはPython3でのみテストしました。しかし、この情報も見て、それが有効なままであることを期待しています。また、readlineの代わりに使用する独自のコードを作成し、それを含めるように回答を更新しました。
ideasman42 2016年

2

元の質問者の問題がありますが、スレッドを呼び出したくありませんでした。Jesseのソリューションとパイプからの直接read()、および行の読み取り用の独自のバッファーハンドラーを混合しました(ただし、私のサブプロセス-ping-常に完全な行<システムページサイズを書き込みました)。gobjectに登録されたioウォッチを読み取るだけで、ビジー待機を回避します。最近私は通常、スレッドを回避するためにgobject MainLoop内でコードを実行します。

def set_up_ping(ip, w):
# run the sub-process
# watch the resultant pipe
p = subprocess.Popen(['/bin/ping', ip], stdout=subprocess.PIPE)
# make stdout a non-blocking file
fl = fcntl.fcntl(p.stdout, fcntl.F_GETFL)
fcntl.fcntl(p.stdout, fcntl.F_SETFL, fl | os.O_NONBLOCK)
stdout_gid = gobject.io_add_watch(p.stdout, gobject.IO_IN, w)
return stdout_gid # for shutting down

ウォッチャーは

def watch(f, *other):
print 'reading',f.read()
return True

そして、メインプログラムはpingを設定してから、gobjectメールループを呼び出します。

def main():
set_up_ping('192.168.1.8', watch)
# discard gid as unused here
gobject.MainLoop().run()

その他の作業はgobjectのコールバックにアタッチされます。


2

最近のPythonの方がはるかに優れています。

次に、単純な子プログラム「hello.py」を示します。

#!/usr/bin/env python3

while True:
    i = input()
    if i == "quit":
        break
    print(f"hello {i}")

そしてそれと対話するためのプログラム:

import asyncio


async def main():
    proc = await asyncio.subprocess.create_subprocess_exec(
        "./hello.py", stdin=asyncio.subprocess.PIPE, stdout=asyncio.subprocess.PIPE
    )
    proc.stdin.write(b"bob\n")
    print(await proc.stdout.read(1024))
    proc.stdin.write(b"alice\n")
    print(await proc.stdout.read(1024))
    proc.stdin.write(b"quit\n")
    await proc.wait()


asyncio.run(main())

それは出力します:

b'hello bob\n'
b'hello alice\n'

ここでも関連する質問でも、以前のほとんどすべての回答による実際のパターンは、子のstdoutファイル記述子を非ブロッキングに設定し、何らかの選択ループでポーリングすることに注意してください。最近ではもちろん、そのループはasyncioによって提供されます。


1

選択モジュールは、次の便利な入力がどこにあるかを判断するのに役立ちます。

ただし、ほとんどの場合、個別のスレッドを使用するほうが幸せです。1つはstdinをブロックして読み取り、もう1つはブロックしたくない場所に読み取ります。


11
この回答は2つの理由で役に立たないと思います:(a)selectモジュールは、Windowsのパイプでは機能しません(提供されたリンクに明確に示されているように)。これは、ポータブルソリューションを持つというOPの意図に反します。(b)非同期スレッドは、親プロセスと子プロセスの間の同期ダイアログを許可しません。親プロセスが、子から読み取った次の行に従って次のアクションをディスパッチしたい場合はどうなりますか?!
ThomasH 2009

4
また、selectは、標準のCセマンティクスがなく、部分的なデータを返さないため、Pythonの読み取りがselectの後でさえブロックされるという点でも役に立ちません。
Helmut Grohne、2011年

子供の出力から読み取るための別のしきい値によって、これと同様の問題が解決されました。同期的な相互作用が必要な場合は、このソリューションを使用できないと思います(期待される出力がわからない場合)。私はこの答えを受け入れたでしょう
エミリアーノ

1

なぜスレッドとキューが面倒なのですか?readline()とは異なり、BufferedReader.read1()は\ r \ nの待機をブロックせず、出力が来た場合にASAPを返します。

#!/usr/bin/python
from subprocess import Popen, PIPE, STDOUT
import io

def __main__():
    try:
        p = Popen( ["ping", "-n", "3", "127.0.0.1"], stdin=PIPE, stdout=PIPE, stderr=STDOUT )
    except: print("Popen failed"); quit()
    sout = io.open(p.stdout.fileno(), 'rb', closefd=False)
    while True:
        buf = sout.read1(1024)
        if len(buf) == 0: break
        print buf,

if __name__ == '__main__':
    __main__()

入ってくるものが何もない場合、それはできるだけ早く戻りますか?そうでない場合は、ブロッキングです。
MathieuPagé2015年

@MathieuPagéは正しいです。read1最初の基礎となる読み取りがブロックされるとブロックされます。これは、パイプがまだ開いているが入力が利用できない場合に発生します。
Jack O'Connor

1

私の場合、バックグラウンドアプリケーションからの出力をキャッチし、それを増強する(タイムスタンプ、色などを追加する)ロギングモジュールが必要でした。

最終的に、実際のI / Oを行うバックグラウンドスレッドができました。次のコードはPOSIXプラットフォーム専用です。重要でない部分は取り除いた。

誰かがこのビーストを長期間使用する場合は、オープン記述子の管理を検討してください。私の場合、それは大きな問題ではありませんでした。

# -*- python -*-
import fcntl
import threading
import sys, os, errno
import subprocess

class Logger(threading.Thread):
    def __init__(self, *modules):
        threading.Thread.__init__(self)
        try:
            from select import epoll, EPOLLIN
            self.__poll = epoll()
            self.__evt = EPOLLIN
            self.__to = -1
        except:
            from select import poll, POLLIN
            print 'epoll is not available'
            self.__poll = poll()
            self.__evt = POLLIN
            self.__to = 100
        self.__fds = {}
        self.daemon = True
        self.start()

    def run(self):
        while True:
            events = self.__poll.poll(self.__to)
            for fd, ev in events:
                if (ev&self.__evt) != self.__evt:
                    continue
                try:
                    self.__fds[fd].run()
                except Exception, e:
                    print e

    def add(self, fd, log):
        assert not self.__fds.has_key(fd)
        self.__fds[fd] = log
        self.__poll.register(fd, self.__evt)

class log:
    logger = Logger()

    def __init__(self, name):
        self.__name = name
        self.__piped = False

    def fileno(self):
        if self.__piped:
            return self.write
        self.read, self.write = os.pipe()
        fl = fcntl.fcntl(self.read, fcntl.F_GETFL)
        fcntl.fcntl(self.read, fcntl.F_SETFL, fl | os.O_NONBLOCK)
        self.fdRead = os.fdopen(self.read)
        self.logger.add(self.read, self)
        self.__piped = True
        return self.write

    def __run(self, line):
        self.chat(line, nl=False)

    def run(self):
        while True:
            try: line = self.fdRead.readline()
            except IOError, exc:
                if exc.errno == errno.EAGAIN:
                    return
                raise
            self.__run(line)

    def chat(self, line, nl=True):
        if nl: nl = '\n'
        else: nl = ''
        sys.stdout.write('[%s] %s%s' % (self.__name, line, nl))

def system(command, param=[], cwd=None, env=None, input=None, output=None):
    args = [command] + param
    p = subprocess.Popen(args, cwd=cwd, stdout=output, stderr=output, stdin=input, env=env, bufsize=0)
    p.wait()

ls = log('ls')
ls.chat('go')
system("ls", ['-l', '/'], output=ls)

date = log('date')
date.chat('go')
system("date", output=date)

1

実行中のプロセスからstdoutとstderrの両方を収集したいので、私の問題は少し異なりますが、ウィジェットの出力を生成されたものとしてレンダリングしたかったので、最終的には同じです。

別のスクリプトを実行してその出力を収集するなどの一般的なタスクを実行する必要がないため、キューまたは追加のスレッドを使用して提案された回避策の多くに頼りたくありませんでした。

提案されたソリューションとpythonのドキュメントを読んだ後、以下の実装で問題を解決しました。はい、select関数呼び出しを使用しているため、POSIXでのみ機能します。

ドキュメントがわかりにくく、実装がそのような一般的なスクリプト作成タスクにとって扱いにくいことに同意します。古いバージョンのpythonのデフォルトPopenと説明は異なるため、多くの混乱が生じたと思います。これはPython 2.7.12と3.5.2の両方でうまく機能するようです。

重要なのはbufsize=1、ラインバッファリングを設定してuniversal_newlines=Trueから、設定時にデフォルトになりそうなバイナリではなく、テキストファイルとして処理することでしたbufsize=1

class workerThread(QThread):
   def __init__(self, cmd):
      QThread.__init__(self)
      self.cmd = cmd
      self.result = None           ## return code
      self.error = None            ## flag indicates an error
      self.errorstr = ""           ## info message about the error

   def __del__(self):
      self.wait()
      DEBUG("Thread removed")

   def run(self):
      cmd_list = self.cmd.split(" ")   
      try:
         cmd = subprocess.Popen(cmd_list, bufsize=1, stdin=None
                                        , universal_newlines=True
                                        , stderr=subprocess.PIPE
                                        , stdout=subprocess.PIPE)
      except OSError:
         self.error = 1
         self.errorstr = "Failed to execute " + self.cmd
         ERROR(self.errorstr)
      finally:
         VERBOSE("task started...")
      import select
      while True:
         try:
            r,w,x = select.select([cmd.stdout, cmd.stderr],[],[])
            if cmd.stderr in r:
               line = cmd.stderr.readline()
               if line != "":
                  line = line.strip()
                  self.emit(SIGNAL("update_error(QString)"), line)
            if cmd.stdout in r:
               line = cmd.stdout.readline()
               if line == "":
                  break
               line = line.strip()
               self.emit(SIGNAL("update_output(QString)"), line)
         except IOError:
            pass
      cmd.wait()
      self.result = cmd.returncode
      if self.result < 0:
         self.error = 1
         self.errorstr = "Task terminated by signal " + str(self.result)
         ERROR(self.errorstr)
         return
      if self.result:
         self.error = 1
         self.errorstr = "exit code " + str(self.result)
         ERROR(self.errorstr)
         return
      return

ERROR、DEBUG、およびVERBOSEは、出力を端末に出力する単なるマクロです。

このソリューションはブロックreadline機能を使用しているため、IMHO 99.99%の効果があります。サブプロセスが適切で、完全なラインを出力すると想定します。

私はまだPythonが初めてなので、ソリューションを改善するためのフィードバックを歓迎します。


この特定のケースでは、Popenコンストラクターでstderr = subprocess.STDOUTを設定し、cmd.stdout.readline()からすべての出力を取得できます。
アーロン

わかりやすい例。select.select()に問題がありましたが、これで解決しました。
maharvey67


0

JF Sebastianの回答と他のいくつかの情報源から、シンプルなサブプロセスマネージャーをまとめました。リクエストのノンブロッキング読み取りを提供し、同時にいくつかのプロセスを実行します。(私が知っている)OS固有の呼び出しを使用しないため、どこでも機能するはずです。

pypiから入手できるので、だけpip install shelljobです。例と完全なドキュメントについては、プロジェクトページを参照してください。


0

編集:この実装はまだブロックされています。代わりにJFSebastianの回答を使用してください。

一番上の答えを試しましたが、スレッドコードの追加のリスクとメンテナンスが気になりました。

IOモジュール(および2.6に限定される)、私はBufferedReaderのを見つけました。これは私のスレッドレス、非ブロッキングソリューションです。

import io
from subprocess import PIPE, Popen

p = Popen(['myprogram.exe'], stdout=PIPE)

SLEEP_DELAY = 0.001

# Create an io.BufferedReader on the file descriptor for stdout
with io.open(p.stdout.fileno(), 'rb', closefd=False) as buffer:
  while p.poll() == None:
      time.sleep(SLEEP_DELAY)
      while '\n' in bufferedStdout.peek(bufferedStdout.buffer_size):
          line = buffer.readline()
          # do stuff with the line

  # Handle any remaining output after the process has ended
  while buffer.peek():
    line = buffer.readline()
    # do stuff with the line

試しましたfor line in iter(p.stdout.readline, ""): # do stuff with the lineか?これはスレッドレス(シングルスレッド)であり、コードがブロックされるとブロックされます。
jfs 2013年

@ jf-sebastianええ、私は最終的にあなたの答えに戻りました。私の実装はまだ時々ブロックされています。回答を編集して、このルートを下らないように他の人に警告します。
romc 2013年

0

私は最近、ノンブロッキングモードでストリームから1行ずつ読み取る必要がある同じ問題に遭遇しました(サブプロセスでテールラン)次の問題を避けたかった:CPUを焼き付けない、1バイトずつストリームを読み取らない( readlineのように)など

ここに私の実装があり ますhttps://gist.github.com/grubberr/5501e1a9760c3eab5e0a それはwindowsをサポートしていない(poll)、EOFを処理しませんが、私にとってはうまくいきます


スレッドベースの答えがないではない CPUを焼く(あなたが任意に指定することができますtimeoutあなたのソリューションのように)と.readline()読み込むより時よりも1バイト(bufsize=1手段がライン書き込みにのみ関連(-buffered))。他にどんな問題が見つかりましたか?リンクのみの回答はあまり役に立ちません。
jfs

0

これはサブプロセスで対話型コマンドを実行する例であり、stdoutは疑似端末を使用して対話型です。あなたは参照することができます:https : //stackoverflow.com/a/43012138/3555925

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import sys
import select
import termios
import tty
import pty
from subprocess import Popen

command = 'bash'
# command = 'docker run -it --rm centos /bin/bash'.split()

# save original tty setting then set it to raw mode
old_tty = termios.tcgetattr(sys.stdin)
tty.setraw(sys.stdin.fileno())

# open pseudo-terminal to interact with subprocess
master_fd, slave_fd = pty.openpty()

# use os.setsid() make it run in a new process group, or bash job control will not be enabled
p = Popen(command,
          preexec_fn=os.setsid,
          stdin=slave_fd,
          stdout=slave_fd,
          stderr=slave_fd,
          universal_newlines=True)

while p.poll() is None:
    r, w, e = select.select([sys.stdin, master_fd], [], [])
    if sys.stdin in r:
        d = os.read(sys.stdin.fileno(), 10240)
        os.write(master_fd, d)
    elif master_fd in r:
        o = os.read(master_fd, 10240)
        if o:
            os.write(sys.stdout.fileno(), o)

# restore tty settings back
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty)

0

このソリューションでは、selectモジュールを使用してIOストリームから「使用可能なデータを読み取り」ます。この関数は、データが利用可能になるまで最初はブロックしますが、その後は利用可能でそれ以上ブロックしないデータのみを読み取ります。

selectモジュールを使用しているため、これはUnixでのみ機能します。

コードは完全にPEP8に準拠しています。

import select


def read_available(input_stream, max_bytes=None):
    """
    Blocks until any data is available, then all available data is then read and returned.
    This function returns an empty string when end of stream is reached.

    Args:
        input_stream: The stream to read from.
        max_bytes (int|None): The maximum number of bytes to read. This function may return fewer bytes than this.

    Returns:
        str
    """
    # Prepare local variables
    input_streams = [input_stream]
    empty_list = []
    read_buffer = ""

    # Initially block for input using 'select'
    if len(select.select(input_streams, empty_list, empty_list)[0]) > 0:

        # Poll read-readiness using 'select'
        def select_func():
            return len(select.select(input_streams, empty_list, empty_list, 0)[0]) > 0

        # Create while function based on parameters
        if max_bytes is not None:
            def while_func():
                return (len(read_buffer) < max_bytes) and select_func()
        else:
            while_func = select_func

        while True:
            # Read single byte at a time
            read_data = input_stream.read(1)
            if len(read_data) == 0:
                # End of stream
                break
            # Append byte to string buffer
            read_buffer += read_data
            # Check if more data is available
            if not while_func():
                break

    # Return read buffer
    return read_buffer

0

私はJesseによって説明された問題にも直面し、BradleyAndyなどがそうであるように "select"を使用して解決しましたが、ビジーループを回避するためにブロッキングモードでした。ダミーのパイプを偽のstdinとして使用します。selectブロックは、標準入力またはパイプの準備ができるまで待機します。キーが押されると、stdinは選択のブロックを解除し、キーの値はread(1)で取得できます。別のスレッドがパイプに書き込むと、パイプは選択のブロックを解除し、stdinの必要性がなくなったことを示していると見なすことができます。ここにいくつかの参照コードがあります:

import sys
import os
from select import select

# -------------------------------------------------------------------------    
# Set the pipe (fake stdin) to simulate a final key stroke
# which will unblock the select statement
readEnd, writeEnd = os.pipe()
readFile = os.fdopen(readEnd)
writeFile = os.fdopen(writeEnd, "w")

# -------------------------------------------------------------------------
def getKey():

    # Wait for stdin or pipe (fake stdin) to be ready
    dr,dw,de = select([sys.__stdin__, readFile], [], [])

    # If stdin is the one ready then read it and return value
    if sys.__stdin__ in dr:
        return sys.__stdin__.read(1)   # For Windows use ----> getch() from module msvcrt

    # Must finish
    else:
        return None

# -------------------------------------------------------------------------
def breakStdinRead():
    writeFile.write(' ')
    writeFile.flush()

# -------------------------------------------------------------------------
# MAIN CODE

# Get key stroke
key = getKey()

# Keyboard input
if key:
    # ... do your stuff with the key value

# Faked keystroke
else:
    # ... use of stdin finished

# -------------------------------------------------------------------------
# OTHER THREAD CODE

breakStdinRead()

注:Windowsでこれを機能させるには、パイプをソケットに置き換える必要があります。私はまだ試していませんが、ドキュメントに従って動作するはずです。
gonzaedu61

0

Windowsのpexpectに代わるwexpectを試してください。

import wexpect

p = wexpect.spawn('myprogram.exe')
p.stdout.readline('.')               // regex pattern of any character
output_str = p.after()

0

UnixライクなシステムとPython 3.5以降os.set_blockingでは、まさにそのとおりです。

import os
import time
import subprocess

cmd = 'python3', '-c', 'import time; [(print(i), time.sleep(1)) for i in range(5)]'
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
os.set_blocking(p.stdout.fileno(), False)
start = time.time()
while True:
    # first iteration always produces empty byte string in non-blocking mode
    for i in range(2):    
        line = p.stdout.readline()
        print(i, line)
        time.sleep(0.5)
    if time.time() > start + 5:
        break
p.terminate()

これは出力します:

1 b''
2 b'0\n'
1 b''
2 b'1\n'
1 b''
2 b'2\n'
1 b''
2 b'3\n'
1 b''
2 b'4\n'

os.set_blockingコメントし、それは次のとおりです。

0 b'0\n'
1 b'1\n'
0 b'2\n'
1 b'3\n'
0 b'4\n'
1 b''

-2

Pythonで非ブロッキング読み取りとバックグラウンド書き込みをサポートするモジュールは次のとおりです。

https://pypi.python.org/pypi/python-nonblock

機能を提供し、

使用可能な場合はストリームからデータを読み取るnonblock_read。それ以外の場合は空の文字列を返します(ストリームが反対側で閉じられ、すべての可能なデータが読み取られている場合はNoneを返します)。

python-subprocess2モジュールを検討することもできます。

https://pypi.python.org/pypi/python-subprocess2

これはサブプロセスモジュールに追加されます。したがって、「subprocess.Popen」から返されたオブジェクトに、追加のメソッドrunInBackgroundが追加されます。これはスレッドを開始し、メインスレッドをブロックすることなく、stdout / stderrにデータが書き込まれるときに自動的に入力されるオブジェクトを返します。

楽しい!


この非ブロックモジュールを試してみたいのですが、Linuxの手順のいくつかは比較的新しいものです。これらのルーチンを正確にインストールするにはどうすればよいですか?私はRaspberry Pi用のDebian Linuxの一種であるRaspbian Jessieを実行しています。「sudo apt-get install nonblock」とpython-nonblockを試してみましたが、どちらもエラーが発生しました-見つかりません。このサイトpypi.python.org/pypi/python-nonblockからzipファイルをダウンロードしましたが、どうすればよいかわかりません。ありがとう.... RDK
RDK
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.