10

I am trying to use a very basic setup pattern here: I have two different installations of a given tool (ocamlc, but that does not really matter). In order to select one tool, I attempt to override the PATH variable by prepending an entry.

% where ocamlc     
/home/choeger/ueb/uebb/amsun-integration/Runtime/test_src/../../opam/4.02.3/bin/ocamlc
/home/choeger/.opam/4.02.3/bin/ocamlc
/usr/bin/ocamlc

However, the zsh still uses the older entry:

% ocamlc -v        
The OCaml compiler, version 4.02.3
Standard library directory: /home/choeger/.opam/4.02.3/lib/ocaml

(One can see that it uses the second entry because the library directory is hardcoded to that installation)

Bash behaves as expected:

% bash -c "ocamlc -v"
The OCaml compiler, version 4.02.3
Standard library directory: /home/choeger/ueb/uebb/amsun-integration/opam/4.02.3/lib/ocaml

So why does zsh ignore the first PATH entry although it lists it as first element of where?

edit: In order to verify that zsh does not invoke the same binary, here is another run:

% type -a ocamlc
ocamlc is /home/choeger/ueb/uebb/amsun-integration/tests/pendulum/../../opam/4.02.3/bin/ocamlc
ocamlc is /home/choeger/.opam/4.02.3/bin/ocamlc
ocamlc is /usr/bin/ocamlc
% ocamlc -v
The OCaml compiler, version 4.02.3
Standard library directory: /home/choeger/.opam/4.02.3/lib/ocaml
% /home/choeger/ueb/uebb/amsun-integration/tests/pendulum/../../opam/4.02.3/bin/ocamlc -v
The OCaml compiler, version 4.02.3
Standard library directory: /home/choeger/ueb/uebb/amsun-integration/opam/4.02.3/lib/ocaml

edit2: Here is the setopt output:

% setopt
autocd
autopushd
nobeep
completeinword
correct
extendedglob
extendedhistory
histignorealldups
histignorespace
nohup
interactive
interactivecomments
longlistjobs
monitor
nonomatch
pushdignoredups
shinstdin
zle
% 

Config is the grml config found here plus some path variables in the .local file.

choeger
  • 3,562
  • 20
  • 33

2 Answers2

14

By default zsh hashes locations of commands the first time they are executed. When executing it a second time the hashed path is used. To refresh the hash table run

rehash

or

hash -r

This should happen automatically every time you change PATH and its main use is, when new executables are added to directories already in PATH.


Note: the following might be overkill for the specific use case. But it also does solve the issue and might be of interest for slightly different use cases.

If you do not care about the (probably negligible) performance hit, you can disable hashing of commands by disabling the HASH_CMDS option:

setopt nohashcmds

While zsh will still be using the hash table, it will not automatically add every command. So, unless a command is entered to the hash table by other means, zsh will check PATH for the command every time.

This might still lead to problems, if the option CORRECT is set. As this does set the hash table in order to provide spelling correction, but will not necessarily refresh it when PATH changes. In order to refresh the table automatically, you can use the precmd hook which is executed each time before the prompt is printed.

autoload -Uz add-zsh-hook
auto_rehash () {
    rehash
}
add-zsh-hook precmd auto_rehash 
Adaephon
  • 16,929
  • 1
  • 54
  • 71
  • That is a nice answer. And indeed `hash` shows that the entry still points to the old location. Unfortunately the rehash-ing does not change a thing here. Where does rehash get its source? Did I hit a genuine bug here? – choeger Apr 11 '16 at 12:31
  • It should get its information from `PATH`. Strange… What version of `zsh` are you using? – Adaephon Apr 11 '16 at 13:31
  • zsh 5.1.1 (x86_64-redhat-linux-gnu) – choeger Apr 11 '16 at 13:41
  • So, I did some more testing and it seems that any changes in `PATH` should automatically lead to using the correct binary according to the new `PATH`, at least with default settings on `zsh` 5.0.6 and 5.2. So this being a bug seems not that likely. Could please post a link to your configuration or at least add the output of `setopt` to your question? – Adaephon Apr 12 '16 at 05:41
  • Added setopt and link to config file. – choeger Apr 12 '16 at 06:19
  • I tested the configuration on my system (zsh 5.2) and everything seems to work as it should: when changing `PATH` the new setting is immediately used. Looking through the configuration, it even contains some extra functionality to ensure that `rehash` is done automatically to avoid any problems with correction or completion. Maybe updating to zsh 5.2 will solve the issue, but - as I said - I think it unlikely, I am sorry, but I am out of ideas. – Adaephon Apr 12 '16 at 06:39
  • Mind. Blown. Thanks for this, I thought I was seeing things. – JBCP Dec 20 '16 at 17:32
0

zsh does not skip PATH entries. It does, however, give priority of alias and shell functions. This doesn't apply to your case, because if you had any of those, where would have listed this too.

In your case, if the where and the ocamlc command have been executed in the same shell, and in that order, you can be pretty sure that ocamlc executes /home/choeger/ueb/uebb/amsun-integration/Runtime/test_src/../../opam/4.02.3/bin/ocamlc. Did you try running Ocam explicitly via this absolute path? I bet you will get the same output.

BTW, type -a would give, in general, a bit more detailed information than where.

user1934428
  • 19,864
  • 7
  • 42
  • 87
  • It must have a different reason. Theoretically, `ocamlc` could query how it was called (via PATH or by abs path), though it is rare that programs are doing this. Try to explicitly cut down the PATH, i.e. `PATH=/home/choeger/ueb/uebb/amsun-integration/tests/pendulum/../../opam/4.02.3/bin ocamlc -v` .... in this case, zsh doesn't have any other PATH to choose from. If you want to be really sure, write `command ocamlc` instead of just `ocamlc`. The keyword `command` would bypass alias-expansion and shell function definitions. – user1934428 Apr 11 '16 at 10:07
  • 1
    command does not change anything, explicitly restricting the path leads to the expected result. – choeger Apr 11 '16 at 10:21
  • 1
    The difference between `type -a` and `where` is mostly formating. The exception being functions, for which `where` actually prints the function body, while `type -a` just states "is a shell function". So `type -a` would give less detailed information. – Adaephon Apr 11 '16 at 11:13