0

In bash I see which returns the paths in order, but in zsh it has different ordering.

I now primarily use zsh, how can I get proper output ordering of which in zsh? Why is it different? I expected the ordering to match my path.

My path is: /usr/local/bin:/usr/bin

bash$ which -a git
/usr/local/bin/git
/usr/bin/git
zsh$ which -a git
/usr/bin/git
/usr/local/bin/git -> ../Cellar/git/2.32.0/bin/git
  • 1
    From bash, you get the external which utility. From zsh, you get the built-in which, which is the same as whence -c. From zsh, you could try /usr/bin/which instead. You haven't shown what your PATH is in each shell, only that it's something unusually short in one of them (uncertain which one). What is the issue that you're trying to solve where the ordering of these strings is important? – Kusalananda Oct 23 '21 at 08:38
  • I only posted the relevant part of my path, and it's the same in both shells. The order I expect is the order the executable is found in the path, which is what bash shows. zsh is listing them in some other order, I believe by file type. I use which to determine which executable will be executed. – Michael Ozeryansky Oct 23 '21 at 08:59
  • 2
    What's the output of type which in zsh? That git -> ... is not something zsh's builtin which command would ever output AFAIK. You might have some form of custom alias for which. – Stéphane Chazelas Oct 23 '21 at 09:23
  • 1
  • @StéphaneChazelas I forgot I made which into a helper function! which(){ /usr/bin/which -a "$@" | xargs ls -l | tr -s ' ' | cut -d ' ' -f 9- } I'll close the question. – Michael Ozeryansky Oct 23 '21 at 09:33
  • @StéphaneChazelas unix stackexchange always has the experts, thanks – Michael Ozeryansky Oct 23 '21 at 09:36

1 Answers1

0

You happened to have which defined as:

which(){
  /usr/bin/which -a "$@" |
    xargs ls -l |
    tr -s ' ' |
    cut -d ' ' -f 9-
}

ls sorts the filename lexically on output and /usr/local/bin/git comes after /usr/bin/git because l comes after b in your locale.

The GNU implementation of ls has a -U option to disable that sorting.

Your /usr/bin/which command seems to be one that prints the path of all command names found in $PATH when passed a -a option. With zsh builtins, you can do the same with whence -pa¹.

So you could do something like:

mywhich() (
  set -o pipefail
  zmodload zsh/stat
  whence -pa "$@" |
    while IFS= read -r f; do
      if [[ -L $f ]] && stat -A l +link -- $f; then
        print -r -- "$f -> $l"
      else
        print -r -- $f
      fi
    done
)

(here assuming that none of the file paths contain newline characters).

A more correct version of yours, on a GNU system, would be like:

mywhich() (
  set -o pipefail
  command which -a "$@" |
    xargs -rd '\n' ls -ndU -- |
    sed -E 's/([^ ]+ +){8}//'
)

In any case, note that bash doesn't have a which builtin, so what which outputs there is independent of the shell. Only tcsh and zsh have which builtin.


¹ though, like your /usr/bin/which (but contrary to zsh's builtin which), it won't necessarily tell you which command the shell would run as it ignores aliases, functions, builtins and even the $hash table of executables. See also Why not use "which"? What to use then?

  • Great, thanks for pointing out sorting is the real issue. All I needed to change is ls -l to ls -lf, since -f is "Output is not sorted." on mac. – Michael Ozeryansky Oct 23 '21 at 20:18