OS X Mountain Lion: path_helper是如何工作的?

17

我通过 Homebrew 安装了 rbenv,但我不知道为什么 path_helper 在路径末尾而不是开头添加了 ~/.rbenv/shims。最重要的是,path_helper 是如何获取此信息的?

根据 path_helper 的 man 页面,它从 /etc/paths 和 /etc/paths.d 中的文件中读取条目。但我在那里找不到字符串 ".rbenv/shims"。

~% cat /etc/paths 
/usr/bin
/bin
/usr/sbin
/sbin
/usr/local/bin
~% ls -la /etc/paths.d 
total 0
drwxr-xr-x    2 root  wheel    68 Jun 21 03:16 .
drwxr-xr-x  107 root  wheel  3638 Sep 10 09:59 ..
~% /usr/libexec/path_helper
PATH="/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/Users/gordon/.rbenv/shims"; export PATH;
2个回答

14
我怀疑你的 .bash_profile.bashrc 文件将.rbenv/shims 添加到了你的路径中,并且在shell启动期间,在path_helper 被调用之前运行了。 path_helper 的 man 页面开头是:
 The path_helper utility reads the contents of the files in the directo-
 ries /etc/paths.d and /etc/manpaths.d and appends their contents to the
 PATH and MANPATH environment variables respectively.

关键点在于path_helper实用工具旨在向现有的PATH设置中添加内容,而不是替换它们。(实际上,它真正做的是在前面添加内容,而不是在后面添加,这对于PATH变量很重要...)

因此,如果我从我的PATH中开始一个条目,由path_helper生成的设置将确保该条目继续存在于其生成的PATH中。

% echo $SHELL
/bin/bash
% uname
Darwin
% /usr/libexec/path_helper 
PATH="/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin"; export PATH;
% PATH="" /usr/libexec/path_helper 
PATH="/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin"; export PATH;
% PATH=foo /usr/libexec/path_helper 
PATH="/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:foo"; export PATH;

请注意,即使/etc/paths/etc/paths.d/*的内容没有更改,foo在最后一行中被包含在我的PATH中。

同时,path_helper实用程序似乎也小心翼翼地不会产生具有重复条目的路径; 它在连接/etc/paths/etc/paths.d/*和当前PATH之后删除重复的条目。

这后一个细节尤其令人困惑,因为它可能导致与原始PATH设置相比的条目重新排序!

以下是一些示例:第一种情况显示删除了重复的foo。 第二个和第三个案例说明了条目重新排序:生成的PATH在两种情况下都相同,但在第三种情况下,/usr/bin条目已从foobar之间移动到PATH的前面。(这个删除重复条目似乎只基于成对条目的简单字符串匹配,如下面第四个案例所示,其中字符串/usr/bin/仍然位于foo/bar之间。)

% PATH=foo:foo /usr/libexec/path_helper 
PATH="/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:foo"; export PATH;
% PATH=foo:bar /usr/libexec/path_helper 
PATH="/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:foo:bar"; export PATH;
% PATH=foo:/usr/bin:bar /usr/libexec/path_helper 
PATH="/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:foo:bar"; export PATH;
% PATH=foo/:/usr/bin/:bar /usr/libexec/path_helper 
PATH="/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:foo/:/usr/bin/:bar"; export PATH;

最后,要归功于那些应得的人: 虽然上述所有命令序列都是我自己调查的结果,但我最初是受到阅读这里的提示而开始研究path_helper的行为, 该提示指出path_helper重用了父进程设置的PATH环境变量。


1
感谢您指出,与文档相反,path_helper会将路径添加到前面并删除重复项。 - algal

13

path_helper的手册页是不正确的。手册页上说path_helper正在将/etc/paths.d添加到PATH中。但实际上,path_helper是将/etc/paths中的列表APPEND到/etc/paths.d中的列表,然后有效地将该结果PREPEND到实际现有的PATH上(以及从该列表中清除被覆盖的重复项)。

确切地说,仅就PATH而言,它所做的是:

  1. 读取文件/etc/paths中路径的列表
  2. 将/etc/paths.d目录中文件中的路径列表APPEND到它上面
  3. 改变PATH变量以删除列表中的任何项目
  4. 将变异后的PATH变量的值APPEND到列表中
  5. 将此列表保存为新的PATH

用伪代码重新表述,它所做的是:

  1. NEWPATH = Read(/etc/paths)
  2. NEWPATH = $NEWPATH +append+ Read(/etc/paths.d/*)
  3. PATH = $PATH -minus- $NEWPATH
  4. NEWPATH = $NEWPATH +append+ $PATH
  5. PATH = $NEWPATH

这种情况的危险在于,如果您手动配置了PATH,那么很可能是为了添加覆盖系统路径组件的组件。如果path_helper在您不希望它调用时被调用,则会通过将系统范围的路径组件放在您的路径组件之前来撤消您的更改。

path_helper如何意外调用?例如,它被/etc/zshenv调用,在启动zsh shell时每次都会调用它,无论是子shell还是从其他应用程序(如emacs或其他)调用的zsh实例。

我已经写了一个更详细的OpenRadar 关于path_helper的错误报告


这里有一个改进的尝试,如果有人感觉想要使用它或检查它,请访问 https://github.com/yb66/path_helper - ian
5
在OS El Capitan中,path_helper/etc/zprofile中调用,而不是/etc/zshenv,我认为这样做更糟糕。至少/etc/zshenv是最先被调用的,因此您可以在~/.zshenv中覆盖所有shell(交互式,非交互式)的PATH。但由于/etc/zprofile~/.zshenv之后被调用,如果您希望交互式和非交互式shell具有相同的PATH,则必须在~/.zprofile中重复对~/.zshenv所做的修改。 - rampion

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