почему python.subprocess зависает после proc.communicate ()? - PullRequest
14 голосов
/ 09 марта 2010

У меня есть интерактивная программа под названием my_own_exe. Сначала он печатает alive, затем вы вводите S\n, а затем снова печатает alive. Наконец вы вводите L\n. Это делает некоторую обработку и выходит.

Однако, когда я вызываю его из следующего скрипта на python, программа как будто зависает после вывода первого «живого».

Может кто-нибудь здесь сказать мне, почему это происходит?

// после прочтения последующих замечаний (спасибо вам, ребята), я изменил код следующим образом:

import subprocess
import time

base_command = "./AO_FelixStrategy_UnitTest --bats 31441 --chix 12467 --enxutp 31884 --turq 26372 --symbol SOGN --target_date " + '2009-Oct-16'
print base_command

proc2 = subprocess.Popen(base_command, shell=True , stdin=subprocess.PIPE,)

time.sleep(2);
print "aliv"
proc2.communicate('S\n')

print "alive"
time.sleep(6)

print "alive"
print proc2.communicate('L\n')
time.sleep(6)

теперь программа работает с первым вводом 'S \ n', но затем остановилась, а второй 'L \ n' вроде бы игнорируется.

Может кто-нибудь дать мне представление, почему это так?

Ответы [ 3 ]

27 голосов
/ 09 марта 2010

Из документов для communicate:

Взаимодействие с процессом: отправка данных на стандартный ввод. Читайте данные из stdout и stderr, пока не будет достигнут конец файла. Ожидание завершения процесса.

Таким образом, после запуска communicate() процесс был завершен .

Если вы хотите писать и читать, не дожидаясь остановки процесса:

  • Никогда никогда не используйте shell=True - он без необходимости вызывает оболочку для вызова вашей программы, поэтому между вами и вашей программой будет другой процесс. Это имеет много неприятных побочных эффектов. По умолчанию shell=False, поэтому вы должны придерживаться этого. Измените Popen строку на:

    p = subprocess.Popen(["./AO_FelixStrategy_UnitTest",
                          "--bats", "31441", "--chix", "12467",
                          "--enxutp", "31884", "--turq", "26372",
                          "--symbol", "SOGN", "--target_date", '2009-Oct-16'],
                         stdin=subprocess.PIPE, 
                         stdout=subprocess.PIPE)
    
  • Используйте p.stdin.write для записи в процесс. Для чтения используйте p.stdout.read.

  • Вызов p.stdout.read, если нечего читать, заблокирует. Вызов p.stdin.write, если буфер записи заполнен, блокируется. Поэтому вы должны убедиться, что у вас есть что-то для чтения / записи - вы делаете это на Unix OS, используя select. На окнах вы, к сожалению, должны прибегать к потокам. По крайней мере, это то, что Popen.communicate делает внутри.
  • Если вы не написали AO_FelixStrategy_UnitTest, у вас есть возможные дополнительные проблемы:
    • Это может быть чтение из другого места, а не стандартный ввод. Некоторые программы читают напрямую из терминала, другие используют некоторые OS API для чтения. Это означает, что данные, записанные в stdin, не попадут в программу. Это часто относится к запросам пароля.
    • Помните, что вы должны учитывать AO_FelixStrategy_UnitTest буферов. По умолчанию стандартная связь C PIPE буферизуется, поэтому вы можете не видеть никаких выходных данных до тех пор, пока не закроете входную сторону (выполнив p.stdin.close(). Если AO_FelixStrategy_UnitTest периодически не очищает выход.

Вот пример кода, основанный на том, что вы описываете. Это может работать в зависимости от того, как AO_FelixStrategy_UnitTest был разработан:

p = subprocess.Popen(["./AO_FelixStrategy_UnitTest",
                      "--bats", "31441", "--chix", "12467",
                      "--enxutp", "31884", "--turq", "26372",
                      "--symbol", "SOGN", "--target_date", '2009-Oct-16'],
                     stdin=subprocess.PIPE, 
                     stdout=subprocess.PIPE)
output = p.communicate('S\nL\n')[0]
print output
4 голосов
/ 09 марта 2010

communicate() считывает данные из stdout и stderr , пока не будет достигнут конец файла . - Он ждет, пока ваша программа не закроется.

1 голос
/ 17 августа 2016

comunicate будет запущен только один раз , а затем закроет канал, поэтому, если вы хотите отправить ему несколько команд, вам нужно отправить одну за другой в та же строка .

Вот пример, который сработал для меня после некоторого исследования, пробуя потоки, subprocess32, stdin.write, stdout.read и т. Д. И т. Д. Эта информация отсутствует в официальной справочной информации Python для связи: https://docs.python.org/2/library/subprocess.html

Единственное место, где я это узнал, было здесь: Связь с подпроцессом Python убивает мой процесс

В любом случае вот код, простой, без потоков, без подпроцесса32, работает на Linux и Windows. Да, вы должны знать, сколько раз отправлять команды другому процессу, но в целом вы это знаете. Вдобавок к этому вы можете добавить потоки, cwd, shell = True или все, что вам нужно, но это самый простой случай:

def do_commands(self, cmd, parms):
    proc = subprocess.Popen(cmd,  stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE )

    # wait for the process to terminate
    out, err = process.communicate(cmd_parms)
    errcode = process.returncode

    return errcode, out, err

Так, например, если вы хотите отправить несколько возвратов каретки (\ n) вызываемому приложению и параметру в середине (обратите внимание, в интерактивном режиме), вы бы назвали его примерно так:

cmd_parms = "\n\n\n\n\nparm\n\n"

errcode, out, err = do_commands(command, cmd_parms)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...