I have been trying to create a python script which opens a exe
file,
enters an input, then reads an output, and based on the output that was received enter another input.
I've been trying to use Python's subprocess library, but the problem is that the communicate()
method can only be used once. so it is impossible to enter 2 inputs unless you enter them both in the communicate()
method, which doesn't work in this case. because the second input is based upon the output which is generated after the first input, so you can't enter both inputs at the same time.
Also, I searched for 3rd party libraries for python but I didn't find any good libraries for windows.
Can someone show me a way of performing this action using the subprocess library or suggest me a good library for windows?
Consider a simple app that has some basic execution control flow and just echoes its STDIN in reverse to the STDOUT - this can be any executable but we'll stick with Python for simplicity - say, app.py
:
#!/usr/bin/env python
import sys
sys.stdout.write("::BEGIN::\n") # tell our listener that we're listening...
sys.stdout.flush() # flush the STDOUT buffer
while True: # a simple event loop
line = sys.stdin.readline().rstrip() # read a line from STDIN
if line: # ignore empty lines
if line == "::END::": # just a convenient way to shut down the app
sys.stdout.write("::END::\n") # tell our listener that we're done
sys.stdout.flush() # flush the STDOUT buffer
break # we're finished here
sys.stdout.write(line[::-1]) # write the reversed line to STDOUT
sys.stdout.write("\n") # add a new line to the STDOUT
sys.stdout.flush() # flush the STDOUT buffer
Then if you want to open this app and communicate with it from your Python script all you need to do is control the subprocesses STDOUT and STDIN and you can do this indefinitely, for example:
import subprocess
# start our subprocess, forward its STDOUT and STDIN to the internal buffers
proc = subprocess.Popen(["python", "app.py"], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
# lets define our data to be sent one by one to our app.py, including ::END:: to exit...
items = ["test", "data", "to", "run", "sequentially", "::END::"]
# a convenience function to get the next item and write it to the passed buffer
def send_next_item(buf):
item = items.pop(0) # pop the first element from `items`
print("REQ: {}".format(item))
buf.write(item) # write it to the passed buffer
buf.write("\n") # write a new line to the passed buffer
buf.flush() # flush the passed buffer
while True: # wait for a prompt by our app
line = proc.stdout.readline().rstrip()
if line == "::BEGIN::":
print("BEGIN!")
send_next_item(proc.stdin) # send the first item to the processes' STDIN
elif line == "::END::":
print("END!")
break # nothing more to do
elif line: # ignore empty lines
print("RES: {}".format(line))
send_next_item(proc.stdin) # send the next item to the processes' STDIN
When you run this you'd get an output like:
BEGIN!
REQ: test
RES: tset
REQ: data
RES: atad
REQ: to
RES: ot
REQ: run
RES: nur
REQ: sequentially
RES: yllaitneuqes
REQ: ::END::
END!
Of course, you can do further processing to decide on how to properly respond to the called application's input request, this is just a basic example.