0

I'm very new to python and its subprocess module, but I'm trying to figure out how to get a command to run within a subprocess. Specifically, I'm running Magic Set Editor 2 in raw commandline interface mode (http://magicseteditor.sourceforge.net/doc/cli/cli) and I want a script that can take that and export a bunch of images from it. Doing this in cmd.exe through the interactive CLI is trivial:

mse --cli setName.mse-set
//entered the interactive CLI now.
> write_image_file(file:set.cards[cardNumber].name+".png", set.cards[cardNumber]

and that writes a png to my folder, so far so good. I can pull off the same thing with the raw commandline:

mse --cli setName.mse-set --raw
//entered the raw CLI now.
write_image_file(file:set.cards[cardNumber].name+".png", set.cards[cardNumber]

again, achieves a png and all that. Now the trick is, how do I get a python script to do the same thing? My current script looks like:

import subprocess

s = subprocess.Popen("mse --cli setName.mse-set --raw",shell=True)
s.communicate("write_image_file(file:set.cards[1].name+'.png',set.cards[1]")

I have shell=True because in cmd.exe it seems to open a new shell, but when I run this script it simply opens that shell and doesn't seem to run the second line, it sits there waiting for my input, which I want the script to provide.

I found an alternate method but it still doesn't quite click for me:

from subprocess import Popen, PIPE

s = Popen("mse --cli setName.mse-set --raw",stdin=PIPE)
s.communicate("write_image_file(file:set.cards[1].name+'.png',set.cards[1]")

...because I get the errors:

Traceback (most recent call last):
  File "cardMaker.py", line 6, in <module>
    s.communicate("write_image_file(file:set.cards[1].name+'.png',set.cards[1]")
  File "C:\Python34\lib\subprocess.py", line 941, in communicate
    self.stdin.write(input)
TypeError: 'str' does not support the buffer interface

edit: After solving the initial problem, I have another question; how do I send it multiple commands? Another s.communicate line with the same command failed with the error: Cannot send input after starting communication.

0

1 Answer 1

2

From the documentation of subprocess -

Popen.communicate(input=None, timeout=None)

Interact with process: Send data to stdin. Read data from stdout and stderr, until end-of-file is reached. Wait for process to terminate. The optional input argument should be data to be sent to the child process, or None, if no data should be sent to the child. The type of input must be bytes or, if universal_newlines was True, a string.

(Emphasis mine)

You need to send in a byte-string as input to the communicate() method , Example -

from subprocess import Popen, PIPE

s = Popen("mse --cli setName.mse-set --raw",stdin=PIPE)
s.communicate(b"write_image_file(file:set.cards[1].name+'.png',set.cards[1]")

Also , you should send the command to run as a list , Example -

from subprocess import Popen, PIPE

s = Popen(['mse', '--cli', 'setName.mse-set', '--raw'],stdin=PIPE)
s.communicate(b"write_image_file(file:set.cards[1].name+'.png',set.cards[1]")
Sign up to request clarification or add additional context in comments.

5 Comments

Ah, that's what I was missing, thanks. It's not quite behaving as I expected it to (it's not exporting the png), so I want to see the return value that the command usually outputs; is there a way of seeing that?
yes use s.returncode . Also, communicate returns a tuple , the first element of which is stdout` and the second stderr , you can analyze them as well. But for that when creating your Popen you need to set stdout and stderr arguments to PIPE .
OK, s.returncode gives me 0, which is the first of three lines this program is meant to return. I set stdout=PIPE and tried print(s.stdout) but I get <_io.BufferedReader name=4> which isn't what it should be, how do I access the stdout?
Like I said, communicate returns a tuple, the first element in it is the stdout . Access it as - returned = s.communicate(b"write_image_file(file:set.cards[1].name+'.png',set.cards[1]")[0].
Ahh, got it now, thanks. The stdout helped me figure out an error in the command so now it all works. I tried adding a second s.communicate after the line with returned, but it tells me Cannot send input after starting communication, how do I do multiple commands?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.