簡體   English   中英

Python:響應命令行提示

[英]Python: Respond to Command Line Prompts

我正在嘗試使用 Python 通過命令行與另一個程序進行交互。 我遇到的主要問題是具有多個后續提示的特定呼叫。 最初,命令行調用會詢問項目名稱,然后繼續詢問我是否要查看項目的任何子文件夾。 我需要按順序對每一個回答 y/n,不幸的是,每個答案都不是 y 或 n。 此外,如果不閱讀單獨的提示,我就無法知道問題的答案,因此我無法一次發送一組“y”或“n”。

這是命令行調用:

si viewproject

輸入命令后,命令行提示:

輸入項目名稱:

一個示例響應是:

輸入項目名稱:c:/test.pj

進入項目后,提示如下:

是否要遞歸到子項目 test_subprj.pj 中? [ynYN](n)

在這一點上,我需要根據我是否需要該子項目來響應 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()

此方法掛在 with 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

懶人之道

安裝分發

做這個

安裝 PyWin32

安裝 Winpexpect

可選:安裝鼻子

可選:安裝 Pip

第一世界問題

Python 對我來說是一門新語言,我之前從未為 Python 安裝過軟件包。 此外,Python 3.x 與其他版本的 Python 略有不同,這使得安裝模塊更加冒險。

因此,為了幫助其他人獲得一些甜蜜的模塊操作(並幫助那些知識淵博的人看看我是否做錯了什么),這里有一個即將成功的故事(希望如此)記錄我如何獲得和安裝我的第一個模塊。

設置

Python 允許第三方團體開發和分發擴展編程語言能力的模塊。 自然,有一種標准方法可以幫助第三方開發人員盡可能輕松地為最終用戶提供模塊。

對於 Python 3.x,分發模塊的標准稱為 Distutils。

以下是開發人員如何使用 Distutils:分發 Python 模塊

以下是最終用戶如何使用 Distutils:安裝 Python 模塊

通常,在命令行中導航到下載模塊的文件夾並運行“setup.py install”就足夠了。

有時生活並不那么容易,您的安裝可能仍然存在問題。 事實上,您可能還需要其他東西。 例如,您可能會收到以下錯誤:

“導入錯誤“沒有名為 Setuptools 的模塊””

幸運的是,有一個解決方案: Python 3: ImportError "No Module named Setuptools"

事實證明,並非所有東西都使用 distutils。 一些軟件包使用 setuptools。 不幸的是,沒有適用於 Python 3.x 的設置工具。 相反,Python 3.x 使用了分發工具,它是 setuptools 的一個分支。

所以對於那些使用 Python 3.x 的人來說,這里是 Distribute: Distribute

對於那些使用 Python 2.x 的人,這里是 Setuptools: Setuptools

在 Distribute 的安裝說明中,它說如下:“下載distribute_setup.py <http://python-distribute.org/distribute_setup.py> _ 並使用您選擇的Python 解釋器執行它。”

它還說:“請注意,源版本中也提供了此文件。”

所以我下載了 Distribute 並將其保存到計算機上。 將其保存到計算機后,我從源版本運行了distribute_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 測試”

嗯,這聽起來不錯:)。 現在我只是希望我知道在哪里運行該測試。 我知道如果你手動安裝你使用 setup.py install 命令,所以肯定會有一個 setup.py 在線壓縮目錄中。 為了查看這是否正確,我下載並保存了 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

好的,那么 Python 3.3 版本的 Nose 包含無效的 Python 3.3 語法?

打印 "%s version %s" % (os.path.basename(sys.argv[0]), version )...

絕對應該在它周圍加上括號......這讓我懷疑鼻子是否真的可以在這里工作,因為它顯然是為早期版本的 Python 制作的。

在您提到xx viewproject < answers.txt > output.txt的評論中,您不能使用它,因為答案取決於子xx viewproject < answers.txt > output.txt的輸出。

通常,可以使用pexpect的模塊,例如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()

要測試您是否可以使用subprocessxx讀取某些內容:

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 ,但是當一個緩沖區在等待另一個緩沖區已滿時,這可能會導致死鎖,因此請注意這一點。 幸運的是,谷歌上有一些復雜的例子

簡單版本是:

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)

除非您明確需要 Shell 調用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM