Python: Respond to Command Line Prompts

后端 未结 2 1713
自闭症患者
自闭症患者 2020-12-11 03:05

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. Initi

相关标签:
2条回答
  • 2020-12-11 03:15

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

    1 - os.linesep

    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.

    0 讨论(0)
  • 2020-12-11 03:29

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

    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 dict
            else:
                continue # skip it
            print(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)))
    
    0 讨论(0)
提交回复
热议问题