Вопрос по subprocess, stdin, python, interactive, stdout – Запуск интерактивной команды из Python

19

У меня есть скрипт, который я хочу запустить из Python (2.6.5), который следует логике ниже:

Prompt user for password. Looks like ("Enter password: ") (*Note: Input does not echo to screen) Output irrelevant information Prompt user for response ("Blah Blah filename.txt blah blah (Y/N)?: ")

Последняя строка подсказки содержит текст, который мне нужно проанализировать (filename.txt). Предоставленный ответ не имеет значения (программа может фактически завершиться, не предоставив его, пока я могу разобрать строку)

Мои требованияsomewhat похожий наОбертывание интерактивного приложения командной строки в сценарии Python, но ответы там кажутся немного запутанными, и мой по-прежнему зависает, даже когда ОП упоминает, что это не для него.

Оглядываясь вокруг, я пришел к выводу, чтоsubprocess это лучший способ сделать это, но у меня есть несколько проблем. Вот моя линия Попен:

p = subprocess.Popen("cmd", shell=True, stdout=subprocess.PIPE, 
stderr=subprocess.STDOUT, stdin=subprocess.PIPE)

When I call a read() or readline() on stdout, the prompt is printer to the screen and it hangs.

If I call a write("password\n") for stdin, the prompt is written to the screen and it hangs. The text in write() is not written (I don't the cursor move the a new line).

If I call p.communicate("password\n"), same behavior as write()

Я искал несколько идей здесь о том, как лучше всегоstdin и, возможно, как разобрать последнюю строку в выводе, если вы чувствуете себя щедрым, хотя я, возможно, смогу со временем это выяснить.

@ColinDunklau Я надеялся свести к минимуму использование внешних модулей user1521597
Вы должны посмотреть на pexpect:noah.org/wiki/pexpect Colin Dunklau
@Джоран, ха-ха, извини. Это то, что я имел в виду. user1521597
Я думаю, что вам нужно написать в стандартный вывод и читать из стандартного ввода ... а не наоборот, как вы положили выше Joran Beasley

Ваш Ответ

2   ответа
0

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

import os
import sys
from getpass import unix_getpass

def cmd(cmd):
    cmd = cmd.split()
    code = os.spawnvpe(os.P_WAIT, cmd[0], cmd, os.environ)
    if code == 127:
        sys.stderr.write('{0}: command not found\n'.format(cmd[0]))
    return code

password = unix_getpass('Password: ')
cmd_run = './run.sh --password {0}'.format(password)
cmd(cmd_run)

pattern = raw_input('Pattern: ')
lines = []
with open('filename.txt', 'r') as fd:
    for line in fd:
        if pattern in line:
            lines.append(line)

# manipulate lines
10

Если вы общаетесь с программой, которая порождает подпроцесс, вы должны проверитьНеблокирующее чтение на подпроцесс. PIPE в Python, У меня была похожая проблема с моим приложением, и я обнаружил, что использование очередей - лучший способ поддерживать постоянную связь с подпроцессом.

Что касается получения значений от пользователя, вы всегда можете использовать встроенную функцию raw_input () для получения ответов, а для паролей попробуйте использоватьgetpass модуль для получения не повторяющихся паролей от вашего пользователя. Затем вы можете проанализировать эти ответы и записать их в свой подпроцесс & apos; STDIN.

В итоге я сделал что-то похожее на следующее:

import sys
import subprocess
from threading  import Thread

try:
    from Queue import Queue, Empty
except ImportError:
    from queue import Queue, Empty  # python 3.x


def enqueue_output(out, queue):
    for line in iter(out.readline, b''):
        queue.put(line)
    out.close()


def getOutput(outQueue):
    outStr = ''
    try:
        while True: #Adds output from the Queue until it is empty
            outStr+=outQueue.get_nowait()

    except Empty:
        return outStr

p = subprocess.Popen("cmd", stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False, universal_newlines=True)

outQueue = Queue()
errQueue = Queue()

outThread = Thread(target=enqueue_output, args=(p.stdout, outQueue))
errThread = Thread(target=enqueue_output, args=(p.stderr, errQueue))

outThread.daemon = True
errThread.daemon = True

outThread.start()
errThread.start()

try:
    someInput = raw_input("Input: ")
except NameError:
    someInput = input("Input: ")

p.stdin.write(someInput)
errors = getOutput(errQueue)
output = getOutput(outQueue)

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

Похожие вопросы