如何通过Python子进程与Mac上的应用程序交互?
我知道已经有类似的问题被提出来了,但我看到的方法都没有用。我想在Mac上用Python的subprocess模块启动一个叫xfoil的应用,并通过一个脚本给xfoil发送一堆命令(xfoil是一个在终端窗口中运行的应用,你需要通过文本命令和它互动)。我能用脚本启动xfoil,但就是不知道怎么给它发送命令。这是我现在尝试的代码:
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')
我也尝试过
xfoil.stdin.write('NACA 0012\n')
来给xfoil发送命令。
1 个回答
根据手册页面的说明,
open命令的作用是打开一个文件(或者文件夹或网址),就像你双击文件图标一样。
最终,应用程序是由LaunchServices启动的,但这并不重要——重要的是,它并不是你命令行或Python脚本的子进程。
而且,open
的主要目的是直接打开应用程序,所以你不需要去找它的Unix可执行文件。如果你已经有那个文件,并且想以Unix可执行文件的方式运行它……只需运行它即可:
xfoil = sp.Popen(['/Applications/Xfoil.app/Contents/MacOS/Xfoil'], stdin=sp.PIPE, stdout=sp.PIPE)
实际上,在这种情况下,MacOS/Xfoil
甚至不是正确的程序;它显然是某种围绕Resources/xfoil
的包装器,而这个Resources/xfoil
才是相当于Linux上/usr/local/bin/xfoil
的实际文件。所以你应该这样做:
xfoil = sp.Popen(['/Applications/Xfoil.app/Contents/Resouces/xfoil'], stdin=sp.PIPE, stdout=sp.PIPE)
(另外,从技术上讲,你的命令行其实根本不应该工作;-a
指定的是一个应用程序,而不是Unix可执行文件,并且你应该至少传递一个文件来打开。但因为LaunchServices可以像启动应用程序一样启动Unix可执行文件,而open
并没有检查参数是否有效,所以open -a /Applications/Xfoil.app/Contents/MacOS/Xfoil
实际上和open /Applications/Xfoil.app/Contents/MacOS/Xfoil
做的事情是一样的。)
为了方便未来的读者,我会在这里加入一些评论中的信息:
如果你只是向stdin
写一行,然后从函数返回或脚本结束等,Popen
对象会被垃圾回收,这样它的两个管道都会关闭。如果xfoil
还没有运行完,下次它尝试写输出时就会出错,显然它会通过打印Fortran runtime error: end of file
(可能是到标准错误输出?)来处理这个问题。你需要调用xfoil.wait()
(或者其他隐式调用wait
的方式)来防止这种情况发生。