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()
=標準入力でデータを送信し、終了するまで待機します。
多くのプロセスを並行して実行し、結果が得られたときにそれらを処理する場合は、次のようなポーリングを使用できます。
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 を参照してください。
Subprocessモジュールは、新しいプロセスを生成してその結果を取得するためのより強力な機能を提供します。このモジュールを使用することは、この関数を使用するよりも望ましいです。サブプロセスモジュールを使用します。特に、古い関数のサブプロセスモジュールセクションへの置き換えを確認してください。
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 ]をノンブロッキングリードラインで使用することも、これを行う別の方法です。 Pexpectはデッドロックの問題を解決し、バックグラウンドでプロセスを簡単に実行できるようにします。また、プロセスが事前定義された文字列を吐き出すときにコールバックを簡単に行うことができ、一般にプロセスとのやり取りがはるかに容易になります。
「戻ってくるのを待つ必要はありません」と考えると、最も簡単な解決策の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を返します)、プログラムがジョブを完了するのを待たないようにします。
Pythonのs3270スクリプトソフトウェアを使用して3270ターミナルに接続しようとすると、同じ問題が発生します。今、私はここで見つけたプロセスのサブクラスで問題を解決しています:
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 __== '__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()
受け入れられる答えはvery古いです。
私はここでより良い現代的な答えを見つけました:
https://kevinmccarthy.org/2016/07/25/streaming-subprocess-stdin-and-stdout-with-asyncio-in-python/
そしていくつかの変更を行いました:
import sys
import asyncio
if sys.platform == "win32":
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
async def _read_stream(stream, cb):
while True:
line = await stream.readline()
if line:
cb(line)
else:
break
async def _stream_subprocess(cmd, stdout_cb, stderr_cb):
try:
process = await asyncio.create_subprocess_exec(
*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
)
await asyncio.wait(
[
_read_stream(process.stdout, stdout_cb),
_read_stream(process.stderr, stderr_cb),
]
)
rc = await process.wait()
return process.pid, rc
except OSError as e:
# the program will hang if we let any exception propagate
return e
def execute(*aws):
""" run the given coroutines in an asyncio loop
returns a list containing the values returned from each coroutine.
"""
loop = asyncio.get_event_loop()
rc = loop.run_until_complete(asyncio.gather(*aws))
loop.close()
return rc
def printer(label):
def pr(*args, **kw):
print(label, *args, **kw)
return pr
def name_it(start=0, template="s{}"):
"""a simple generator for task names
"""
while True:
yield template.format(start)
start += 1
def runners(cmds):
"""
cmds is a list of commands to excecute as subprocesses
each item is a list appropriate for use by subprocess.call
"""
next_name = name_it().__next__
for cmd in cmds:
name = next_name()
out = printer(f"{name}.stdout")
err = printer(f"{name}.stderr")
yield _stream_subprocess(cmd, out, err)
if __== "__main__":
cmds = (
[
"sh",
"-c",
"""echo "$Shell"-stdout && sleep 1 && echo stderr 1>&2 && sleep 1 && echo done""",
],
[
"bash",
"-c",
"echo 'hello, Dave.' && sleep 1 && echo dave_err 1>&2 && sleep 1 && echo done",
],
[sys.executable, "-c", 'print("hello from python");import sys;sys.exit(2)'],
)
print(execute(*runners(cmds)))
サンプルのコマンドがシステム上で完全に機能することはまずありませんし、奇妙なエラーも処理しませんが、このコードはasyncioを使用して複数のサブプロセスを実行し、出力をストリームする1つの方法を示しています。