Skip to content Skip to sidebar Skip to footer

Python: Respond To Command Line Prompts

I am trying to use Python to interact with another program via the command line. The main problem I am having is a specific call that has multiple follow-up prompts. Initially th

Solution 1:

In the comments you mentioned that xx viewproject < answers.txt > output.txt works but you can't use it because answers depend on the output from the subprocess.

In general pexpect-like modules such as winpexpect (for Windows) could be used. Something like:

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 initer(partial(p.expect, patterns), 2): # until EOFif 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)

If the prompts are terminated with a newline (and the subprocess doesn't buffer them); you could read line by line using subprocess module directly:

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 dictelse:
            continue# skip itprint(answer, file=p.stdin) # provide answer
        p.stdin.flush()

To test that you can read something from the xx using subprocess:

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)))

Solution 2:

Yes, first of all you may create subprocess as an object by:

p = subprocess.Popen('xx viewproject', shell=True, stdin=subprocess.PIPE, 
                      stdout=subprocess.PIPE, universal_newlines=True)

Then you'll have methods like communicate() available, for instance:

newline = os.linesep # [1]
commands = ['y', 'n', 'y', 'n', 'y']
p.communicate( newline.join( commands))

Which will send all the answers at once (and hopefully it'll be enough) relying on the same order of question every time.

You may also try parsing p.stdout and then writing to p.stdin, but this may cause deadlock when one buffer will get full while waiting for another, so be careful with this. Luckily there are some complex examples on google.

Simple version would be:

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'))

I would also consider rewriting:

p = subprocess.Popen('si viewproject --project=d:/Projects/test.pj', shell=True, 
                      stdin=subprocess.PIPE, stdout=subprocess.PIPE) 

To:

p = subprocess.Popen( ['si', 'viewproject', '--project=d:/Projects/test.pj'],
                      shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE)

Unless you explicitly need Shell invocation.

Post a Comment for "Python: Respond To Command Line Prompts"