PATH="/custom/dir:$PATH" prepending instead of appending – Conda vs Pyenv

Question:

I have a Mac (latest software update) with Pyenv and Anaconda. I manage packages with Homebrew and have installed Python 3 with it. When I echo $PATH I get the following:

/Library/Frameworks/Python.framework/Versions/3.8/bin:
 /usr/local/bin:
 /usr/bin:
 /bin:
 /usr/local/sbin:
 /usr/sbin:
 /sbin:
 /Library/TeX/texbin:
 /Users/luca/.pyenv/versions/anaconda3-2020.02/condabin:
 /Users/luca/.pyenv/versions/3.8.5/bin:
 /Users/luca/.pyenv/bin
  1. I do not know what sets up /Library/Frameworks/Python.framework/Versions/3.8/bin: this directory is non-existent on my Mac. I had previously installed Python without Homebrew that is why the directory was created. I did remove that, but there is still something that exports that line in $PATH, but I cannot find it! Does someone have a guess? I did try and grep -r /* it, but that is too much of a search for my laptop to finish.

  2. I set up in my .zshenv the code for initialising pyenv and conda. Of course, I did write PATH="/dir/to/conda/bin:$PATH" and the same for pyenv (see code below). I do not understand why, but they end up at the end of $PATH. Does someone know why? Is it because of eval "$(pyenv init -)" being evaluated before conda?

# >>> pyenv initialize
export PYENV_SHELL=$SHELL
export PATH="/Users/luca/.pyenv/bin:$PATH"
export PATH="/Users/luca/.pyenv/versions/3.8.5/bin:$PATH" #export Python 3.8 bin directory
if command -v pyenv 1>/dev/null 2>&1; then
    eval "$(pyenv init -)"
fi
# <<< pyenv initialize <<<

# >>> conda initialize >>>
# !! Contents within this block are managed by 'conda init' !!
__conda_setup="$('/Users/luca/.pyenv/versions/anaconda3-2020.02/bin/conda' 'shell.zsh' 'hook' 2> /dev/null)"
if [ $? -eq 0 ]; then
    eval "$__conda_setup"
else
    if [ -f "/Users/luca/.pyenv/versions/anaconda3-2020.02/etc/profile.d/conda.sh" ]; then
        . "/Users/luca/.pyenv/versions/anaconda3-2020.02/etc/profile.d/conda.sh"
    else
        export PATH="/Users/luca/.pyenv/versions/anaconda3-2020.02/bin:$PATH"
    fi
fi
unset __conda_setup
# <<< conda initialize <<<
Asked By: baggiponte

||

Answers:

I have the exact same problem, and I could not find any other source on why the conda environment gets appended instead of prepended. I have added the following code to my .bash_profile (after conda initialisation) which flips the order of all path location. This is definitely a terrible practice, but it might be a quick fix for someone who is stuck on the same problem.

locations=($(echo $PATH | tr ":" "n"))
path=""

for (( idx=${#locations[@]}-2 ; idx>=0 ; idx-- )) ; do
    # Skip the last path location since this would put the general /Users/XXX/opt/anaconda3/condabin first
    # To not do this change the -2 to -1
    path="${path}:${locations[$idx]}"
done

path="${path:1}"
PATH=$path
export $PATH

As noted in the comment, I do not add the location "/Users/XXX/opt/anaconda3/condabin", since it would mess up my environment location, which is located second to last. Please examine your own path variable and decide accordingly.

EDIT: If anyone has a better solution, please let me know!

Answered By: lodewijk

TL;DR

In macOS, if you place any code that modifies $PATH on macOS inside .zshenv, it will be overridden. You should put it in .zshrc or somewhere else which is then sourced by your .zshrc.

Complete explanation

As answered on Unix & Linux Stack Exchange, when zsh is sourced, files are read in the following order:

etc/zshenv/ -> $ZDOTDIR/.zshenv/ -> etc/zprofile/ -> $ZDOTDIR/.zprofile/ -> etc/zshrc/ -> $ZDOTDIR/.zshrc/ -> etc/zlogin/ -> $ZDOTDIR/.zlogin/

In macOS’s /etc/zprofile, a script is sourced that overrides the $PATH according to the contents of these files:

/etc/paths
/etc/paths.d
/etc/manpaths
/etc/manpaths.d

So you should put not put any line that changes $PATH in your $ZDOTDIR/.zshenv, but rather in any file sourced after that (e.g. $ZDOTDIR/.zshrc). Do not edit files in /etc/!

Answered By: baggiponte
Categories: questions Tags: , , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.