6

I am trying to communicate with a command-line chat bot with Python using the subprocess module. (http://howie.sourceforge.net/ using the compiled win32 binary, I have my reasons!)

This works:

proc = Popen('Howie/howie.exe', stdout=PIPE,stderr=STDOUT,stdin=PIPE)
output = proc.communicate()

But Popen.communicate waits for the process to terminate (and sends it EOF?), I want to be able to interact with it. The apparent solution for this was to read stdout / write stdin like so:

This doesn't work:

proc = Popen('Howie/howie.exe', stdout=PIPE,stderr=STDOUT,stdin=PIPE)
while True: print proc.stdout.readline()

(Note that I am actually using more complex code based on http://code.activestate.com/recipes/440554/ but the issue is the same.)

The problem is, the second approach works perfectly for communicating to cmd, but when I run the chatbot, nothing. So my question is, how is this different in capturing output to using Popen.communicate()?

i.e. I can use the second approach to use the command line as per normal, until I run the chatbot, at which point I stop receiving output. Using the first approach correctly displays the first few lines of output from the bot, but leaves me unable to interact with it.

SudoNhim
  • 580
  • 4
  • 17

2 Answers2

9

One major difference between the two is that communicate() closes stdin after sending the data. I don't know about your particular case, but in many cases this means that if a process is awaiting the end of the user input, he will get it when communicate() is used, and will never get it when the code blocks on read() or readline().

Try adding Popen.stdin.close() first and see if it affects your case.

vmalloc
  • 820
  • 5
  • 8
  • Why thank you, closing stdin allowed the chatbot to start exactly as `Popen.communicate` did, although now I have the issue that I need to be able to keep talking to it... – SudoNhim Mar 12 '12 at 20:52
  • Perhaps your application is reading line-by-line, in which case you can just send single lines to let it answer back? What is the exact task you're trying to achieve? – vmalloc Mar 12 '12 at 20:57
  • But how can I send it input once stdin has been closed? I want to be able to submit and receive line-by-line – SudoNhim Mar 12 '12 at 21:00
  • @SudoNhim: consider using [pexpect](http://www.noah.org/wiki/pexpect) instead of `subprocess` if you want bidirectional communication with a process. – Fred Foo Mar 12 '12 at 21:13
  • Thanks I will look into it. Just found a possible solution; sending the EOF character manually ('\x1a' in Python), has the same effect to the program as stdin, except of course it leaves stdin open. The program still spits the dummy at the EOF character however. – SudoNhim Mar 12 '12 at 21:29
3

If you want to interact with the program after sending the EOF, rather than using Popen.stdin.close(), you can manually send the command-line End Of File character, which has the same effect but leaves stdin open.

In Python this character's escape sequence is '\x1a'.

SudoNhim
  • 580
  • 4
  • 17
  • This only works in the strange world of DOS/Windows, which use a fixed `^Z` as the EOF marker. On many other systems this is usually `^D` (`\x04`) instead, however it can easily be changed and should not be relied upon and definitely never hardcoded anywhere. – ravilov Jul 15 '19 at 03:21