How do I interact with a console app as if I'm

2019-09-08 15:50发布

问题:

I'm trying to write a Python program that interacts with the bsdgames trek program. It's kind of like Zork with Klingons:

   * * *   S T A R   T R E K   * * *

Press return to continue.

What length game: short
What skill game: fair
Enter a password: hunter2
10 Klingons
2 starbases at 3,6, 0,2
It takes 400 units to kill a Klingon

Command: 

I'm currently trying to use subprocess.Popen() to interact with it:

>>> import subprocess
>>> t = subprocess.Popen('trek', stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

Unfortunately:

>>> t.communicate('')
('', None)
>>> t.poll()
-2
>>> t.communicate('')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/subprocess.py", line 754, in communicate
    return self._communicate(input)
  File "/usr/lib/python2.7/subprocess.py", line 1297, in _communicate
    self.stdin.flush()
ValueError: I/O operation on closed file

It ends itself as soon as I .communicate() with it. It seems to respond to my first .communicate():

>>> t = subprocess.Popen('trek', stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
>>> t.communicate('\n')
('\n   * * *   S T A R   T R E K   * * *\n\nPress return to continue.\nWhat length game: ', None)

But I need to be able to read stdout to be able to figure out what the next stdin should be. So how do I send stuff to stdin without doing whatever is telling trek that that's the end of its input?

EDIT: Someone suggested t.stdin.write(). This works, but now I can't find a way to read the results:

>>> t.stdin.write('\n')
>>> t.poll()
>>> t.stdout.read()

This hangs forever so:

^CTraceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyboardInterrupt
>>> t.stdout.readline()
''

So now what's going on?

回答1:

Assuming this is linux, Use the pexpect module. It's written to deal with interactive programs. Techniques like communicate() don't work because they wait for the program to exit. Simply reading doesn't work because the program hasn't flushed its stout so there is nothing to read. You can create your own pty and use that when calling Popen() or let pexpect do the work for you.