检测macOS应用程序是否是从命令行(终端)启动的

9

我有一个macOS的GUI应用程序,也可以从终端启动,并且带有可选的命令行参数。

当使用参数启动时,我想要运行程序在“cmdline”模式下,不显示任何UI,而是只通过stdin + stdout通信。

我可以像这样检测到这个cmdline模式:

BOOL cmdMode = NSProcessInfo.processInfo.arguments.count > 1;

现在,这是一个重要的问题:

如果用户从终端中不使用参数(通过在Contents/MacOS中调用应用程序的可执行文件,即非通过open命令),我希望也可以进入命令行模式。我该如何检测到这个情况?(arg 0 is always the executable's path,所以任何更多的args都将是手动传递的args)

注意:旧版的OS X版本确实会传递一个“-psn ...”参数,当不存在时,可以用于检测是否从命令行启动,但是最近的macOS版本似乎不再传递此参数,因此我不能再用它来进行检测。

更新

我意识到我几乎可以通过检查某些环境变量的存在来正确解决这个问题:

当从终端启动应用程序时,TERMPWD才被设置,但是从Finder启动则没有设置。

但是,我也希望能够区分直接启动(可执行文件在Contents/MacOS目录中)和使用open命令启动,因为我认为open命令相当于通过Launch Services从Finder或其他应用程序打开应用程序。

简而言之,问题也可能是:检测应用程序是否是由Launch Services启动的


以下是environ()中的值。带有星号的标记仅在从Terminal.app调用时存在,但不在从Finder启动时存在:

    __CF_USER_TEXT_ENCODING=0x1F5:0x0:0x0
*   _=/Applications/Myapp.app/Contents/MacOS/Myapp
    Apple_PubSub_Socket_Render=/private/tmp/com.apple.launchd.laVQnD7IXl/Render
    HOME=/Users/username
*   LANG=en_US.UTF-8
*   LC_ALL=en_US.UTF-8
*   LC_CTYPE=UTF-8
    LOGNAME=username
    PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
*   PWD=/Users/username
    SHELL=/bin/bash
*   SHLVL=1
    SSH_AUTH_SOCK=/private/tmp/com.apple.launchd.KeHv8KNuuk/Listeners
*   TERM_PROGRAM_VERSION=388.1.2
*   TERM_PROGRAM=Apple_Terminal
*   TERM_SESSION_ID=EF2C59E6-D661-45BE-B7EF-7A0E71158C8D
*   TERM=xterm-color
    TMPDIR=/var/folders/hm/ycnxcbwx8xl1v7008k8wnpjh0000gn/T/
    USER=username
    XPC_FLAGS=0x0
    XPC_SERVICE_NAME=0

然而,使用Launch Services启动的应用程序(例如在Finder中双击)没有独特的环境值。


1
这个问题在这里绝对是切题的,所以我的评论并不是抱怨或争论要关闭这个问题,但值得指出的是,在 [apple.se] 上可能有一些潜在的专家资源可以提供帮助。 - David Thomas
2
我认为我不会在A D上得到答案,因为那是给用户的,而我正在问一个程序员的问题。 - Thomas Tempelmann
正如所述,我不会投票关闭/迁移,因为您的问题在此处符合主题,但是看看AskDifferent,他们似乎也有针对MacOS/iOS特定编程问题的提问。但那里回答编程问题的人很有可能也会在这里参与讨论。 - David Thomas
1个回答

1
如果您想知道哪个进程执行了您的程序,可以使用getppid()获取父进程ID,然后检查该进程以确定是否由交互式shell进程、Finder或launchctl等执行。 /sbin/launchd是PID 1-如果您的进程的父PID是1,则由launchd执行您的进程。
否则,您将由另一个进程执行-可能是交互式shell,或作为另一个进程的子进程。您可以使用KERN_PROCARGS系统调用和sysctl()按其PID获取进程名称。
您还可以考虑使用isatty(STDIN):交互式shell具有TTY,非交互式shell和其他进程则没有。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接