在 Python 中运行交互式命令
Posted
技术标签:
【中文标题】在 Python 中运行交互式命令【英文标题】:Running an interactive command from within Python 【发布时间】:2012-07-12 13:30:56 【问题描述】:我想在 Python (2.6.5) 中运行一个脚本,该脚本遵循以下逻辑:
提示用户输入密码。它看起来像(“输入密码:”)(*注意:输入不会回显到屏幕) 输出无关信息 提示用户回复(“Blah Blah filename.txt blah blah (Y/N)?:”)最后一行包含我需要解析的文本(filename.txt)。提供的响应无关紧要(只要我可以解析该行,程序实际上可以在不提供响应的情况下退出这里)。
我的要求有点类似于 Wrapping an interactive command line application in a Python script,但那里的响应似乎有点令人困惑,即使 OP 提到它没有,我的要求仍然挂起给他。
通过环顾四周,我得出结论,subprocess
是执行此操作的最佳方式,但我遇到了一些问题。这是我的 Popen 行:
p = subprocess.Popen("cmd", shell=True, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, stdin=subprocess.PIPE)
当我在stdout
上调用read()
或readline()
时,提示是打印机打印到屏幕上并且它挂起。
如果我为stdin
调用write("password\n")
,则会将提示写入屏幕并挂起。 write()
中的文字没有写(我没有把光标移到新的一行)。
如果我调用 p.communicate("password\n")
,与 write() 的行为相同
我在这里寻找一些关于输入stdin
的最佳方式的想法,以及如果您觉得大方的话,可能如何解析输出中的最后一行,尽管我最终可能会弄清楚。
【问题讨论】:
你应该看看pexpect:noah.org/wiki/pexpect 我认为你需要写入标准输出并从标准输入读取......而不是像你上面所说的那样 @Joran 哈哈是的,对不起。这就是我的意思。 @ColinDunklau 我希望尽量减少使用外部模块 如果有人想在现代 Python 中做到这一点,我已经在这里发布了明确的答案:***.com/a/56051270/240515 【参考方案1】:如果您正在与产生子进程的程序进行通信,您应该查看 A non-blocking read on a subprocess.PIPE in Python。我的应用程序遇到了类似的问题,发现使用 queues 是与子进程进行持续通信的最佳方式。
至于从用户那里获取值,您始终可以使用 raw_input() 内置函数来获取响应,对于密码,请尝试使用 getpass
模块从您的用户那里获取非回显密码。然后,您可以解析这些响应并将它们写入您的子进程的标准输入。
我最终做了类似于以下的事情:
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)
一旦创建了队列并启动了线程,就可以循环获取用户的输入、进程的错误和输出,以及处理并将它们显示给用户。
【讨论】:
【参考方案2】:对于简单的任务,使用线程可能有点过分。 相反,可以使用 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
【讨论】:
以上是关于在 Python 中运行交互式命令的主要内容,如果未能解决你的问题,请参考以下文章