Как я могу выполнить внешнюю команду асинхронно из Python? - PullRequest
94 голосов
/ 12 марта 2009

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

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

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

Затем я ушел и провел некоторое тестирование, и, похоже, os.system() выполнит работу при условии, что я использую & в конце команды, чтобы мне не пришлось ждать ее возвращения. Что мне интересно, так это если это правильный способ для достижения такой цели? Я попытался commands.call(), но он не будет работать для меня, потому что он блокирует внешнюю команду.

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

Ответы [ 7 ]

112 голосов
/ 12 марта 2009

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(), чтобы отправить ему данные на стандартный ввод и дождитесь его завершения.

39 голосов
/ 12 марта 2009

Если вы хотите запустить много процессов параллельно, а затем обрабатывать их, когда они дают результаты, вы можете использовать опрос, как показано ниже:

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 в первом запущенном процессе, и оказывается, что он выполняется дольше, остальные запущенные процессы будут сидеть. там простаивают, когда вы могли бы обрабатывать их результаты.

9 голосов
/ 12 марта 2009

Что мне интересно, так это то, что этот [os.system ()] является правильным способом для достижения такой цели?

Нет. os.system() не правильный путь. Вот почему все говорят, чтобы использовать subprocess.

Для получения дополнительной информации читайте http://docs.python.org/library/os.html#os.system

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

7 голосов
/ 12 марта 2009

У меня был хороший успех с модулем 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
6 голосов
/ 06 июля 2010

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

3 голосов
/ 06 июня 2009

У меня та же проблема при попытке подключиться к терминалу 3270 с помощью скриптового программного обеспечения s3270 в Python. Теперь я решаю проблему с подклассом 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()
2 голосов
/ 06 августа 2017

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

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

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

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

...