Взаимодействовать с консольным приложением Windows через Python

Я использую Python 2.5 на Windows. Я хочу взаимодействовать с консольным процессом через Popen. В настоящее время у меня есть этот небольшой фрагмент кода:

p = Popen( ["console_app.exe"], stdin=PIPE, stdout=PIPE )
# issue command 1...
p.stdin.write( 'command1\n' )
result1 = p.stdout.read() # <---- we never return here
# issue command 2...
p.stdin.write( 'command2\n' )
result2 = p.stdout.read()

Я могу написать на стандартный ввод, но не могу прочитать на стандартный вывод. Я пропустил шаг? Я не хочу использовать p.communicate( "command")[0], так как он завершает процесс, и мне нужно динамически взаимодействовать с процессом с течением времени.

Заранее спасибо.

5 ответов

Ваша проблема здесь в том, что вы пытаетесь управлять интерактивным приложением.

stdout.read() продолжит чтение, пока не достигнет конца потока, файла или канала. К сожалению, в случае интерактивной программы канал закрывается только тогда, когда программа завершается; что никогда, если команда, которую вы послали, это было что-то кроме "quit",

Вам придется возвращаться к чтению вывода подпроцесса построчно, используя stdout.readline(), и вам лучше узнать, когда программа готова принять команду, и когда команда, которую вы дали программе, закончена, и вы можете предоставить новую. В случае такой программы, как cmd.exe, четное readline() этого будет недостаточно, так как строка, указывающая, что новая команда может быть отправлена, не завершается новой строкой, поэтому придется анализировать вывод за байтом. Вот пример сценария, который выполняется cmd.exeищет подсказку, затем выдает dir а затем exit:

from subprocess import *
import re

class InteractiveCommand:
    def __init__(self, process, prompt):
        self.process = process
        self.prompt  = prompt
        self.output  = ""
        self.wait_for_prompt()

    def wait_for_prompt(self):
        while not self.prompt.search(self.output):
            c = self.process.stdout.read(1)
            if c == "":
                break
            self.output += c

        # Now we're at a prompt; clear the output buffer and return its contents
        tmp = self.output
        self.output = ""
        return tmp

    def command(self, command):
        self.process.stdin.write(command + "\n")
        return self.wait_for_prompt()

p      = Popen( ["cmd.exe"], stdin=PIPE, stdout=PIPE )
prompt = re.compile(r"^C:\\.*>", re.M)
cmd    = InteractiveCommand(p, prompt)

listing = cmd.command("dir")
cmd.command("exit")

print listing

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

from subprocess import *

p = Popen( ["cmd.exe"], stdin=PIPE, stdout=PIPE )
p.stdin.write("dir\n")
p.stdin.write("exit\n")

print p.stdout.read()

Вы пытались заставить оконные линии окон? т.е.

p.stdin.write( 'command1 \r\n' )
p.stdout.readline()

ОБНОВИТЬ:

Я только что проверил решение на Windows cmd.exe, и он работает с readline(). Но у него есть одна проблема: блоки Popd's stdout.readline. Так что, если приложение когда-нибудь вернет что-то без конца, ваше приложение застрянет навсегда.

Но есть обходной путь для этой проверки: http://code.activestate.com/recipes/440554/

Возможно ли, что консольное приложение каким-то образом буферизует свой вывод, так что оно отправляется только на стандартный вывод, когда канал закрыт? Если у вас есть доступ к коду для консольного приложения, может, вам поможет вставка сброса после пакета выходных данных?

В качестве альтернативы, это на самом деле запись в stderr и вместо stdout по какой-то причине?

Просто снова посмотрел на ваш код и подумал о чем-то другом, я вижу, вы отправляете "команду \n". Может ли консольное приложение просто ждать символ возврата каретки вместо новой строки? Возможно, консольное приложение ожидает, пока вы отправите команду, прежде чем она выдаст какой-либо вывод.

Была такая же проблема здесь. Я покопался в исходном коде DrPython и украл решение wx.Execute(), которое работает нормально, особенно если ваш скрипт уже использует wx. Я никогда не находил правильное решение на платформе Windows, хотя...

Я думаю, что вы можете попробовать использовать readline() вместо этого?

Изменить: извините, неправильно.

Может быть, этот вопрос может помочь вам?

Другие вопросы по тегам