2014-01-15 4 views
5

Я заметил два разных поведения с двумя подходами, которые должны были привести к одному и тому же результату.subprocess popen.communicate() vs. stdin.write() и stdout.read()

Цель - выполнить внешнюю программу с использованием модуля подпроцесса, отправить некоторые данные и прочитать результаты.

Внешняя программа PLINK, платформа WindowsXP, версия 3.3 для Python.

Основной idea-

execution=["C:\\Pr..\\...\\plink.exe", "-l", username, "-pw", "***", IP] 
a=subprocess.Popen(execution, bufsize=0, stdout=PIPE, stdin=PIPE, stderr=STDOUT, shell=False) 
con=a.stdout.readline() 
if (con.decode("utf-8").count("FATAL ERROR: Network error: Connection timed out")==0): 
    a.stdin.write(b"con rout 1\n") 
    print(a.stdout.readline().decode("utf-8")) 
    a.stdin.write(b"infodf\n") 
    print(a.stdout.readline().decode("utf-8")) 
else: 
    print("ERROR") 
a.kill() 

До сих пор так хорошо.

Теперь я хочу иметь возможность делать цикл (после каждой записи на stdin подпроцесса), который ждет до EOF в стандартном дескрипторе sub, печатает его, затем другую команду stdin и т. Д.

Итак, я сначала попробовал, какие предыдущие обсуждения о том же уроке темы (live output from subprocess command, read subprocess stdout line by line, python, subprocess: reading output from subprocess).

И он не работал (он вечно вечно), потому что процесс PLINK остается в живых до тех пор, пока я его не уничтожу, поэтому нет необходимости ждать, пока stdout вспомогательного процесса достигнет EOF или сделайте цикл while stdout истинно, потому что это всегда будет правдой, пока я не убью его.

Так что я решил читать стандартный вывод в два раза каждый раз, когда я пишу на стандартный ввод (хороший прийти для меня) -

execution=["C:\\Pr..\\...\\plink.exe", "-l", username, "-pw", "***", IP] 
a=subprocess.Popen(execution, bufsize=0, stdout=PIPE, stdin=PIPE, stderr=STDOUT, shell=False) 
con=a.stdout.readline() 
if (con.decode("utf-8").count("FATAL ERROR: Network error: Connection timed out")==0): 
    a.stdin.write(b"con rout 1\n") 
    print(a.stdout.readline().decode("utf-8")) 
    print(a.stdout.readline().decode("utf-8")) //the extra line [1] 
    a.stdin.write(b"infodf\n") 
    print(a.stdout.readline().decode("utf-8")) 
    print(a.stdout.readline().decode("utf-8")) //the extra line [2] 
else: 
    print("ERROR") 
a.kill() 

Но первый дополнительный readline() вешает навсегда, насколько я понимаю, для того же причина, о которой я упоминал. Первый дополнительный readline() ждет навсегда для вывода, потому что единственный выход уже был прочитан в первом , а поскольку PLINK жив, функция просто «сидит» там и ждет выхода новой выходной линии.

Так что я попробовал этот код, ожидая того же повесить, потому что Plink никогда не умирает, пока я не убить это-

execution=["C:\\Pr..\\...\\plink.exe", "-l", username, "-pw", "***", IP] 
a=subprocess.Popen(execution, bufsize=0, stdout=PIPE, stdin=PIPE, stderr=STDOUT, shell=False) 
con=a.stdout.readline() 
if (con.decode("utf-8").count("FATAL ERROR: Network error: Connection timed out")==0): 
    a.stdin.write(b"con rout 1\n") 
    print(a.stdout.readline().decode("utf-8")) 
    a.stdin.write(b"infodf\n") 
    print(a.stdout.readline().decode("utf-8")) 
    print(a.communicate()[0].decode("utf-8"))  //Popen.communicate() function 
else: 
    print("ERROR") 
a.kill() 

Я пробовал, потому что в соответствии с документацией communicate(), функция ожидания до процесса закончился, а затем он заканчивается. Кроме того, он считывает с stdout до EOF. (так же, как запись и чтение stdout и stdin)

Но communicate() заканчивается и не висит, в отличие от предыдущего кода.

Что мне здесь не хватает? почему при использовании communicate() PLINK заканчивается, но при использовании readline() это не так?

ответ

4

Ваша программа без communicate() взаимоблокировок, потому что оба процесса ждут друг друга, чтобы что-то написать, прежде чем они сами что-нибудь напишут.

communicate() не является взаимоблокировкой в ​​вашем примере, потому что он закрывает поток, как и команда a.stdin.close(). Это отправляет EOF вашему подпроцессу, позволяя ему знать, что больше нет ввода, поэтому он может закрыться, что, в свою очередь, закрывает его вывод, поэтому a.stdout.read() в конечном итоге возвращает EOF (пустая строка).

Нет специального сигнала, что ваш основной процесс получит от вашего подпроцесса, чтобы вы знали, что он записывает результаты из одной команды, но готов к другой команде.

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

-1

Дело в том, что при использовании subprocess.Popen ваш код продолжает считываться еще до завершения процесса. Попробуйте добавление .wait() к вашему POPEN вызова (см documentation),

a=subprocess.Popen(execution, bufsize=0, stdout=PIPE, stdin=PIPE, stderr=STDOUT, shell=False).wait() 

Это будет гарантировать, что выполнение заканчивается перед выходом на с чем-нибудь еще.

+0

Извините, но это не ответ на мой вопрос. Я знаю этот факт :) – user2162550

-1

Вы можете использовать темы, писать и читать одновременно, особенно если выход только должен быть напечатан пользователю:

from threading import Thread 

def print_remaining(stream): 
    for line in stream: 
     print(line.decode("utf-8")) 

con = a.stdout.readline() 
if "FATAL ERROR" not in con.decode("utf-8"): 
    Thread(target=print_remaining, args=[a.stdout]).start() 
    for cmd in LIST_OF_COMMANDS_TO_SEND: 
     a.stdin.write(cmd) 
+0

Я знаю, что могу это использовать. обратите внимание мой вопрос. – user2162550

Смежные вопросы