从命令行界面读取输入并使用Python提供答案。
从命令行界面读取输入并使用Python提供答案。
我正在尝试使用Python通过命令行与另一个程序交互。我遇到的主要问题是一个特定的调用有多个后续提示。最初的命令行调用要求输入一个项目的名称,然后继续询问是否要查看项目的任何子文件夹。我需要按顺序对每个子文件夹回答是或否,而且每个回答不都是全是是或全是否。此外,我无法在不阅读各个提示的情况下知道问题的答案,因此无法一次性发送一堆'y'或'n'。
这是命令行调用:
si viewproject
输入命令后,命令行提示:
输入项目名称:
示例回答:
输入项目名称:c:/test.pj
输入项目后,提示如下:
是否递归进入子项目test_subprj.pj?[ynYN](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 # 跳过它 print(answer, file=p.stdin) # 提供答案 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): # 直到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 "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().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: match: None match_index: None exitstatus: None flag_eof: False pid: 6448 child_fd: 4 closed: False timeout: 30 delimiter: logfile: <_io.TextIOWrapper name=' ' 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
懒人的方式
安装Distribute
做这个
安装PyWin32
安装Winpexpect
可选:安装Nose
可选:安装Pip
富人的问题
Python是对我来说一个新的语言,我以前从未为Python安装过软件包。此外,Python 3.x与其他版本的Python有些不同,使得安装模块稍微复杂一些。
因此,为了帮助其他人获得一些甜蜜的模块操作(并帮助那些更有经验的人看看我是否有任何错误),这里有一个即将成功的故事(希望如此),记录了我如何获得和安装我的第一个模块。
设置
Python允许第三方团体开发和分发扩展编程语言能力的模块。当然,有一种标准方法可以帮助第三方开发者尽可能方便地将模块提供给最终用户。
对于Python 3.x,分发模块的标准称为Distutils。
开发者使用Distutils的方法如下:
分发Python模块
终端用户使用Distutils的方法如下:
安装Python模块
通常,导航到下载的模块文件夹并在命令行中运行“setup.py install”就足够了。
但是
有时生活并不那么容易,您可能仍然在安装过程中遇到问题。您可能实际上需要其他东西。例如,您可能会遇到以下错误:“ImportError “No Module named Setuptools””
幸运的是,这个问题有解决办法:
Python 3: ImportError "No Module named Setuptools"
事实证明,并不是所有的东西都使用distutils。有些包使用setuptools。不幸的是,Python 3.x没有setuptools。而是使用distribute,它是setuptools的一个分支。
因此,对于使用Python 3.x的人,这是Distribute:Distribute
对于使用Python 2.x的人,这是Setuptools:Setuptools
在Distribute的安装说明中,它说:
"Download
distribute_setup.py
_
and execute it, using the Python interpreter of your choice."
它还说:“Notice this file is also provided in the source release.”
所以我下载了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, insys.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:
好吧,这就不好了!我真的不知道该错误来自何处或为什么会发生。
不管怎样,然后我找到了以下网站,运行了一个.exe文件来安装distribute以及pip。
Install Distribute
Install Pip
所以我安装了它们,然后使用以下网站设置了计算机,以便更轻松地使用easy_install:Setting Up Easy Install Made Easy
一旦我搞定了这个,然后我安装了nose:Nose
我之所以安装nose,是因为Winpexpect网站上说:
"WinPexpect包含单元测试。使用以下命令运行测试:
$ python setup.py test"
嗯,听起来不错:)。现在我只希望我知道在哪里运行那个测试。我知道如果手动安装,您将使用setup.py install命令,因此在线上肯定会有一个setup.py。 为了验证这一点,我下载并保存了winpexpect文件,提取了信息,通过命令行导航到它,并运行了setup.py test。
以下是结果:
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, inuse_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 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 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的无效语法?
print "%s version %s" % (os.path.basename(sys.argv[0]), version)...
应该在它周围加上括号... 这让我怀疑nose是否实际上在这里工作,因为它显然看起来是为早期版本的Python制作的。
问题:从命令行界面读取输入并使用Python提供答案的原因是什么?如何解决?
原因:在命令行界面中,需要通过Python编写代码来读取用户的输入,并根据输入提供相应的答案。这个问题的原因可能是需要与其他进程进行交互,或者需要自动化执行命令行任务。
解决方法:可以使用Python的subprocess模块来实现从命令行读取输入和提供答案的功能。首先,可以使用subprocess.Popen()方法创建一个子进程对象。然后,可以使用communicate()方法向子进程发送指令并获取输出结果。另一种方法是解析子进程的stdout,并将答案写入子进程的stdin。需要注意的是,这种方法可能会导致死锁,因此在使用时需要小心。
下面是一个示例代码:
import subprocess import os p = subprocess.Popen('xx viewproject', shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True) newline = os.linesep commands = ['y', 'n', 'y', 'n', 'y'] p.communicate(newline.join(commands))
另外,如果不需要使用Shell调用,可以将Popen()方法的shell参数设为False,将命令以列表形式传递给Popen()方法:
p = subprocess.Popen(['si', 'viewproject', '--project=d:/Projects/test.pj'], shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
如果在使用communicate()方法时出现了'TypeError: 'str' does not support the buffer interface'的错误,可以在Popen()方法中指定universal_newlines=True,以使用Unicode字符串代替字节。
通过使用Python的subprocess模块,可以实现从命令行读取输入并提供答案的功能。可以使用communicate()方法发送指令并获取输出结果,也可以解析stdout并向stdin写入答案。需要注意的是,使用这些方法时要注意可能出现的死锁问题。
从上面的内容可以看出,问题的原因是用户需要使用Python从命令行界面(CLI)读取输入并提供答案。然而,用户尝试了两种方法,但都遇到了问题。第一种方法是使用subprocess模块直接读取命令行输出,但代码卡在Popen语句之后,无法进入循环。第二种方法是使用winpexpect模块,但出现了TIMEOUT错误。
为了解决这个问题,可以尝试以下方法:
1. 对于第一种方法,可以尝试使用subprocess模块的communicate()方法来替代循环读取输出的方式。这样可以避免卡在Popen语句的问题。代码示例:
from subprocess import Popen, PIPE p = Popen(["xx", "viewproject"], stdin=PIPE, stdout=PIPE, universal_newlines=True) output, _ = p.communicate()
2. 对于第二种方法,可以尝试调整正则表达式,确保能够正确匹配命令行输出。另外,可以使用print语句输出p.before来检查是否能够正确读取子进程的输出。代码示例:
import re from winpexpect import winspawn as spawn p = spawn('xx viewproject') patterns = ['the project:', re.escape('? [ynYN](n)'), winpexpect.TIMEOUT] for found in iter(p.expect, 2): # until TIMEOUT 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) else: print(p.before) # debug output
通过调整代码和尝试不同的方法,可以解决从CLI读取输入并使用Python提供答案的问题。