2

I know there are similar questions posted already, but non of the methods I have seen seems to work. I want to launch the application xfoil, on mac, with python subprocess, and send xfoil a bunch of commands with a script (xfoil is an application that runs in a terminal window and you interact with it through text commands). I am able to launch xfoil with the script, but I can't seem to find out how to send commands to it. This is the code I am currently trying:

import subprocess as sp

xfoil = sp.Popen(['open', '-a', '/Applications/Xfoil.app/Contents/MacOS/Xfoil'], stdin=sp.PIPE, stdout=sp.PIPE)

stdout_data = xfoil.communicate(input='NACA 0012')

I have also tried

xfoil.stdin.write('NACA 0012\n')

in order to send commands to xfoil.

user1143657
  • 185
  • 4
  • 13
  • Have you tried `subprocess.call()` ? – Henrik Andersson Feb 21 '13 at 19:33
  • Google says top hit for ``xfoil subprocess`` [using-xfoil-and-automating-via-python-subprocess-module](http://hakantiftikci.wordpress.com/2010/12/21/using-xfoil-and-automating-via-python-subprocess-module/) – sotapme Feb 21 '13 at 19:40
  • @sotapme: That doesn't really help the OP, because it assumes that he already knows how to `Popen` the xfoil executable (and the example of doing so is for Windows), which is exactly what he doesn't know how to do. – abarnert Feb 21 '13 at 20:05
  • 1
    I see - well there exists ``/Applications/Xfoil.app/Contents/Resources/xfoil`` which I guess is the underlying smarts but I presume the whole point of Xfoil is for the gui. Sending that input does get a response from ``xfoil`` but just as much as driving it from the cli. – sotapme Feb 21 '13 at 20:41
  • @sotapme Yeah, I have seen the link you are talking about (I did try google first:) but I cant get it to work. – user1143657 Feb 21 '13 at 20:46
  • By the way, +1 to @sotapme for discovering the "normal" executable that `MacOS/Xfoil` is a wrapper around. This is pretty common for Mac ports of command-line-and-X programs, and I should have thought of it myself, but didn't. – abarnert Feb 21 '13 at 21:24

1 Answers1

3

As the man page says,

The open command opens a file (or a directory or URL), just as if you had double-clicked the file's icon.

Ultimately, the application gets started by LaunchServices, but that's not important—what's important is that it's not a child of your shell, or Python script.

Also, the whole point of open is to open the app itself, so you don't have to dig into it and find the Unix executable file. If you already have that, and want to run it as a Unix executable… just run it:

xfoil = sp.Popen(['/Applications/Xfoil.app/Contents/MacOS/Xfoil'], stdin=sp.PIPE, stdout=sp.PIPE)

As it turns out, in this case, MacOS/Xfoil isn't even the right program; it's apparently some kind of wrapper around Resources/xfoil, which is the actual equivalent to what you get as /usr/local/bin/xfoil on linux. So you want to do this:

xfoil = sp.Popen(['/Applications/Xfoil.app/Contents/Resouces/xfoil'], stdin=sp.PIPE, stdout=sp.PIPE)

(Also, technically, your command line shouldn't even work at all; the -a specifies an application, not a Unix executable, and you're supposed to pass at least one file to open. But because LaunchServices can launch Unix executables as if they were applications, and open doesn't check that the arguments are valid, open -a /Applications/Xfoil.app/Contents/MacOS/Xfoil ends up doing effectively the same thing as open /Applications/Xfoil.app/Contents/MacOS/Xfoil.)


For the benefit of future readers, I'll include this information from the comments:

If you just write a line to stdin and then return from the function/fall off the end of the main script/etc., the Popen object will get garbage collected, closing both of its pipes. If xfoil hasn't finished running yet, it will get an error the next time it tries to write any output, and apparently it handles this by printing Fortran runtime error: end of file (to stderr?) and bailing. You need to call xfoil.wait() (or something else that implicitly waits) to prevent this from happening.

abarnert
  • 354,177
  • 51
  • 601
  • 671
  • Ah, that gives me some explanation, but it still does not work :S The communicate() and stdin.write() dosen't do anything. The application launches with your method as well, but thats it. Am I sending commands to it in the wrong way maybe? :S – user1143657 Feb 21 '13 at 20:44
  • It's possible that the `Xfoil` executable inside `Xfoil.app` isn't actually meant to be runnable from the command line. Try just running it from your bash shell in the Terminal and see if you can interact with it. If this doesn't work, `subprocess` won't work. You'll either need to use some other mechanism to control it, or you'll need to get a copy of xfoil built differently. – abarnert Feb 21 '13 at 20:59
  • Or… given one of sotapme's comments, it may be that you already _do_ have a usable executable, but it's `/Applications/Xfoil.app/Contents/Resources/xfoil`, and `/Applications/Xfoil.app/Contents/MacOS/Xfoil` is a wrapper around that, and it's just a matter of running the former rather than the latter. Again, try it from Terminal.app and see. – abarnert Feb 21 '13 at 21:01
  • I can interact with it, but it is comming up in separate terminal window. Does that have anything to do with it? Should it come up in the same terminal window for it to work properly? – user1143657 Feb 21 '13 at 21:02
  • Did you try the `Resources/xfoil`? As I explained in my last comment, `MacOS/Xfoil` may be a wrapper that, e.g., fires up a new terminal window and then runs `Resources/xfoil` in it. (That's just an example—a wrapper could instead, e.g., fire off `Resources/xfoil` as a subprocess, and connect its input and output with a terminal window explicitly.) – abarnert Feb 21 '13 at 21:03
  • Ok, so heres a new problem: when I write /Applications/Xfoil.app/Contents/Resources/xfoil in the terminal app, and press enter, I get the program running, in the same terminal window. If I use the script, and put the same path inside sp.Popen([..], stdin=..etc), I get Fortran runtime error: end of file and then nothing :S – user1143657 Feb 21 '13 at 21:08
  • Oh, apparently, if I write xfoil.wait() at the end it works :D Now I can send commands, and get values from the program! Thanks alot! – user1143657 Feb 21 '13 at 21:18
  • Well, the new problem is that you're sending it one line of input and then closing the pipe. It's as if you typed `NACA 0012` then a newline and a ^D in the terminal window. Are you trying to communicate with an interactive command-line shell type program? If so, you either need to do a sequence multiple `stdin.write` and `stdout.read` calls (which can get very tricky because of blocking issues), or use something like `pexpect`. – abarnert Feb 21 '13 at 21:18
  • Oops, never mind that last comment. The problem is much simpler, and you figured out the solution yourself. You're apparently implicitly closing the `Popen` object (by returning from the function where it's defined and letting it get garbage collected) before it's done, which closes both pipes, which probably causes `xfoil` to get an error when trying to write to stdout. – abarnert Feb 21 '13 at 21:20