Как поиск в $ PATH работает под капотом?

8

В Интернете слишком много статей / ресурсов, которые учат людей, КАК устанавливать переменную среды, PATHчтобы они могли использовать короткий javaили и pythonт. Д. Вместо абсолютного пути в интерфейсе командной строки.

Что мне интересно знать, так это то, что скрывается за сценой, когда мы вводим команду и нажимаем клавишу ввода (аналогично тому, что происходит, когда вы вводите URL в браузере ).

Вот мое предположение:

  1. прочитайте команду (стандартный синтаксический анализ / препроцесс, чтобы получить правильные аргументы $@)
  2. поиск команд
  3. выполнение команды (программа запущена, использует память, stdout / stderr в оболочку)
  4. перерисовать эмулятор с помощью соответствующих переменных среды (например $PS#, $PROMPTи т. д.)

Часть, которую я хочу выяснить больше всего, это поиск команд. Очевидно, что $PATHэта функция используется какой-то фоновой функцией и разделяется символами :/ в ;качестве разделителей. Что же тогда произошло? Используем ли мы хеш-таблицу (ключ: базовое имя файла, значение: абсолютное имя файла) для хранения двоичных файлов под этими переменными или другими хуками?

ПРИМЕЧАНИЕ. Первоначально я думал, что это хеш-таблица, поскольку я могу использовать ее, [ -z hash [command] ]чтобы проверить, доступна ли команда в текущем env, но когда я использую, hash | grep pythonя ничего не получаю из вывода, пока which pythonработает, как ожидалось. (Я думаю, что механизм может быть специфичным для оболочки, но я хочу получить более подробную информацию о нем.)

Xlee
источник

Ответы:

11

Как вы подозреваете, точное поведение зависит от оболочки, но базовый уровень функциональности определяется POSIX.

Поиск и выполнение команд для стандартного языка команд оболочки (который большинство оболочек реализует надмножество) имеет много случаев, но нас интересует только тот случай, когда PATHон используется. В таком случае:

команда должна искать переменную среды PATH, как описано в переменных среды XBD

а также

Если поиск успешен:

[...]

оболочка выполняет утилиту в отдельной служебной среде с действиями, эквивалентными вызову execl()функции [...] с аргументом пути, установленным на путь, полученный в результате поиска.

В неудачном случае выполнение завершается неудачно, и возвращается код выхода 127 с сообщением об ошибке.

Такое поведение согласуется с execvpфункцией, в частности. Все exec*функции принимают имя файла запускаемой программы, последовательность аргументов (которая будет являться argvчастью программы) и, возможно, набор переменных среды. Для версий, использующих PATHпоиск, POSIX определяет, что :

Файл аргументов используется для создания имени пути, которое идентифицирует новый файл образа процесса. [...] префикс пути для этого файла получается путем поиска каталогов, передаваемых в качестве переменной среды PATH.


Поведение PATH определяется в другом месте , как:

Эта переменная должна представлять последовательность префиксов пути, которые применяются некоторыми функциями и утилитами при поиске исполняемого файла, известного только по имени файла. Префиксы должны быть разделены <двоеточием> (':'). Когда к этому имени файла применяется префикс ненулевой длины, между префиксом и именем файла должен быть вставлен символ <slash>, если префикс не заканчивается. Префикс нулевой длины - это устаревшая функция, которая указывает текущий рабочий каталог. Он отображается в виде двух соседних символов ("::"), в качестве начального <двоеточия>, предшествующего остальной части списка, или в виде завершающего <двоеточия>, следующего за остальной частью списка. Строго соответствующее приложение должно использовать фактическое имя пути (например,.) Для представления текущего рабочего каталога в PATH.Список следует искать от начала до конца, применяя имя файла к каждому префиксу, пока не будет найден исполняемый файл с указанным именем и соответствующими разрешениями на выполнение . Если искомый путь содержит <слэш>, поиск по префиксам пути не должен выполняться. Если путь начинается с <косой черты>, указанный путь разрешается (см. Разрешение имени пути ). Если PATH не установлен или имеет значение null, поиск пути определяется реализацией.

Это немного плотно, поэтому резюме:

  1. Если в имени программы есть /(косая черта, U + 002F SOLIDUS), обработайте его как путь обычным способом и пропустите остальную часть этого процесса. Для оболочки этот случай технически не возникает (потому что правила оболочки уже рассмотрели его).
  2. Значение PATHделится на части в каждом двоеточии, а затем каждый компонент обрабатывается слева направо. В качестве особого (исторического) случая пустой компонент непустой переменной рассматривается как .(текущий каталог).
  3. Для каждого компонента имя программы добавляется в конец с присоединением, /и проверяется наличие файла с таким именем, а если он существует, то также проверяются действительные разрешения на выполнение (+ x). Если одна из этих проверок не пройдена, процесс переходит к следующему компоненту. В противном случае команда преобразуется в этот путь, и поиск завершен.
  4. Если у вас закончились компоненты, поиск не удастся.
  5. Если в нем ничего нет PATHили его нет, делай что хочешь.

Реальные оболочки будут иметь встроенные команды, которые находятся перед этим поиском, а также часто псевдонимы и функции. Те не взаимодействуют с PATH. POSIX определяет некоторое поведение вокруг них , и ваша оболочка может иметь гораздо больше.


Хотя на exec*большинство из них можно положиться, на практике оболочка может реализовать этот поиск сам, особенно для целей кэширования, но поведение пустого кэша должно быть аналогичным. Оболочки имеют довольно широкую широту здесь и ведут себя немного иначе в угловых случаях.

Как вы обнаружили, Bash использует хеш-таблицу для запоминания полных путей команд, которые он видел ранее, и эту таблицу можно получить с помощью hashфункции. При первом запуске команды она ищет, и когда результат найден, он добавляется в таблицу, поэтому вам не нужно беспокоиться о поиске в следующий раз.

В zsh, с другой стороны, полный PATHпоиск обычно выполняется при запуске оболочки. Таблица поиска заполняется всеми обнаруженными именами команд, поэтому поиск во время выполнения обычно не требуется (если не добавлена ​​новая команда). Вы можете заметить, что это происходит, когда вы пытаетесь завершить команду, которой раньше не было.

Очень легкие оболочки, как dashправило, имеют тенденцию делегировать как можно больше поведения системной библиотеке и не беспокоятся о прошлых путях команд.

Майкл Гомер
источник
Большое спасибо за такое подробное объяснение, это действительно дает глубокое понимание. Ваше сравнение о PATHмежду bashи zshпомогает мне решить мою растерянность!
Xlee