我有一个GUI macOS应用程序,也可以从终端启动,带有可选的命令行参数 .
当使用参数启动时,我喜欢以“cmdline”模式运行应用程序,其中我不显示任何UI,而是仅通过stdin stdout进行通信 .
我可以像这样检测这个cmdline模式:
BOOL cmdMode = NSProcessInfo.processInfo.arguments.count > 1;
(arg 0始终是可执行文件的路径,因此任何更多的args都将手动传递args) .
现在,这是一个大问题:
如果用户在没有来自终端的参数的情况下调用我的应用程序(通过在Contents / MacOS中调用应用程序的可执行文件,即不通过 open
cmd),我也想进入cmdline模式 . 我怎么检测到这个?
注意:较旧的OS X版本确实传递了一个“-psn ...”参数,该参数在不存在时可用于检测cmdline的启动,但是最近的macOS版本似乎不再从此处启动应用程序时传递此参数Finder,所以我不能再使用它进行检测了 .
Update
我意识到通过检查某些环境变量的存在,我几乎可以正确地解决这个问题:
TERM
和 PWD
仅在从终端启动应用程序时设置,而不是从Finder启动 .
但是,我也希望能够分辨直接启动(在Contents / MacOS目录中执行)与使用 open
命令启动之间的区别,因为我认为open cmd相当于通过Finder或其他应用程序通过其他应用程序打开应用程序启动服务 .
简而言之,问题也可能是: Detect whether an app was launched by 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 回答
如果您想知道哪个进程已执行您的程序,您可以使用
getppid()
获取父进程ID,然后检查该进程以确定您是由交互式shell进程,Finder还是launchctl等执行的 ./sbin/launchd
是PID 1 - 如果进程的父PID为1,则由launchd执行 .否则,您被另一个进程执行 - 可能是一个交互式shell,或者作为另一个进程的子进程 . 您可以使用KERN_PROCARGS syscall with sysctl()通过其PID获取进程名称 .
您可能还需要考虑使用
isatty(STDIN)
:交互式shell具有TTY,非交互式shell,而其他进程则不会 .