15%

Сэкономьте 15% на всех хостинговых услугах

Проверьте свои навыки и получите скидку на любой тарифный план

Используйте код:

Skills
Начать
08.10.2024

Команда `which` в Linux: Полное техническое руководство с примерами

Команда `which` в Linux находит абсолютный путь исполняемого файла, сканируя каталоги, перечисленные в переменной окружения `PATH`, и возвращая первое совпадение. Это утилита, близкая к стандарту POSIX, которую системные администраторы, разработчики и DevOps-инженеры используют ежедневно для проверки расположения бинарных файлов, аудита сред выполнения и отладки конфликтов, связанных с PATH.

Когда вы запускаете `which python3`, оболочка не выполняет поиск по всей файловой системе — она проходит только по разделённому двоеточиями списку каталогов, хранящихся в `$PATH`, слева направо, и останавливается на первом совпадении. Это поведение является одновременно её главным преимуществом и наиболее важным ограничением, которое необходимо понимать.

Базовый синтаксис

“`bash

which [options] command_name [command_name …]

“`

  • `[options]` — Необязательные флаги, изменяющие поведение вывода (подробно рассмотрены ниже).
  • `command_name` — Одно или несколько имён исполняемых файлов, которые нужно найти.

Как работает `which` изнутри

При вызове `which` команда считывает текущее значение переменной окружения `PATH`, разбивает его по разделителям `:` и последовательно перебирает каждый каталог. Для каждого каталога она проверяет, существует ли файл с именем команды и установлен ли у него бит исполнения (права `x`). Первое совпадение выводится на стандартный вывод.

Это означает, что `which` полностью зависит от текущего состояния `$PATH`. Если ваш `PATH` настроен неверно — например, пользовательский каталог стоит после `/usr/bin` вместо того, чтобы стоять перед ним — `which` точно отразит эту неправильную конфигурацию, что и делает его полезным для отладки.

Чтобы просмотреть текущий `PATH`:

“`bash

echo $PATH

Example output:

/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

“`

Основные варианты использования и примеры

Пример 1: Поиск одного исполняемого файла

Наиболее базовое применение — найти, где находится бинарный файл:

“`bash

which python3

“`

“`

/usr/bin/python3

“`

Это подтверждает, что при вводе `python3` система выполняет `/usr/bin/python3`. Если вы скомпилировали собственную сборку и поместили её в `/opt/python3.12/bin/`, но этот каталог не включён в `PATH`, `which` её не найдёт.

Пример 2: Запрос нескольких команд за один проход

Можно передать несколько имён команд в одном вызове — это удобно при аудите среды сборки:

“`bash

which python3 gcc git curl wget

“`

“`

/usr/bin/python3

/usr/bin/gcc

/usr/bin/git

/usr/bin/curl

/usr/bin/usr/bin/wget

“`

Это особенно полезно в скриптах валидации CI/CD-пайплайнов, где необходимо убедиться в наличии всех необходимых инструментов перед началом сборки.

Пример 3: Поиск всех экземпляров с помощью `-a`

Флаг `-a` указывает `which` продолжать поиск после первого совпадения и сообщать обо всех найденных экземплярах во всех каталогах `PATH`:

“`bash

which -a python3

“`

“`

/usr/bin/python3

/usr/local/bin/python3

“`

Это критически важно в средах, где установлено несколько версий Python — например, системный Python по пути `/usr/bin/python3` и версия, управляемая pyenv, по пути `/usr/local/bin/python3`. Бинарный файл, который стоит первым в `PATH`, и будет выполнен. Если активна неверная версия, этот вывод точно укажет, где возникает конфликт.

Реальный граничный случай: На серверах, где одновременно установлены Node.js из дистрибутива и Node.js, управляемый nvm, `which -a node` нередко выявляет два или три конфликтующих пути. Для решения этой проблемы требуется изменить порядок записей `PATH` в `.bashrc` или `.zshrc`, а не переустанавливать программное обеспечение.

Пример 4: Поведение при разрешении псевдонимов

Поведение `which` с псевдонимами во многом зависит от оболочки и конкретной реализации `which`, установленной в системе.

Во многих дистрибутивах Linux `which` является самостоятельным внешним бинарным файлом (не встроенной командой оболочки), поэтому у него нет доступа к таблице псевдонимов текущей оболочки. Однако в системах, где `which` реализован как функция или псевдоним оболочки (что характерно для конфигураций zsh), он может разрешать псевдонимы:

“`bash

alias ls='ls –color=auto'

which ls

“`

В системе zsh с функциональным `which`:

“`

ls: aliased to ls –color=auto

“`

В системе bash с внешним бинарным файлом `which`:

“`

/bin/ls

“`

Эта непоследовательность является широко известным источником путаницы и одной из основных причин, по которым опытные администраторы предпочитают использовать `type` или `command -v` в скриптах (рассмотрено ниже).

Пример 5: Использование `which` в условной логике скриптов

Распространённый паттерн в скриптах оболочки — использование `which` для проверки зависимости перед продолжением выполнения:

“`bash

if ! which docker > /dev/null 2>&1; then

echo "Docker is not installed or not in PATH. Aborting."

exit 1

fi

“`

Однако более переносимым и POSIX-совместимым подходом для скриптов является `command -v`:

“`bash

if ! command -v docker > /dev/null 2>&1; then

echo "Docker not found."

exit 1

fi

“`

Это различие важно при написании скриптов, предназначенных для работы в нескольких дистрибутивах или оболочках.

Техническое сравнение `which`, `type` и `command -v`

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

Функция`which``type``command -v`
Поиск внешних бинарных файловДаДаДа
Разрешение псевдонимов оболочкиЗависит от реализацииДа (всегда)Да (всегда)
Разрешение функций оболочкиНетДаДа
Определение встроенных команд оболочкиНетДаДа
Соответствие стандарту POSIXНетДаДа
Надёжная работа в скриптахРискованноРискованно (встроенная команда bash)Рекомендуется
Формат выводаТолько путьОписательная строкаПуть или определение
Поиск по всем записям PATH (эквивалент `-a`)Да (с `-a`)Да (с `-a`)Нет
Внешний бинарный файл (не встроенная команда)ДаНет (встроенная команда)Нет (встроенная команда)

Практические рекомендации:

  • Используйте `which` в интерактивном режиме в терминале, когда нужно быстро найти путь.
  • Используйте `type -a`, когда хотите увидеть все формы команды (псевдоним, функция, встроенная команда и бинарный файл).
  • Используйте `command -v` в производственных скриптах оболочки для обеспечения переносимости POSIX.

`type` в действии

“`bash

type -a python3

“`

“`

python3 is /usr/bin/python3

python3 is /usr/local/bin/python3

“`

“`bash

type ls

“`

“`

ls is aliased to `ls –color=auto'

“`

`command -v` в действии

“`bash

command -v git

“`

“`

/usr/bin/git

“`

“`bash

command -v ll

“`

“`

ll: aliased to ls -alF

“`

Практические сценарии отладки

Отладка неверной версии Python

Разработчик сообщает, что `python3 –version` возвращает `3.9.x`, хотя он установил `3.11` через собственную сборку. Последовательность диагностики:

“`bash

which python3 # Shows the first match

which -a python3 # Shows all matches

echo $PATH # Reveals directory ordering

ls -la /usr/local/bin/python3 # Checks if the custom build is symlinked correctly

“`

Решение почти всегда сводится либо к отсутствующей символической ссылке, либо к проблеме порядка записей `PATH` в файле инициализации оболочки.

Диагностика отсутствующей команды после установки

Если `which curl` не возвращает никакого вывода, бинарный файл либо не установлен, либо установлен в каталог, не входящий в `PATH`. Чтобы различить эти случаи:

“`bash

which curl # No output = not in PATH

find /usr -name curl -type f 2>/dev/null # Search for the binary outside PATH

apt list –installed 2>/dev/null | grep curl # Check package manager

“`

Проверка путей к инструментам перед развёртыванием

При настройке нового окружения VPS Хостинга стандартный чек-лист перед развёртыванием должен включать запуск `which -a` для каждого критически важного бинарного файла, от которого зависит ваше приложение. Это позволяет выявить расхождения в окружении между разработкой, тестированием и производством до возникновения сбоев во время выполнения.

Известные ограничения `which`

Понимание этих ограничений предотвращает ошибочную диагностику в сложных средах:

  • Область видимости только `PATH`: `which` не видит ни одного исполняемого файла, недоступного через `$PATH`. Инструменты, установленные в пользовательских локальных каталогах, например `~/.local/bin`, будут найдены только в том случае, если этот каталог включён в `PATH`.
  • Отсутствие осведомлённости о встроенных командах оболочки: Команды `cd`, `echo`, `alias` и `source` являются встроенными командами оболочки. `which cd` вернёт пустой результат или путь к внешнему бинарному файлу `cd`, который редко используется, что даст вводящий в заблуждение результат.
  • Таблицы псевдонимов, специфичные для оболочки: `which` как внешний бинарный файл не может читать таблицу псевдонимов вызывающей оболочки. Это делает его ненадёжным для интроспекции псевдонимов в bash.
  • Прозрачность символических ссылок: `which` сообщает путь к символической ссылке, а не к разрешённой цели. Если `/usr/bin/python3` является символической ссылкой на `/usr/bin/python3.11`, `which python3` покажет `/usr/bin/python3`. Используйте `readlink -f $(which python3)` для разрешения полной цепочки.
  • Контекст `sudo`: Запуск команды с `sudo` использует `PATH` пользователя root, который может существенно отличаться от `PATH` обычного пользователя. `which node` от имени обычного пользователя может вернуть другой путь, чем `sudo which node`.

Продвинутые паттерны

Разрешение полной цепочки символических ссылок

“`bash

readlink -f $(which python3)

Output: /usr/bin/python3.11

“`

Проверка прав на выполнение вместе с путём

“`bash

ls -la $(which nginx)

Output: -rwxr-xr-x 1 root root 1234567 Jan 10 2024 /usr/sbin/nginx

“`

Совместное использование с `xargs` для пакетной проверки

“`bash

echo "python3 gcc git" | xargs -n1 which

“`

Использование в скриптах валидации окружения

На Выделенном сервере, на котором работает сложный стек приложений, скрипт валидации при запуске может выглядеть следующим образом:

“`bash

#!/bin/bash

REQUIRED_BINS="nginx php-fpm mysql redis-cli composer"

MISSING=0

for bin in $REQUIRED_BINS; do

if ! command -v "$bin" > /dev/null 2>&1; then

echo "MISSING: $bin"

MISSING=$((MISSING + 1))

else

echo "OK: $bin -> $(which $bin)"

fi

done

[ "$MISSING" -gt 0 ] && exit 1

exit 0

“`

Примечания о поведении в различных оболочках

Поведение `which` не одинаково во всех средах Linux:

  • Bash: `which` обычно является внешним бинарным файлом (`/usr/bin/which`). Он не видит псевдонимы или функции bash, если только они не экспортированы.
  • Zsh: Многие конфигурации zsh поставляют `which` как встроенную функцию оболочки, которая разрешает псевдонимы и функции, что делает её вывод более информативным, но и отличным от поведения в bash.
  • Fish shell: Fish имеет собственный встроенный эквивалент `which`, а её система псевдонимов (называемых `functions`) обрабатывается иначе.
  • Alpine Linux / BusyBox-окружения: Утилита `which` предоставляется BusyBox и может иметь урезанный набор функций по сравнению с пакетом GNU `which`.

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

Соображения безопасности

В средах с повышенными требованиями к безопасности `which` можно использовать как лёгкий инструмент аудита:

  • Убедитесь, что привилегированные бинарные файлы, такие как `sudo`, `su` или `passwd`, разрешаются в ожидаемые системные пути, а не в каталоги, доступные для записи пользователем и стоящие раньше в `PATH`.
  • Обнаруживайте попытки перехвата PATH: если `which ls` возвращает `/home/user/bin/ls` вместо `/bin/ls`, возможно, был внедрён вредоносный бинарный файл.

“`bash

Audit critical system binaries

for cmd in sudo su passwd ssh scp; do

echo "$cmd -> $(which $cmd)"

done

“`

Это стандартный шаг при усилении защиты сервера, который будет обслуживать SSL-сертификаты или выполнять обработку конфиденциального TLS-трафика, где целостность бинарных файлов не подлежит обсуждению.

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

Матрица решений: когда какой инструмент использовать

СценарийРекомендуемый инструмент
Быстрый интерактивный поиск пути`which`
Скрипт: проверка наличия команды`command -v`
Определение, является ли команда псевдонимом или функцией`type`
Поиск всех экземпляров в PATH`which -a` или `type -a`
Разрешение символических ссылок до конечного бинарного файла`readlink -f $(which …)`
Аудит на предмет перехвата PATH`which` + ручная проверка PATH
Переносимые скрипты для разных оболочек`command -v`

Технические ключевые выводы

  • `which` выполняет поиск в `$PATH` слева направо и возвращает первый найденный исполняемый файл — порядок записей `PATH` напрямую определяет, какой бинарный файл будет запущен.
  • Флаг `-a` необходим, когда сосуществуют несколько версий инструмента; никогда не предполагайте, что существует только один экземпляр, не проверив это.
  • Не используйте `which` в производственных скриптах оболочки — используйте `command -v` для соответствия стандарту POSIX и согласованного поведения в bash, dash и zsh.
  • `which` не может видеть встроенные команды оболочки, функции или псевдонимы, определённые в текущем сеансе оболочки, когда работает как внешний бинарный файл.
  • Всегда дополняйте результат `which` командой `readlink -f` при наличии символических ссылок, чтобы определить фактически выполняемый бинарный файл.
  • В многопользовательских или контейнеризованных средах `PATH` различается между пользователями и между контекстами `sudo` и не-`sudo` — всегда проверяйте в правильном контексте.
  • Перехват PATH через каталоги, доступные для записи пользователем и добавленные в начало `$PATH`, является реальным вектором атаки; `which` — быстрый инструмент первичного аудита против него.

Часто задаваемые вопросы

В чём разница между `which` и `whereis`?

`which` выполняет поиск исполняемых файлов только в `$PATH`. `whereis` одновременно выполняет поиск в более широком наборе предопределённых системных каталогов для нахождения бинарного файла, его страницы руководства и исходных файлов. Используйте `whereis`, когда вам нужно найти документацию или исходный код вместе с бинарным файлом.

Почему `which cd` ничего не возвращает?

`cd` является встроенной командой оболочки, а не внешним исполняемым файлом. Поскольку `which` сканирует только `$PATH` в поисках файлов с правом на выполнение, он не может найти встроенные команды. Вместо этого используйте `type cd`, который корректно сообщит `cd is a shell builtin`.

Может ли `which` сообщить мне, какая версия программы установлена?

Нет. `which` возвращает только путь. Чтобы получить версию, передайте результат по конвейеру: `$(which python3) –version` или просто `python3 –version`. Путь от `which` помогает убедиться, что вы запрашиваете нужный бинарный файл.

Почему `which python3` возвращает другой результат при использовании `sudo`?

`sudo` выполняет команды в окружении root, включая `PATH` пользователя root, который обычно более ограничен, чем `PATH` обычного пользователя. Каталоги вроде `~/.local/bin` или пути nvm/pyenv, добавленные в `.bashrc` пользователя, отсутствуют в `PATH` пользователя root. При отладке выполнения с повышенными привилегиями всегда проверяйте с помощью `sudo which python3` отдельно.

Доступна ли `which` на macOS?

Да, macOS включает `which` как часть своего пользовательского пространства, производного от BSD. Однако версия macOS не поддерживает флаг `-a` во всех более старых версиях. На современных macOS с Homebrew у вас может быть установлен GNU `which` наряду с системной версией. Используйте `type -a which` на macOS, чтобы увидеть, какая реализация активна.

15%

Сэкономьте 15% на всех хостинговых услугах

Проверьте свои навыки и получите скидку на любой тарифный план

Используйте код:

Skills
Начать