Recently, I met a strange issue related to Tmux.
After open a tmux session, the PATH
variable is changed and is different from outside tmux.
This strange behavior breaks my Python:
- outside tmux:
python3
points to Python installed by Miniconda - inside tmux:
python3
points to Python3 installed by Apple
The cause#
The reason is that when you start a tmux session, it will start a shell (in the latest macOS, it is zsh by default) in login mode.
So certain zsh config files are sourced again (the culprit is /etc/zprofile
), which leads to messed up PATH variable.
This is also caused by macOS’s special way to construct the PATH
variable.
The content of /etc/zprofile
is:
if [ -x /usr/libexec/path_helper ]; then
eval `/usr/libexec/path_helper -s`
fi
When zsh is used as a login shell, the file /etc/zprofile
will be sourced.
So when we run tmux command and it initialize a zsh shell, this file is source again,
which messes up our PATH
variable due to the behavior of path_helper
.
In order to confirm this, we can do an experiment (credit here):
> PATH=BEG:$PATH:END
> echo $PATH
# output: BEG:/opt/homebrew/Caskroom/miniconda/base/bin:/opt/homebrew/Caskroom/miniconda/base/condabin:/Users/hao/.local/share/zinit/polaris/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:END
> source /etc/zprofile
> echo $PATH
# output: /usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:BEG:/opt/homebrew/Caskroom/miniconda/base/bin:/opt/homebrew/Caskroom/miniconda/base/condabin:/Users/hao/.local/share/zinit/polaris/bin:/opt/homebrew/bin:/opt/homebrew/sbin:END
What path_helper
does is to construct PATH from /etc/paths
and /etc/path.d
.
Append the current PATH variable to the constructed PATH and remove the duplicated items:
I think it is just looping through the item in the PATH variable and remove items that have appeared before.
This explains why unique item inside BEG:END
structure is kept and duplicate items are removed.
How to prevent#
One thing I see people do is to change /etc/zprofile
, adding one line to empty PATH
:
if [ -x /usr/libexec/path_helper ]; then
PATH="" #empty the PATH
eval `/usr/libexec/path_helper -s`
fi
For me, it is hacky. I do not want to change the system default settings.
Another method is to tell tmux to start a non-login shell instead via default-command
option:
# default command should be path to zsh executable on your system
set -g default-command /bin/zsh
If zsh is started as a non-login shell, /etc/zprofile
won’t be sourced, so PATH
is not changed.
To check if we are running a login shell or not (zsh-only), run the following (source here):
if [[ -o login ]]; then
print yes
else
print no
fi
After this setup, to make sure it work, run killall tmux
and exit your terminal and restart it again.
References#
- does tmux sort PATH variable? https://superuser.com/q/544989/736190
- prevent tmux from filling global PATH: https://stackoverflow.com/q/13058578/6064933
- configure tmux to use zsh: https://unix.stackexchange.com/q/214068/221410