【问题标题】:Detect whether macOS app was launched from the command line (Terminal)检测 macOS 应用程序是否从命令行(终端)启动
【发布时间】:2019-03-12 06:28:47
【问题描述】:

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

当使用参数启动时,我喜欢在“命令行”模式下运行应用程序,在这种模式下我不显示任何 UI,而是仅通过 stdin + stdout 进行通信。

我可以像这样检测这种命令行模式:

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

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

现在,这是一个大问题:

如果用户从终端调用我的应用程序没有参数(通过在Contents/MacOS中调用应用程序的可执行文件,即不通过open cmd),我喜欢也进入命令行模式。如何检测到这一点?

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

更新

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

TERMPWD 仅在从终端启动应用程序时设置,而不是从 Finder 启动。

但是,我也希望能够区分直接启动(可在 Contents/MacOS 目录中执行)与使用 open 命令启动之间的区别,因为我认为打开 cmd 等同于通过 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 中双击时)没有独特的环境值。

【问题讨论】:

  • 这个问题在这里绝对是话题,所以我的评论并不代表结束这个问题的抱怨或论据,但值得指出的是,Ask Different 上有潜在的专家库这可能会有所帮助。
  • 我不认为我会在 A D 上得到答案,因为那是针对用户的,而我在问程序员的问题。
  • 如前所述,我不会投票关闭/迁移,因为您的问题是这里的主题,但看看 AskDifferent,他们似乎也有 MacOS/iOS 特定的编程问题。但是很有可能那些回答编程问题的人也会在这里参与。

标签: macos cmdline-args


【解决方案1】:

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

/sbin/launchd 是 PID 1 - 如果您的进程的父 PID 是 1,那么您是由 launchd 执行的。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-04-03
    • 1970-01-01
    • 2021-08-04
    • 2010-09-29
    • 1970-01-01
    • 2011-04-26
    • 2017-06-05
    • 1970-01-01
    相关资源
    最近更新 更多