[python] Python에서 비동기 적으로 외부 명령을 실행하려면 어떻게해야합니까?

Python 스크립트에서 비동기 적으로 셸 명령을 실행해야합니다. 이것은 외부 명령이 꺼지고 필요한 모든 작업을 수행하는 동안 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


답변

non-blocking readlines와 함께 pexpect 를 사용 하는 것도이를위한 또 다른 방법입니다. Pexpect는 교착 상태 문제를 해결하고, 프로세스를 백그라운드에서 쉽게 실행할 수 있으며, 프로세스가 사전 정의 된 문자열을 뱉어 낼 때 콜백을 쉽게 할 수있는 방법을 제공하고 일반적으로 프로세스와의 상호 작용을 훨씬 쉽게 만듭니다.


답변

“돌아올 때까지 기다릴 필요가 없습니다”를 고려할 때 가장 쉬운 해결책 중 하나는 다음과 같습니다.

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 터미널에 연결하는 데 동일한 문제가 있습니다. 이제 여기에서 찾은 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()