Python: ответ на подсказки командной строки

Я пытаюсь использовать Python для взаимодействия с другой программой через командную строку. Основная проблема, с которой я столкнулся, – это конкретный вызов с несколькими подсказками. Изначально вызов командной строки запрашивает имя проекта, а затем продолжает спрашивать, хочу ли я просмотреть любую из подпапок проекта. Мне нужно ответить y / n на каждый из них по порядку, и ответ каждому из них, к сожалению, не все y или n. Кроме того, я не могу знать ответ на вопрос, не читая отдельные подсказки, поэтому я не могу сразу отправить блок «y» или «n».

Это вызов командной строки:

si viewproject

После ввода команды в командной строке:

Введите название проекта:

И пример ответа будет:

Введите название проекта: c: /test.pj

После ввода проекта он запрашивает следующее:

Вы хотите переписать в подпроект test_subprj.pj? [ynYN] (п)

В какой момент мне нужно ответить либо ay, либо n в зависимости от того, нужен ли мне этот подпроект. Опять же, ответ на этот вопрос зависит от подпроекта. Мне нужно уметь читать подпроект в этом приглашении, чтобы ответить на него «y» или «n»,

В настоящее время мне нужно вручную вводить в проект и каждый из y и n соответственно. Моя цель – автоматизировать этот процесс с помощью Python.

Есть ли способ ответить на эти подсказки командной строки автоматически?

Текущий прогресс

Стратегия подпроцесса

project_path = "c:/test.pj" with Popen(["si", "viewproject", "--project=" + project_path], stdin=PIPE, stdout=PIPE, universal_newlines=True) as p: for line in p.stdout: if line.startswith("Do you want"): answer = 'n' else: continue # skip it print(answer, file=p.stdin) # provide answer p.stdin.flush() 

Этот метод висит после инструкции Popen. Это никогда не приводит к ошибкам, но никогда не входит или не выходит из инструкции for и никогда не завершается. В настоящее время я не выполняю все ответы на «n», но это будет заменено логикой позже.

Стратегия Winpexpect

  import re import sys from functools import partial import winpexpect project_path = "c:/test.pj" p = winpexpect.winspawn('si viewproject --project=' + project_path) p.logfile = sys.stdout patterns = [re.compile('ynYN'), winpexpect.EOF] for found in iter(partial(p.expect, patterns), 1): # until EOF if found == 0: answer = 'n' p.sendline(answer) 

Возвращает следующее сообщение об ошибке:

  Traceback (most recent call last): File "C:\Python33\lib\site-packages\winpexpect-1.5-py3.3.egg\winpexpect.py", line 541, in read_nonblocking handle, status, data = self.child_output.get(timeout=timeout) File "C:\Python33\lib\queue.py", line 175, in get raise Empty queue.Empty During handling of the above exception, another exception occurred: Traceback (most recent call last): File "C:\Python33\lib\site-packages\winpexpect-1.5-py3.3.egg\pexpect.py", line 1378, in expect_loop c = self.read_nonblocking (self.maxread, timeout) File "C:\Python33\lib\site-packages\winpexpect-1.5-py3.3.egg\winpexpect.py", line 543, in read_nonblocking raise TIMEOUT('Timeout exceeded in read_nonblocking().') pexpect.TIMEOUT: Timeout exceeded in read_nonblocking(). During handling of the above exception, another exception occurred: Traceback (most recent call last): File "K:\eclipse_3.6.0\plugins\org.python.pydev_2.6.0.2012062818\pysrc\pydev_runfiles.py", line 432, in __get_module_from_str mod = __import__(modname) File "C:\workspace\Test_prj\Test_prj.py", line 19, in <module> for found in iter(partial(p.expect, patterns), 1): # until EOF File "C:\Python33\lib\site-packages\winpexpect-1.5-py3.3.egg\pexpect.py", line 1311, in expect return self.expect_list(compiled_pattern_list, timeout, searchwindowsize) File "C:\Python33\lib\site-packages\winpexpect-1.5-py3.3.egg\pexpect.py", line 1325, in expect_list return self.expect_loop(searcher_re(pattern_list), timeout, searchwindowsize) File "C:\Python33\lib\site-packages\winpexpect-1.5-py3.3.egg\pexpect.py", line 1409, in expect_loop raise TIMEOUT (str(e) + '\n' + str(self)) pexpect.TIMEOUT: Timeout exceeded in read_nonblocking(). <winpexpect.winspawn object at 0x0144AE50> version: 2.3 ($Revision: 399 $) command: si args: ['si', 'viewproject', '--project=c:/test.pj'] searcher: searcher_re: 0: re.compile("ynYN") 1: EOF buffer (last 100 chars): before (last 100 chars): after: <class 'pexpect.TIMEOUT'> match: None match_index: None exitstatus: None flag_eof: False pid: 6448 child_fd: 4 closed: False timeout: 30 delimiter: <class 'pexpect.EOF'> logfile: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='Cp1252'> logfile_read: None logfile_send: None maxread: 2000 ignorecase: False searchwindowsize: None delaybeforesend: 0.05 delayafterclose: 0.1 delayafterterminate: 0.1 ERROR: Module: Test_prj could not be imported (file: C:\workspace\Test_prj\Test_prj.py). 

Установка Winpexpect

Lazy Persons Way

Установить распределить

Сделай это

Установить PyWin32

Установка Winpexpect

Дополнительно: установить нос

Дополнительно: установить Pip

Первая мировая проблема

Python – это новый язык для меня, и я никогда не устанавливал пакет раньше для Python. Кроме того, Python 3.x немного отличается от других версий Python, что делает установку модулей немного более приключенческой.

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

Настроить

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

Для Python 3.x этот стандарт для распространения модулей называется Distutils.

Вот как разработчик использует Distutils: распространение модулей Python

И вот как конечный пользователь использует Distutils: установка модулей Python

Обычно достаточно переместиться в папку загруженного модуля в командной строке и запустить «setup.py install».

НО

Иногда жизнь не так проста, и у вас могут быть проблемы с установкой. На самом деле вам может понадобиться что-то еще. Например, вы можете получить следующую ошибку:

"ImportError" Нет модуля с именем Setuptools ""

К счастью, для этого есть решение: Python 3: ImportError «Нет модуля с именем Setuptools»

Как оказалось, не все использует distutils. Некоторые пакеты используют setuptools. К сожалению, для Python 3.x нет setuptools. Скорее, Python 3.x использует дистрибутив, который является ветвью setuptools.

Итак, для тех, кто использует Python 3.x, вот Распространение: Распространять

Для тех, кто использует Python 2.x, вот Setuptools: Setuptools

В Инструкции по установке для Distribute он говорит следующее: «Загрузите файл distribute_setup.py <http://python-distribute.org/distribute_setup.py> _ и выполните его, используя интерпретатор Python по вашему выбору».

В нем также говорится: «Обратите внимание, что этот файл также предоставляется в исходной версии».

Поэтому я загрузил Distribute и сохранил его на компьютере. После того, как он был сохранен на компьютере, я запустил дистрибутив distute_setup.py из исходной версии и получил следующую ошибку:

 Downloading http://pypi.python.org/packages/source/d/distribute/distribute-0.6.36.tar.gz Traceback (most recent call last): File "C:\Python33\lib\urllib\request.py", line 1252, in do_open h.request(req.get_method(), req.selector, req.data, headers) File "C:\Python33\lib\http\client.py", line 1049, in request self._send_request(method, url, body, headers) File "C:\Python33\lib\http\client.py", line 1087, in _send_request self.endheaders(body) File "C:\Python33\lib\http\client.py", line 1045, in endheaders self._send_output(message_body) File "C:\Python33\lib\http\client.py", line 890, in _send_output self.send(msg) File "C:\Python33\lib\http\client.py", line 828, in send self.connect() File "C:\Python33\lib\http\client.py", line 806, in connect self.timeout, self.source_address) File "C:\Python33\lib\socket.py", line 406, in create_connection for res in getaddrinfo(host, port, 0, SOCK_STREAM): socket.gaierror: [Errno 11001] getaddrinfo failed During handling of the above exception, another exception occurred: Traceback (most recent call last): File "C:\workspace\PythonTest\distribute_setup.py", line 553, in <module> sys.exit(main()) File "C:\workspace\PythonTest\distribute_setup.py", line 549, in main tarball = download_setuptools(download_base=options.download_base) File "C:\workspace\PythonTest\distribute_setup.py", line 204, in download_setuptools src = urlopen(url) File "C:\Python33\lib\urllib\request.py", line 160, in urlopen return opener.open(url, data, timeout) File "C:\Python33\lib\urllib\request.py", line 473, in open response = self._open(req, data) File "C:\Python33\lib\urllib\request.py", line 491, in _open '_open', req) File "C:\Python33\lib\urllib\request.py", line 451, in _call_chain result = func(*args) File "C:\Python33\lib\urllib\request.py", line 1272, in http_open return self.do_open(http.client.HTTPConnection, req) File "C:\Python33\lib\urllib\request.py", line 1255, in do_open raise URLError(err) urllib.error.URLError: <urlopen error [Errno 11001] getaddrinfo failed> 

Ну, это не годится! Я, честно говоря, до сих пор не знаю, откуда эта ошибка или почему это произошло.

Несмотря на это, я нашел следующий сайт, на котором был запущен .exe для установки дистрибутива, а также для пипа.

Установить распределить

Установить Pip

Таким образом, я получил те, которые были установлены, а затем использовал следующий сайт для настройки моего компьютера для более простого использования easy_install: настройка Easy Install Made Easy

Как только я получил эту работу, я установил нос: Нос

Причина, по которой я получил нос, состояла в том, что на веб-сайте Winpexpect говорится: «WinPexpect включает в себя модульные тесты. Для запуска тестов вам нужен нос. Используйте следующую команду для запуска тестов:

$ python setup.py test "

Ну, это звучит неплохо :). Теперь мне просто хотелось, чтобы я знал, где выполнить этот тест. Я знаю, что если вы устанавливаете вручную, вы используете команду setup.py install, поэтому определенно будет setup.py в каталоге zipped в Интернете. Чтобы убедиться, что это правильно, я загрузил и сохранил файл winpexpect, извлек информацию, перешел к ней через командную строку и выполнил тест setup.py.

Здесь был приведен следующий результат:

 running test running build_py running egg_info creating c:\documents and settings\slz1fh\desktop\winpexpect\geertj-winpexpect-76df3cfcb143\build\lib\winpexpect.egg-info writing c:\documents and settings\slz1fh\desktop\winpexpect\geertj-winpexpect-76df3cfcb143\build\lib\winpexpect.egg-info\PKG-INFO writing dependency_links to c:\documents and settings\slz1fh\desktop\winpexpect\geertj-winpexpect-76df3cfcb143\build\lib\winpexpect.egg-info\dependency_links.txt writing top-level names to c:\documents and settings\slz1fh\desktop\winpexpect\geertj-winpexpect-76df3cfcb143\build\lib\winpexpect.egg-info\top_level.txt writing requirements to c:\documents and settings\slz1fh\desktop\winpexpect\geertj-winpexpect-76df3cfcb143\build\lib\winpexpect.egg-info\requires.txt writing manifest file 'c:\documents and settings\slz1fh\desktop\winpexpect\geertj-winpexpect-76df3cfcb143\build\lib\winpexpect.egg-info\SOURCES.txt' reading manifest file 'c:\documents and settings\slz1fh\desktop\winpexpect\geertj-winpexpect-76df3cfcb143\build\lib\winpexpect.egg-info\SOURCES.txt' writing manifest file 'c:\documents and settings\slz1fh\desktop\winpexpect\geertj-winpexpect-76df3cfcb143\build\lib\winpexpect.egg-info\SOURCES.txt' running build_ext Traceback (most recent call last): File "C:\Documents and Settings\SLZ1FH\Desktop\winpexpect\geertj-winpexpect-76df3cfcb143\setup.py", line 35, in <module> use_2to3 = True File "C:\Python33\lib\distutils\core.py", line 148, in setup dist.run_commands() File "C:\Python33\lib\distutils\dist.py", line 917, in run_commands self.run_command(cmd) File "C:\Python33\lib\distutils\dist.py", line 936, in run_command cmd_obj.run() File "C:\Python33\lib\site-packages\distribute-0.6.36-py3.3.egg\setuptools\command\test.py", line 138, in run self.with_project_on_sys_path(self.run_tests) File "C:\Python33\lib\site-packages\distribute-0.6.36-py3.3.egg\setuptools\command\test.py", line 118, in with_project_on_sys_path func() File "C:\Python33\lib\site-packages\distribute-0.6.36-py3.3.egg\setuptools\command\test.py", line 164, in run_tests testLoader = cks File "C:\Python33\lib\unittest\main.py", line 124, in __init__ self.parseArgs(argv) File "C:\Python33\lib\unittest\main.py", line 168, in parseArgs self.createTests() File "C:\Python33\lib\unittest\main.py", line 175, in createTests self.module) File "C:\Python33\lib\unittest\loader.py", line 137, in loadTestsFromNames suites = [self.loadTestsFromName(name, module) for name in names] File "C:\Python33\lib\unittest\loader.py", line 137, in <listcomp> suites = [self.loadTestsFromName(name, module) for name in names] File "C:\Python33\lib\unittest\loader.py", line 96, in loadTestsFromName module = __import__('.'.join(parts_copy)) File "C:\Python33\lib\site-packages\nose-1.3.0-py3.3.egg\nose\__init__.py", line 1, in <module> from nose.core import collector, main, run, run_exit, runmodule File "C:\Python33\lib\site-packages\nose-1.3.0-py3.3.egg\nose\core.py", line 143 print "%s version %s" % (os.path.basename(sys.argv[0]), __version__) ^ SyntaxError: invalid syntax 

Итак, версия Nose для Python 3.3 содержит недопустимый синтаксис для Python 3.3?

print "% s version% s"% (os.path.basename (sys.argv [0]), версия ) …

должен обязательно иметь круглые скобки вокруг … Это заставляет меня задаться вопросом, будет ли здесь нос, поскольку он явно выглядит для более ранних версий Python.

В комментариях вы упомянули, что xx viewproject < answers.txt > output.txt работает, но вы не можете его использовать, потому что ответы зависят от вывода подпроцесса.

В общем, pexpect такие winpexpect подобные модули, как winpexpect (для Windows). Что-то вроде:

 import re import sys from functools import partial from winpexpect import EOF, winspawn as spawn p = spawn('xx viewproject') p.logfile = sys.stdout patterns = ['the project:', re.escape('? [ynYN](n)'), EOF] for found in iter(partial(p.expect, patterns), 2): # until EOF if found == 0: p.sendline(project_name) elif found == 1: filename = get_filename_from_prompt(p.before) # a regex could be used answer = yes_or_no_from_subproject.get(filename, 'no') # a dict p.sendline(answer) 

Если приглашения заканчиваются новой строкой (и подпроцесс не буферизует их); вы можете читать строки за строкой напрямую с помощью модуля subprocess :

 from subprocess import Popen, PIPE with Popen(["xx", "viewproject"], stdin=PIPE, stdout=PIPE, universal_newlines=True) as p: for line in p.stdout: if line.startswith("Please enter the name of the project"): answer = project_name elif line.startswith("Would you like to recurse into the subproject"): filename = get_filename_from_prompt(line) # a regex could be used answer = yes_or_no_from_subproject.get(filename, 'n') # a dict else: continue # skip it print(answer, file=p.stdin) # provide answer p.stdin.flush() 

Чтобы проверить, что вы можете прочитать что-то из xx используя subprocess :

 from subprocess import Popen, PIPE, STDOUT with Popen(["xx", "viewproject"], bufsize=0, stdin=PIPE, stdout=PIPE, stderr=STDOUT) as p: print(repr(p.stdout.read(1))) 

Да, в первую очередь вы можете создать подпроцесс как объект:

 p = subprocess.Popen('xx viewproject', shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True) 

Тогда у вас будут такие методы, как communicate() , например:

 newline = os.linesep # [1] commands = ['y', 'n', 'y', 'n', 'y'] p.communicate( newline.join( commands)) 

1 – os.linesep

Который отправит все ответы сразу (и, надеюсь, этого будет достаточно), каждый раз полагаясь на один и тот же порядок вопросов.

Вы также можете попробовать разбор p.stdout а затем записать на p.stdin , но это может привести к тупиковой ситуации, когда один буфер будет заполнен во время ожидания другого, поэтому будьте осторожны с этим. К счастью, в Google есть несколько сложных примеров .

Простой вариант:

 p = Popen(...) line = p.stdout.readline() # At this point, if child process will wait for stdin # you have a deadlock on your hands parse_line( line) p.stdin.write( newline.join( commands).encode( 'utf-8')) 

Я также хотел бы переписать:

 p = subprocess.Popen('si viewproject --project=d:/Projects/test.pj', shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE) 

Для того, чтобы:

 p = subprocess.Popen( ['si', 'viewproject', '--project=d:/Projects/test.pj'], shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE) 

Если вам явно не требуется вызов оболочки.