I have a GUI macOS app that may also be launched from Terminal, with optional command line arguments.
When launched with arguments, I like to run the app in a "cmdline" mode where I do not display any UI but instead communicate via stdin + stdout only.
I can detect this cmdline mode like this:
BOOL cmdMode = NSProcessInfo.processInfo.arguments.count > 1;
(arg 0 is always the executable's path, so any more args would be manually passed args).
Now, here's the big question:
If the user invokes my app without arguments from Terminal (by invoking the app's executable in Contents/MacOS, i.e. not via the open
cmd), I like to also go into the cmdline mode. How do I detect this?
Note: Older OS X versions did pass a "-psn ..." argument that, when not present, could be used to detect a launch from cmdline, but recent macOS versions seem to not pass this argument any more when launching apps from the Finder, so I cannot use that for detection any more.
Update
I realize that I can almost correctly solve this by checking for the presence of certain environment variables:
TERM
and PWD
are only set when launching the app from Terminal but not from Finder.
However, I also like to be able to tell the difference between being launched directly (executable in Contents/MacOS dir) vs. launched with the open
command as I consider the open cmd being equivalent to opening the app via Finder or from another app via Launch Services.
In short, the question might also be: Detect whether an app was launched by Launch Services
For the record, here are the values from environ()
. The ones marked with an asterisk are only present when invoked from Terminal.app but not when lanched from 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
There are, however, no envinment values that are unique to apps launched with Launch Services (such as when double clicked in Finder).
If you want to know what process has executed your program, you could use getppid()
to get the parent process ID, then inspect that process to determine whether you were executed by an interactive shell process, or Finder, or launchctl, etc.
/sbin/launchd
is PID 1 - if your process's parent PID is 1, you were executed by launchd.
Otherwise, you were executed by another process - probably an interactive shell, or as a subprocess of another process. You can use the KERN_PROCARGS
syscall with sysctl()
to get the process name by its PID.
You might also want to consider using isatty(STDIN)
as well: interactive shells have a TTY, non-interactive shells and other processes won't.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With