How to handle an executable requiring interactive

2019-07-28 17:30发布

问题:

I have a executable, called tsfoil2.exe, and I want to operate this .exe from my python environment. I'm running Python 2.7.3, with Spyder 2.1.11 on Windows 7.

In order to operate the .exe, it requires some input, the default hard drive ('I:\'), a name for the outputfile ('test'), and a name for the input file ('SC20610.inp').

One of my colleagues advised me to use os.system, and supply this with a temporary input file, that contains all the arguments.

f = open('temp', 'w') 
f.write('I:\ \n') 
f.write('test \n')
f.write('SC20610.inp\n')  
f.close() 

I then supply this file with arguments to the .exe in the following way:

os.system("tsfoil2.exe < temp")

This all works, but the program requires a 'ENTER' to close. For some reason, the .exe is repeatedly asking to 'Press the ENTER key to exit'. Even, when I press the enter key in my Spyder-console, the program does not terminate.

Is there a way to give the 'ENTER' key as an interactive input to .exe? I've tried to use the SendKeys class, but as the program does not terminate, it does not reach the line of code that contains the SendKeys command. I've also tried to include it in the arguments-file, but this does not work either.

Furthermore I've also found out that it might be beneficial to switch to subprocesses command, as it might give me more command over the execution, but I haven't been able to run the executable with the input files.

Is it possible to provide the necessary 'ENTER' using os.system, or should I switch to subprocess, and if so, how can I construct a method similar to the os.system("tsfoil2.exe < temp") I'm using now.

I've tried this:

import subprocess as sub
f = open('temp', 'w') 
f.write('I:\ \n') 
f.write('test \n')
f.write('SC20610.inp\n')  
f.close() 
proc=sub.Popen(["tsfoil2.exe","temp"], shell=True)

and this

import subprocess as sub
p=sub.Popen('tsfoil2.exe')
p.communicate(input='I:' )

But, the program does not respond to the arguments given.

MWE:

import os
f = open('temp', 'w') 
f.write('I:\ \n') 
f.write('test \n')
f.write('SC20610.inp\n')  
f.close() 

os.system("tsfoil2.exe < temp")

Both the program can be found via http://www.dept.aoe.vt.edu/~mason/Mason_f/tsfoil2.exe, the input file can be found via http://www.dept.aoe.vt.edu/~mason/Mason_f/SC20610.inp.

I hope everything is clear, and you can help me out.

回答1:

'Press the ENTER key to exit' means that the programs expects a newline.

I see no blank line at the end of the temp file. Also, you might have meant 'I:\\\n' -- you need to use '\\' in a Python string literal if you want \ in the output.

The question is what tsfoil2.exe considers a newline e.g., b'\r\n' or just b'\n' and where it expects to receive it: from stdin (getchar()) or directly from console (getch()).

Assuming that the program expects b'\r\n' from stdin on Windows:

import os
from subprocess import CalledProcessError, Popen, PIPE

cmd = "tsfoil2.exe"
input_data = os.linesep.join(['I:\\', 'test', 'SC20610.inp', os.linesep])
p = Popen(cmd, stdin=PIPE, bufsize=0)
p.communicate(input_data.encode('ascii'))
if p.returncode != 0:
   raise CalledProcessError(p.returncode, cmd)

How it works

os.linesep == "\r\n" on Windows. "\n".join(["a", "b"]) == "a\nb".

Each process may have three standard streams: stdin, stdout, stderr. In Python, they are represented as sys.stdin, sys.stdout, sys.stderr. You can read input from stdin and write to stdout, stderr e.g., input function reads from stdin and print writes to stdout by default. stderr may be used to write error messages.

stdin=PIPE tells Popen to create a pipe between the parent process where it is called and the new child process ("tsfoil2.exe") and redirect the subprocess' stdin. p.communicate(data) writes data to p.stdin, closes it and waits for the child process to finish. p.returncode contains the exit status of the child process. Usually non-zero status means failure.

It emulates the shell pipeline without actually spawning the shell:

$ echo input data | tsfoil2.exe

If input is expected directly from console, you could try SendKeys module or its pure Python implementation SendKeys-ctypes:

from SendKeys import SendKeys

SendKeys(r"""
    {LWIN}
    {PAUSE .25}
    r
    C:\Path\To\tsfoil2.exe{ENTER}
    {PAUSE 1}
    I:\{ENTER}
    {PAUSE 1}
    test{ENTER}
    {PAUSE 1}
    SC20610.inp{ENTER}
    {PAUSE 1}
    {ENTER}
""")

I haven't tested it.