Search code examples
zsh

Why does `which` print out a script?


which conda or which -a conda prints out the content of conda.sh, a script which chooses the conda executable and runs it, as (1) below.

I expect which to print the path of conda. That may or may not be this conda.sh, but in any case I don't expect to see the script's contents.

Simply running conda correctly prints out conda's help text, as (2) below.

The PATH correctly includes conda's directory at the beginning, as (3).

This is zsh on Mac.

(1)

conda () {
    if [ "$#" -lt 1 ]
    then
        "$CONDA_EXE" $_CE_M $_CE_CONDA
    else
        \local cmd="$1"
        shift
        case "$cmd" in
            (activate | deactivate) __conda_activate "$cmd" "$@" ;;
            (install | update | upgrade | remove | uninstall) CONDA_INTERNAL_OLDPATH="${PATH}"
                __add_sys_prefix_to_path
                "$CONDA_EXE" $_CE_M $_CE_CONDA "$cmd" "$@"
                \local t1=$?
                PATH="${CONDA_INTERNAL_OLDPATH}"
                if [ $t1 = 0 ]
                then
                    __conda_reactivate
                else
                    return $t1
                fi ;;
            (*) CONDA_INTERNAL_OLDPATH="${PATH}"
                __add_sys_prefix_to_path
                "$CONDA_EXE" $_CE_M $_CE_CONDA "$cmd" "$@"
                \local t1=$?
                PATH="${CONDA_INTERNAL_OLDPATH}"
                return $t1 ;;
        esac
    fi
}

(2)

conda
usage: conda [-h] [-V] command ...

conda is a tool for managing and deploying applications, environments and packages.

Options:

positional arguments:
  command
    clean        Remove unused packages and caches.
    compare      Compare packages between conda environments.

...

(3)

/Users/user1/miniconda3/bin:/Users/user1/miniconda3/condabin:/Users/user1/gcloud/google-cloud-sdk/bin:/Users/user1/miniconda3/bin:/usr/local/opt/[email protected]/bin:/Users/user1/.krew/bin:/Users/user1/bin:Library/Python/3.7/bin:/Users/user1/bin:/usr/local/bin:/Library/Frameworks/Python.framework/Versions/3.9/bin:/Library/Frameworks/Python.framework/Versions/3.8/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/go/bin:/usr/local/munki:/Library/Apple/usr/bin:/Applications/Wireshark.app/Contents/MacOS:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin:/usr/local/bin:/Users/user1/executables:/Users/user1/go/bin:/Users/user1/executables/apache-maven-3.6.3/bin:/Users/user1/.local/bin


Solution

  • Use the -p flag:

    % which pyenv 
    pyenv () {
        local command
        command="${1:-}" 
        if [ "$#" -gt 0 ]
        then
            shift
        fi
        case "$command" in
            (rehash | shell) eval "$(pyenv "sh-$command" "$@")" ;;
            (*) command pyenv "$command" "$@" ;;
        esac
    }
    % which -p pyenv
    /usr/local/bin/pyenv
    % 
    

    Note, though, that on Zsh, you can get more informative output by using the whence command:

    % whence -aSv pyenv
    pyenv is a shell function from /Users/marlon/.cache/zsh-snap/eval/pyenv-init.zsh
    pyenv is /usr/local/bin/pyenv -> /usr/local/bin/../Cellar/pyenv/2.0.0/bin/pyenv -> /usr/local/Cellar/pyenv/2.0.0/bin/../libexec/pyenv
    %
    

    The reason why you cannot find the -p flag in man which, is because in Zsh, which is a builtin command equivalent to whence -c:

    % whence -aSv which
    which is a shell builtin
    which is /usr/bin/which
    %
    

    If you do man which, you get the manual for the external command which (on the second line in the output above). You can call the latter as follows:

    % command which pyenv
    /usr/local/bin/pyenv
    %
    

    The output of whence is more reliable, though. External command which only searches your $path, the result of which may or may not be what will be called when you type a command on the command line. whence -aSv, on the other hand, tells you exactly what will be called (the first line) and what alternatives there are.

    For more info, consult the Zsh manual: