如何通过Python子进程与Mac上的应用程序交互?

2 投票
1 回答
3136 浏览
提问于 2025-04-17 16:40

我知道已经有类似的问题被提出来了,但我看到的方法都没有用。我想在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 个回答

3

根据手册页面的说明,

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的方式)来防止这种情况发生。

撰写回答