Почему 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)