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

Tho*_*ann 7 macos cmdline-args

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

当使用参数启动时,我喜欢以"cmdline"模式运行应用程序,其中我不显示任何UI,而是仅通过stdin + stdout进行通信.

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

BOOL cmdMode = NSProcessInfo.processInfo.arguments.count > 1;
Run Code Online (Sandbox Code Playgroud)

(arg 0始终是可执行文件的路径,因此任何更多的args都将手动传递args).

现在,这是一个大问题:

如果用户在没有来自终端的参数的情况下调用我的应用程序(通过在Contents/MacOS中调用应用程序的可执行文件,即不通过opencmd),我也想进入cmdline模式.我怎么检测到这个?

注意:较旧的OS X版本确实传递了一个"-psn ..."参数,该参数在不存在时可用于检测cmdline的启动,但是当从这个版本启动应用程序时,最近的macOS版本似乎不再传递此参数Finder,所以我不能再用它来检测了.

更新

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

TERM并且PWD仅在从终端启动应用程序时设置,而不是从Finder启动.

但是,我也希望能够分辨直接启动(在Contents/MacOS dir中执行)与使用open命令启动之间的区别,因为我认为open cmd相当于通过Finder或其他应用程序通过Launch打开应用程序服务.

简而言之,问题可能还有:检测启动服务是否启动了应用程序


为了记录,这里是来自的值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
Run Code Online (Sandbox Code Playgroud)

但是,使用Launch Services启动的应用程序(例如在Finder中双击时)没有任何独特的值.

dos*_*ssy 1

如果你想知道哪个进程执行了你的程序,你可以使用getppid()获取父进程ID,然后检查该进程以确定你是否是由交互式shell进程、Finder或launchctl等执行的。

/sbin/launchdis PID 1 - 如果您的进程的父进程 PID 为 1,则您将被 launchd 执行。

否则,您将被另一个进程执行 - 可能是交互式 shell,或者作为另一个进程的子进程。您可以使用KERN_PROCARGS系统调用sysctl()通过 PID 来获取进程名称。

您可能还想考虑使用isatty(fileno(stdin)):交互式 shell 有 TTY,非交互式 shell 和其他进程则没有。