149

Python スクリプトから非同期でシェル コマンドを実行する必要があります。これは、外部コマンドが停止し、必要なことは何でも実行している間も、Python スクリプトを実行し続けたいということです。

私はこの投稿を読みました:

Python で外部コマンドを呼び出す

その後、私は出発していくつかのテストをos.system()行いましたが、コマンドの最後に使用することを条件&に、コマンドが戻るのを待つ必要がないように機能するようです。私が疑問に思っているのは、これがそのようなことを達成するための適切な方法であるかどうかということです? 試してみcommands.call()ましたが、外部コマンドでブロックされるため、うまくいきません。

os.system()これを使用するのが賢明かどうか、または他のルートを試す必要があるかどうかを教えてください。

4

10 に答える 10

159

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()したり、標準入力でデータを送信したり、終了するのを待ったりするなど、他にもさまざまなことを行うことができます。

于 2009-03-11T22:05:32.613 に答える
58

多くのプロセスを並行して実行し、結果が得られたときにそれらを処理する場合は、次のようにポーリングを使用できます。

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、それが最も長く実行されていることが判明した場合、他の実行中のプロセスは、結果を処理できたときにアイドル状態のままでした。

于 2009-03-11T22:15:50.373 に答える
21

これは、「コマンドが非同期的に終了するのを待つ」の下のPython 3 サブプロセスの例で説明されています。IPythonまたはを使用してこのコードを実行しますpython -m asyncio

import asyncio

proc = await asyncio.create_subprocess_exec(
   'ls','-lha',
   stdout=asyncio.subprocess.PIPE,
   stderr=asyncio.subprocess.PIPE)

# do something else while ls is working

# if proc takes very long to complete, the CPUs are free to use cycles for 
# other processes
stdout, stderr = await proc.communicate()

プロセスは、await asyncio.create_subprocess_exec(...)が完了するとすぐに実行を開始します。を呼び出すawait proc.communicate()までに終了していない場合は、出力ステータスを提供するためにそこで待機します。それが終わったら、proc.communicate()すぐに戻ります。

ここでの要点はTerrels の回答に似ていますが、Terrel の回答は複雑すぎるように見えると思います。

詳細については、を参照asyncio.create_subprocess_execしてください。

于 2020-04-16T15:52:47.670 に答える
14

私が疑問に思っているのは、この [os.system()] がそのようなことを達成する適切な方法であるかどうかです。

いいえ os.system()、適切な方法ではありません。だから誰もが を使えと言いますsubprocess

詳細については、http://docs.python.org/library/os.html#os.systemを参照してください。

subprocess モジュールは、新しいプロセスを生成し、その結果を取得するためのより強力な機能を提供します。この関数を使用するよりも、そのモジュールを使用することをお勧めします。subprocess モジュールを使用します。特に古い機能をサブプロセスモジュールに置き換えるセクションを確認してください。

于 2009-03-11T22:24:32.567 に答える
8

私は、プロセスからの出力を適切に処理する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
于 2009-03-11T23:04:44.390 に答える
7

ノンブロッキング readlines でpexpectを使用することも、これを行う別の方法です。Pexpect はデッドロックの問題を解決し、プロセスをバックグラウンドで簡単に実行できるようにし、プロセスが事前定義された文字列を吐き出すときにコールバックを行う簡単な方法を提供し、一般的にプロセスとのやり取りをはるかに簡単にします。

于 2010-07-06T14:30:28.793 に答える
3

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()
于 2009-06-06T10:07:22.427 に答える
1

ここにはいくつかの答えがありますが、どれも私の以下の要件を満たしていません:

  1. コマンドが終了するのを待ったり、サブプロセスの出力で端末を汚染したりしたくありません。

  2. リダイレクトを使用して bash スクリプトを実行したい。

  3. bash スクリプト内でパイピングをサポートしたい (例: find ... | tar ...)。

上記の要件を満たす唯一の組み合わせは次のとおりです。

subprocess.Popen(['./my_script.sh "arg1" > "redirect/path/to"'],
                 stdout=subprocess.PIPE, 
                 stderr=subprocess.PIPE,
                 shell=True)
于 2020-01-15T08:43:47.263 に答える