Running an interactive command from within python

大兔子大兔子 提交于 2019-11-26 05:34:43

问题


I have a script that I want to run from within python (2.6.5) that follows the logic below:

  • Prompt user for password. Looks like (\"Enter password: \") (*Note: Input does not echo to screen)
  • Output irrelevant information
  • Prompt user for response (\"Blah Blah filename.txt blah blah (Y/N)?: \")

The last prompt line contains text which I need to parse (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.

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. Here is my Popen line:

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.

  • If I call a write(\"password\\n\") for stdin, the prompt is written to the screen and it hangs. The text in write() is not written (I don\'t the cursor move the a new line).

  • If I call p.communicate(\"password\\n\"), same behavior as 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.


回答1:


If you are communicating with a program that subprocess spawns, you should check out Non-blocking read on a subprocess.PIPE in python. 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. 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.




回答2:


Using threading it might be slightly overkill for simple tasks. Instead os.spawnvpe can be used. 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


来源:https://stackoverflow.com/questions/11457931/running-an-interactive-command-from-within-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!