OS X Mountain Lion: как работает path_helper?

Я установил rbenv через homebrew, и теперь я не знаю, почему path_helper помещает ~/.rbenv/shims в конец пути вместо начала. И самое главное, как эта информация была получена path_helper?

В соответствии с man-страницей path_helper он считывает записи из /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;

Ответ 1

Я подозреваю, что ваш .bash_profile или .bashrc добавляет .rbenv/shims для вашей PATH, и это работает в какой-то момент раньше path_helper вызывается во время запуска оболочки.

Страница man для path_helper открывается с помощью

 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;

Обратите внимание, что foo был включен в мою PATH в последней строке, хотя содержимое /etc/paths и /etc/paths.d/* не изменилось.

В то же время утилита path_helper также, похоже, для создания путей с дублирующимися записями; он удаляет повторяющиеся записи после конкатенации /etc/paths и /etc/paths.d/*, а ток PATH.

Эта последняя деталь может быть особенно запутанной, поскольку она может вызвать (по умолчанию).

Ниже приведены некоторые примеры такого поведения: первый случай показывает удаление дубликата foo. Второй и третий случай иллюстрируют переупорядочение записи: генерируемый PATH в обоих случаях одинаковый, но в третьем случае запись /usr/bin была перемещена из промежуточных foo и bar в начало 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, заданную родительским процессом.

Ответ 2

Неправильная страница пользователя path_helper. На странице man говорится, что path_helper добавляет /etc/paths.d в PATH. Но на самом деле path_helper использует APPENDing/etc/paths.d в списке из /etc/paths, а затем эффективно PREPENDing этот результат на фактическую ранее существовавшую PATH (а также очистку PATH переопределенных дубликатов из этого списка).

Чтобы быть точным, говоря только о PATH, например, что он делает:

  • прочитайте список путей в файле /etc/paths
  • ПРИЛОЖИТЕ на нем списки путей в файлах в каталоге /etc/paths.d
  • мутировать переменную PATH для удаления любых элементов в списке
  • ПРИЛОЖИТЬ в список значение измененной переменной PATH
  • Сохраните этот список как новый PATH

Чтобы перефразировать это в псевдокоде, что он делает:

  • NEWPATH = Чтение (/etc/paths)
  • NEWPATH = $NEWPATH + append + Read (/etc/paths.d/*)
  • PATH = $PATH -minus- $NEWPATH
  • NEWPATH = $NEWPATH + append + $PATH
  • PATH = $NEWPATH

Какая вероломность в том, что если вы вручную настраиваете свой PATH, возможно, чтобы добавить компоненты, которые переопределяют компоненты системного пути. Если path_helper вызывается, когда вы его не ожидаете, он отменит ваши изменения, поместив системные компоненты пути перед вашими компонентами пути.

Как вызвал бы случайный помощник пути? Например, он вызывается /etc/zshenv, который вызывается каждый раз, когда вы запускаете оболочку zsh, независимо от того, является ли она подоболочкой или экземпляр zsh из другого приложения, такого как emacs, или что-то еще.

Я написал это более подробно как отчет об ошибках OpenRadar в файле path_helper.