Почему python.subprocess зависает после proc.communicate()?
У меня есть интерактивная программа под названием my_own_exe
. Во-первых, он печатает alive
, затем вы вводите S\n
, а затем снова печатаете alive
. Наконец, вы вводите L\n
. Он выполняет некоторую обработку и завершает работу.
Однако, когда я вызываю его из следующего python script, программа, похоже, зависала после распечатки первого "живого".
Может ли кто-нибудь здесь рассказать мне, почему это происходит?
//после прочтения последующих действий (спасибо вам, ребята), я изменил код следующим образом:
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 L n игнорируется.
Может кто-нибудь дать мне понять, почему это так?
Ответы
Ответ 1
Из docs для communicate
:
Взаимодействие с процессом: отправка данных в stdin. Чтение данных из 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 с помощью 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
Ответ 2
communicate()
считывает данные из stdout и stderr до тех пор, пока не будет достигнут конец файла. - Он ждет, пока ваша программа не выйдет.
Ответ 3
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) в вызываемое приложение и a param в середине (в интерактивном режиме), вы можете назвать это примерно так:
cmd_parms = "\n\n\n\n\nparm\n\n"
errcode, out, err = do_commands(command, cmd_parms)