How can I execute scripts from the system-wide Script menu with a normal environment? It seems the environment is not being setup at all.
Scripts that are run from the script menu can find and execute commands from the base system. However, user-installed utilities are not found on the path. Also,
/usr/bin/env foo is not finding commands that are installed outside of the base system.
The system Script menu appears at the right side of the menu bar. It is enabled via Script Editor.app > Preferences > Show Script menu in menu bar. It displays scripts located in
~/Library/Scripts and other system locations. The menu can execute AppleScript, JXA, bash, python and other scripts (use shebang as needed).
Investigation So Far
Scripts that are run from the Script menu don't inherit the user's bash environment. They know the shell is BASH and your USER name, but no initialization occurs. I ran a little script to dump the
env to a text file
~/Library/Scripts/dump_env.sh (be sure to make executable):
#!/bin/bash env > ~/env.txt ps -ef >> ~/env.txt
Here are the interesting entries.
USER=mat LOGNAME=mat SHELL=/bin/bash PATH=/usr/bin:/bin:/usr/sbin:/sbin PWD=/ HOME=/Users/mat _=/usr/bin/env
It is very vanilla. None of the custom paths are included. For example, the following are unavailable:
/usr/local/bin/svn # Subversion source control client /opt/local/bin/python3 # python3 installed via MacPorts ~/bin
The latter two are normally sourced in my .bash_profile.
/usr/local/bin/ is setup by
path_helper which is called by
/etc/profile (though apparently not for the Script menu).
I'm running macOS 10.13 High Sierra, but I'm interested whether others are getting different results.
- Avoid hard-coding path to executables in shebang. Ex: if I install a different python version / distribution, I want to use it in all my scripts.
/usr/bin/env python3accomplishes this, but it seems to depend on the PATH?
- Finds utilities in /usr/local/bin
- Specify PATH once for CLI and Script menu (and GUI?). If I install a new Python I would like it to be used everywhere.
- Specify PATH only for user-level processes. I don't want system processes using binaries from in my add-on path entries (especially user-permissioned path entries).
- Avoid breaking 3rd party builds/installers/programs that were written with assumptions about what is present on my macOS install.
I'm looking into
launchctl, which can be used to setup environment for GUI apps. Not sure if it still works or if it will work with the Script menu.
I added trace statements to all of the bash environment files:
~/.bashrc. These statements export environment variable so I can see that the file has been sourced. None of these are getting run for scripts run from the Script menu.
launchctl setenv KEY VALUE will set an environmental variable for all processes subsequently launched by launch services (launchd) in the user space.
This works for Terminal.app, GUI applications and scripts that are double-clicked in the Finder. However, it does not work for the Script menu.
ps -ef to the dump_env.sh script. This tells me the Script menu is not invoking bash with any arguments that would strip the environment (such as -r or -p). The parent process is
otool -tV /System/Library/Frameworks/Foundation.framework/Versions/C/XPCServices/com.apple.foundation.UserScriptService.xpc/Contents/MacOS/com.apple.foundation.UserScriptService.
…reveals a symbol named
__NSUserScriptTaskServiceStart. This sounds awfully similar to NSUserScriptTask in the CoreFoundation API. From the API doc:
The NSUserScriptTask class is able to run all the scripts normally run by the one of its subclasses…
I strongly suspect this is what the Script menu uses to execute scripts. The API doc does says nothing about the script's runtime environment.