A repository to investigate how the environment is setup when opening a shell
There is quiet some discussion about how the PATH environmental variable is configured when opening a shell in MacOS:
- An explanation about how PATH is constructed, and a warning about
path_helper - A very long discussion about how
path_helperis broken - An explanation about how
path_helperworks
Thus, I decided to test it by myself to understand what is going on.
Depending on how you open a shell, the files that will be sourced change:
interactive + login: the shell opened when launching the terminal appnon-interactive + login: the shell open when executing$SHELL --login -c '<command>'. Most commonly used from inside scripts, when running a command is required.non-interactive + non-login: the shell open when executing$SHELL -c '<command>'. Most commonly used from inside scripts, when running a command is required.
I created a script that adds the line export SOURCE_ORIGINS="<fileName>:$SOURCE_ORIGINS" to every file in the system suspected to be sourced when opening a shell. As a result, after running the script, when openning a new shell the environmental variable SOURCE_ORIGINS will contain which of the files where sourced, and in which order.
I am running the test using the following versions:
- MacOS Catalina 10.15.3 (19D76)
/bin/zsh(preinstalled with the OS): zsh 5.7.1 (x86_64-apple-darwin19.0)/bin/bash(preinstalled with the OS): GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin19)/usr/local/bin/bash(installed using brew): GNU bash, version 5.0.16(1)-release (x86_64-apple-darwin19.3.0)
The * found in the data below indicates that the file contains a call to path_helper.
The results below are the output of the analyzer.sh script.
For /bin/zsh:
interactive_login: $HOME/.zlogin:/etc/zlogin:$HOME/.zshrc:/etc/zshrc:$HOME/.zprofile:/etc/zprofile*:$HOME/.zshenv:/etc/zshenv:
non_interactive_login: $HOME/.zlogin:/etc/zlogin:$HOME/.zprofile:/etc/zprofile*:$HOME/.zshenv:/etc/zshenv:
non_interactive_non_login: $HOME/.zshenv:/etc/zshenv:
For /bin/bash and /usr/local/bin/bash:
interactive_login: $HOME/.bash_profile:/etc/profile*:/etc/bashrc:
non_interactive_login: $HOME/.bash_profile:/etc/profile*:
non_interactive_non_login:
Tables below show the files that are sourced:
- From top to bottom, where the top files are sourced first
- For the diferent shell types
| interactive + login | non-interactive + login | non-interactive + non-login |
|---|---|---|
| /etc/zshenv | /etc/zshenv | /etc/zshenv |
| $HOME/.zshenv | $HOME/.zshenv | $HOME/.zshenv |
| /etc/zprofile* | /etc/zprofile* | |
| $HOME/.zprofile | $HOME/.zprofile | |
| /etc/zshrc | /etc/zlogin | |
| $HOME/.zshrc | $HOME/.zlogin | |
| /etc/zlogin | ||
| $HOME/.zlogin |
| interactive + login | non-interactive + login | non-interactive + non-login |
|---|---|---|
| /etc/bashrc | /etc/profile* | |
| /etc/profile* | $HOME/.bash_profile | |
| $HOME/.bash_profile |
In MacOS there is a tool called path_helper that is used to construct the PATH environmental variable: it adds several paths to $PATH, removes duplicates and rearanges them. This last part is the problematic one: if you set your PATH inside the file $HOME/.zshenv and path_helper is executed after, the system paths will have priority over your custom-defined paths, most probably causing you all shorts of problems.
In the results above the files that contain a call to path_helper are marked with an asterisk (*).
After seeing the results, we can say that:
-
zshunder MacOS sources$HOME/.zshenvregardless of the type of shell. You could think that this is a safe place to customize the environment. The problem is that/etc/zprofileis sourced after for theinteractive + loginandnon-interactive + loginshell types, which will rearange yourPATHenvironmental variable and override any customization done in$HOME/.zshenv.One way to work around this issue is to setup the files as follows:
- Setup your enviornment inside
$HOME/.zshenv. - Inside
$HOME/.zprofile, write the following:
# Empty path PATH="" # Reset path with the default MacOS values [ -f /etc/zshenv ] && source /etc/zshenv [ -f /etc/zprofile ] && source /etc/zprofile # Customize the path [ -f $HOME/.zshenv ] && source $HOME/.zshenv
- Setup your enviornment inside
bashunder MacOS does not source$HOME/.bashrc. A quick search on google confirms this very strange fact, for example: Mac OS X .bashrc not working or OSX Terminal not recognizing ~/.bashrc and ~/.bash_profile on startup- When using
bashunder MacOS, changing the type of shell will most likely change the environment. It is not possible to customize the environment for all types of shells, as none of the files is sourced by all shell types. - The only way to customize the environment for a shell in
bashunder MacOS is to place it in$HOME/.bash_profile.
- There isn't an alternative to setup the environment for both
bashandzsh.