Как я могу запустить внешнюю команду асинхронно с Python?

мне нужно запустить команду асинхронно из скрипта Python. Под этим я подразумеваю, что я хочу, чтобы мой скрипт Python продолжал работать, пока внешняя команда выключается и делает все, что ей нужно.

Я прочитал этот пост:

вызов внешней команды в Python

затем я пошел и сделал некоторое тестирование, и это выглядит как os.system() будет делать эту работу при условии, что я использую & в конце команды чтобы мне не пришлось ждать его возвращения. Мне интересно, является ли это правильным способом выполнить такую вещь? Я пытался!--2--> но это не будет работать для меня, потому что он блокирует на внешние команды.

пожалуйста, дайте мне знать, если вы используете os.system() для этого желательно, или если я должен попробовать другой маршрут.

7 ответов


подпроцесс.К popen делает именно то, что вы хотите.

from subprocess import Popen
p = Popen(['watch', 'ls']) # something long running
# ... do other stuff while subprocess is running
p.terminate()

(Edit для завершения ответа из комментариев)

экземпляр 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

модуль подпроцесса обеспечивает больше мощные установки для нереста новых процессы и получение их результаты; использование этого модуля предпочтительнее использовать эту функцию. Использовать модуль подпроцесса. Проверять особенно замена старых Функции с модулем подпроцесса раздел.


используя pexpect [ http://www.noah.org/wiki/Pexpect ] с неблокируемой readlines другой способ сделать это. Pexpect решает проблемы взаимоблокировки, позволяет легко запускать процессы в фоновом режиме и дает простые способы обратного вызова, когда ваш процесс выплевывает предопределенные строки, и, как правило, упрощает взаимодействие с процессом.


У меня был хороший успех с 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

У меня такая же проблема, пытаясь подключиться к терминалу 3270, используя программное обеспечение сценариев s3270 в Python. Теперь я решаю проблему с подклассом процесса, который я нашел здесь:

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()

учитывая, что "мне не нужно ждать его возвращения", одним из самых простых решений будет следующее:

subprocess.Popen( \
    [path_to_executable, arg1, arg2, ... argN],
    creationflags = subprocess.CREATE_NEW_CONSOLE,
).pid

но... Из того, что я читал, это не "правильный способ выполнить такую вещь" из-за рисков безопасности, созданных subprocess.CREATE_NEW_CONSOLE флаг.

ключевые вещи, которые здесь происходят, - это использование subprocess.CREATE_NEW_CONSOLE для создания новой консоли и .pid (возвращает идентификатор процесса, чтобы вы могли проверить программу позже, если хотите), чтобы не ждать завершения программы работа.