为什么zsh会忽略PATH条目的顺序?

10

我在这里尝试使用一种非常基本的设置模式:我有两个不同的工具安装(ocamlc,但那并不重要)。为了选择一个工具,我试图通过在路径前面添加一个条目来覆盖PATH变量。

% 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

然而,zsh 仍然使用较旧的入口:

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

可以看出它使用了第二个条目,因为库目录是硬编码到该安装中的。

Bash 的行为符合预期:

% 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
因此,为什么zsh会忽略第一个PATH条目,尽管它将其列为 where 的第一个元素?
编辑: 为了验证zsh不会调用相同的二进制文件,这里进行另一次运行:
% 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

编辑2:

这里是setopt输出:

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

配置是在这里找到的grml配置加上.local文件中的一些路径变量。

2个回答

15
默认情况下,zsh会在第一次执行命令时对其位置进行哈希处理。第二次执行时将使用哈希路径。要刷新哈希表,请运行:
```hash -r```
rehash
或者
hash -r

每当您更改 PATH 时,这应该自动发生,并且其主要用途是在已经在 PATH 中的目录中添加新可执行文件时使用。


注意: 对于特定的用例,以下内容可能过于复杂。但它也确实解决了问题,并且对于略微不同的用例可能会有所帮助。

如果您不关心(可能可以忽略不计的)性能影响,则可以通过禁用 HASH_CMDS 选项来禁用命令的哈希处理:

setopt nohashcmds

尽管zsh 仍将使用哈希表,但它不会自动添加每个命令。因此,除非通过其他方式将命令输入哈希表,否则zsh 将每次都检查 PATH 中的命令。

如果设置了选项CORRECT,这可能仍然会导致问题。由于此选项设置哈希表以提供拼写更正,但不一定在PATH更改时刷新哈希表。为了自动刷新哈希表,可以使用precmd钩子,在每次打印提示符之前执行该钩子。

autoload -Uz add-zsh-hook
auto_rehash () {
    rehash
}
add-zsh-hook precmd auto_rehash 

这是一个很好的答案。而且,hash显示该条目仍指向旧位置。不幸的是,重新哈希在这里并没有改变任何事情。rehash从哪里获取源?我是否遇到了真正的错误? - choeger
它应该从“PATH”获取信息。奇怪...你使用的是什么版本的“zsh”? - Adaephon
zsh 5.1.1 (x86_64-redhat-linux-gnu) - choeger
所以,我进行了更多的测试,似乎在PATH中进行任何更改都应该自动导致使用新PATH中正确的二进制文件,至少在zsh 5.0.6和5.2上的默认设置是如此。因此,这似乎不太可能是一个错误。请发布您配置的链接或至少将setopt的输出添加到您的问题中? - Adaephon
我在我的系统(zsh 5.2)上测试了配置,一切似乎都按照预期工作:更改PATH时立即使用新设置。通过查看配置,它甚至包含一些额外的功能,以确保自动执行rehash以避免任何更正或完成问题。也许升级到zsh 5.2会解决这个问题,但是 - 如我所说 - 我认为这不太可能,很抱歉,我已经没有更多想法了。 - Adaephon
显示剩余2条评论

0

zsh不会跳过PATH条目。但是,它会优先考虑别名和shell函数。这不适用于您的情况,因为如果您有任何这些内容,where也会列出它们。

在您的情况下,如果whereocamlc命令在同一个shell中执行,并且按照那个顺序执行,您可以非常确定ocamlc将执行/home/choeger/ueb/uebb/amsun-integration/Runtime/test_src/../../opam/4.02.3/bin/ocamlc。您是否尝试通过此绝对路径显式运行Ocam?我敢打赌您会得到相同的输出。

顺便说一句,type -a通常会提供比where更详细的信息。


它必须有一个不同的原因。理论上, ocamlc 可以查询它是通过 PATH 还是绝对路径调用的,尽管程序很少这样做。尝试显式地削减 PATH,即 PATH=/home/choeger/ueb/uebb/amsun-integration/tests/pendulum/../../opam/4.02.3/bin ocamlc -v .... 在这种情况下,zsh 没有其他的 PATH 可以选择。如果你想确保,可以写成 command ocamlc 而不是 ocamlc。关键字 command 将绕过别名扩展和 shell 函数定义。 - user1934428
1
命令不会改变任何东西,显式限制路径会导致预期结果。 - choeger
1
type -awhere 的区别主要在于格式。唯一的例外是函数,对于函数而言,where 实际上会打印函数体,而 type -a 只会显示 "is a shell function"。因此,type -a 提供的信息更为简略。 - Adaephon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接