Pythonから外部コマンドを非同期的に実行するにはどうすればよいですか?
-
10-07-2019 - |
質問
Pythonスクリプトから非同期にシェルコマンドを実行する必要があります。これは、外部コマンドがオフになり、必要なことを実行している間、Pythonスクリプトを実行し続けることを意味します。
この投稿を読みました:
その後、立ち去っていくつかのテストを行ったところ、 os.system()
は、最後に&
を使用していれば、仕事をするように見えますコマンドが返されるまで待つ必要はありません。私が疑問に思っているのは、これがそのようなことを達成する適切な方法であるかどうかです。 commands.call()
を試しましたが、外部コマンドでブロックされるため、動作しません。
このために os.system()
を使用することをお勧めするか、他のルートを試す必要があるかどうかを教えてください。
解決
subprocess.Popen はまさにあなたが望むことをします。
from subprocess import Popen
p = Popen(['watch', 'ls']) # something long running
# ... do other stuff while subprocess is running
p.terminate()
(コメントから回答を完成させるために編集)
Popenインスタンスは、 poll()
を実行して、まだ実行されているかどうかを確認します。 communicate()
を使用してstdinにデータを送信し、終了するのを待ちます。
他のヒント
多数のプロセスを並行して実行し、結果が得られたときにそれらを処理する場合は、次のようなポーリングを使用できます。
from subprocess import Popen, PIPE
import time
running_procs = [
Popen(['/usr/bin/my_cmd', '-i %s' % path], stdout=PIPE, stderr=PIPE)
for path in '/tmp/file0 /tmp/file1 /tmp/file2'.split()]
while running_procs:
for proc in running_procs:
retcode = proc.poll()
if retcode is not None: # Process finished.
running_procs.remove(proc)
break
else: # No process is done, wait a bit and check again.
time.sleep(.1)
continue
# Here, `proc` has finished with return code `retcode`
if retcode != 0:
"""Error handling."""
handle_results(proc.stdout)
そこにある制御フローは、私がそれを小さくしようとしているので、少し複雑になっています-あなたはあなたの好みにリファクタリングできます。 :-)
これには、早期終了リクエストを最初に処理するという利点があります。最初に実行中のプロセスで communicate
を呼び出して、それが最も長く実行されることが判明した場合、実行中のプロセスは、結果を処理できていたときにアイドル状態のままでした。
私が疑問に思っているのは、この[os.system()]がそのようなことを達成する適切な方法かどうかです。
いいえ。 os.system()
は適切な方法ではありません。だからこそ、誰もが subprocess
を使用するように言っています。
詳細については、 http://docs.python.org/libraryをご覧ください。 /os.html#os.system
サブプロセスモジュールはさらに多くを提供します 産卵のための強力な施設 プロセスとその取得 結果;そのモジュールの使用は この関数を使用するよりも望ましい。つかいます サブプロセスモジュール。チェック 特に古いものを置き換える サブプロセスモジュールの機能 セクション。
asyncproc モジュールで成功しました、プロセスからの出力をうまく処理します。例:
import os
from asynproc import Process
myProc = Process("myprogram.app")
while True:
# check to see if process has ended
poll = myProc.wait(os.WNOHANG)
if poll is not None:
break
# print any new output
out = myProc.read()
if out != "":
print out
pexpect [ http://www.noah.org/wiki/Pexpect ]を使用してこれを行うもう1つの方法は、ノンブロッキングリードラインです。 Pexpectはデッドロックの問題を解決し、プロセスをバックグラウンドで簡単に実行できるようにします。また、プロセスが定義済みの文字列を吐き出すときにコールバックを行う簡単な方法を提供し、通常、プロセスとの対話をはるかに容易にします。
Pythonのs3270スクリプトソフトウェアを使用して3270端末に接続しようとすると、同じ問題が発生します。ここで見つけたProcessのサブクラスで問題を解決しています:
http://code.activestate.com/recipes/440554/
そして、これはファイルから取られたサンプルです:
def recv_some(p, t=.1, e=1, tr=5, stderr=0):
if tr < 1:
tr = 1
x = time.time()+t
y = []
r = ''
pr = p.recv
if stderr:
pr = p.recv_err
while time.time() < x or r:
r = pr()
if r is None:
if e:
raise Exception(message)
else:
break
elif r:
y.append(r)
else:
time.sleep(max((x-time.time())/tr, 0))
return ''.join(y)
def send_all(p, data):
while len(data):
sent = p.send(data)
if sent is None:
raise Exception(message)
data = buffer(data, sent)
if __name__ == '__main__':
if sys.platform == 'win32':
shell, commands, tail = ('cmd', ('dir /w', 'echo HELLO WORLD'), '\r\n')
else:
shell, commands, tail = ('sh', ('ls', 'echo HELLO WORLD'), '\n')
a = Popen(shell, stdin=PIPE, stdout=PIPE)
print recv_some(a),
for cmd in commands:
send_all(a, cmd + tail)
print recv_some(a),
send_all(a, 'exit' + tail)
print recv_some(a, e=0)
a.wait()
「戻るまで待つ必要はありません」と考えると、最も簡単な解決策の1つは次のとおりです。
subprocess.Popen( \
[path_to_executable, arg1, arg2, ... argN],
creationflags = subprocess.CREATE_NEW_CONSOLE,
).pid
しかし...私が読んだことから、これは「そのようなことを達成する適切な方法」ではありません。 subprocess.CREATE_NEW_CONSOLE
フラグによって作成されたセキュリティリスクのため。
ここで発生する重要なことは、 subprocess.CREATE_NEW_CONSOLE
を使用して新しいコンソールと .pid
を作成することです(プロセスIDを返すので、後でプログラムをチェックできますプログラムがそのジョブを終了するのを待たないようにしたい)