简体   繁体   English

在 Python 中运行交互式命令

[英]Running an interactive command from within Python

I have a script that I want to run from within Python (2.6.5) that follows the logic below:我有一个要在 Python (2.6.5) 中运行的脚本,它遵循以下逻辑:

  • Prompts the user for a password.提示用户输入密码。 It looks like ("Enter password: ") (*Note: Input does not echo to screen)它看起来像(“输入密码:”)(*注意:输入不会回显到屏幕)
  • Output irrelevant information输出无关信息
  • Prompt the user for a response ("Blah Blah filename.txt blah blah (Y/N)?: ")提示用户响应(“Blah Blah filename.txt blah blah (Y/N)?:”)

The last prompt line contains text which I need to parse (filename.txt).最后一个提示行包含我需要解析的文本(filename.txt)。 The response provided doesn't matter (the program could actually exit here without providing one, as long as I can parse the line).提供的响应无关紧要(只要我可以解析该行,程序实际上可以在不提供响应的情况下退出这里)。

My requirements are somewhat similar to Wrapping an interactive command line application in a Python script , but the responses there seem a bit confusing, and mine still hangs even when the OP mentions that it doesn't for him.我的要求有点类似于Wrapping an interactive command line application in a Python script ,但那里的响应似乎有点令人困惑,即使 OP 提到它不适合他,我的要求仍然挂起。

Through looking around, I've come to the conclusion that subprocess is the best way of doing this, but I'm having a few issues.通过环顾四周,我得出结论, subprocess是执行此操作的最佳方式,但我遇到了一些问题。 Here is my Popen line:这是我的 Popen 行:

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.当我在stdout上调用read()readline()时,提示是屏幕上的打印机并且它挂起。

  • If I call a write("password\n") for stdin , the prompt is written to the screen and it hangs.如果我为stdin调用write("password\n") ,则提示将写入屏幕并挂起。 The text in write() is not written (I don't the cursor move the a new line). write()中的文本未写入(我没有将光标移动到新行)。

  • If I call p.communicate("password\n") , same behavior as write()如果我调用p.communicate("password\n") ,则行为与 write()

I was looking for a few ideas here on the best way to input to stdin and possibly how to parse the last line in the output if your feeling generous, though I could probably figure that out eventually.我在这里寻找一些关于输入stdin输入的最佳方式的想法,以及如果你觉得慷慨的话,可能如何解析输出中的最后一行,尽管我最终可能会弄清楚这一点。

If you are communicating with a program that subprocess spawns, you should check out A non-blocking read on a subprocess.PIPE in Python .如果您正在与子进程生成的程序进行通信,您应该查看Python 中 subprocess.PIPE 上的非阻塞读取 I had a similar problem with my application and found using queues to be the best way to do ongoing communication with a subprocess.我的应用程序遇到了类似的问题,发现使用队列是与子进程进行持续通信的最佳方式。

As for getting values from the user, you can always use the raw_input() builtin to get responses, and for passwords, try using the getpass module to get non-echoing passwords from your user.至于从用户那里获取值,您始终可以使用 raw_input() 内置函数来获取响应,对于密码,请尝试使用getpass模块从用户那里获取非回显密码。 You can then parse those responses and write them to your subprocess' stdin.然后,您可以解析这些响应并将它们写入您的子进程的标准输入。

I ended up doing something akin to the following:我最终做了类似于以下的事情:

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)

Once you have the queues made and the threads started, you can loop through getting input from the user, getting errors and output from the process, and processing and displaying them to the user.一旦创建了队列并启动了线程,就可以循环获取用户的输入、进程的错误和输出,以及处理并将它们显示给用户。

Using threading it might be slightly overkill for simple tasks.对于简单的任务,使用线程可能有点过分。 Instead os.spawnvpe can be used.相反,可以使用 os.spawnvpe。 It will spawn script shell as a process.它将作为进程生成脚本外壳。 You will be able to communicate interactively with the script.您将能够与脚本进行交互通信。 In this example I passed password as an argument, obviously it is a not good idea.在此示例中,我将密码作为参数传递,显然这不是一个好主意。

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

If you just want a user to enter a password without it being echoed to the screen just use the standard library's getpass module:如果您只想让用户输入密码而不将其回显到屏幕上,只需使用标准库的getpass模块:

import getpass
print("You entered:", getpass.getpass())

NOTE:The prompt for this function defaults to "Password: " also this will only work on command lines where echoing can be controlled.注意:此功能的提示默认为“密码:”,这也仅适用于可以控制回显的命令行。 So if it doesn't work try running it from terminal.因此,如果它不起作用,请尝试从终端运行它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM