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

StackOverflow https://stackoverflow.com/questions/636561

Вопрос

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

Другие советы

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

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)

Поток управления там немного запутан, потому что я пытаюсь сделать его маленьким - вы можете рефакторинг на свой вкус. : -)

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

Мне интересно, является ли этот [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

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

У меня та же проблема при попытке подключиться к терминалу 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()

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top