Вот все, о чем вы никогда не думали, что никогда не захотите узнать об этом:
Резюме
Чтобы получить путь к исполняемому файлу в Bourne-подобном сценарии оболочки (есть несколько предостережений; см. Ниже):
ls=$(command -v ls)
Чтобы узнать, существует ли данная команда:
if command -v given-command > /dev/null 2>&1; then
echo given-command is available
else
echo given-command is not available
fi
По приглашению интерактивной оболочки типа Борна:
type ls
whichКоманда сломанное наследие от C-Shell и лучше оставить в покое в Bourne-подобных оболочках.
Сценарии использования
Существует различие между поиском этой информации как части скрипта или в интерактивном режиме в приглашении оболочки.
В командной строке типичный вариант использования: эта команда ведет себя странно, я использую правильный? Что именно произошло, когда я напечатал mycmd? Могу ли я посмотреть дальше, что это такое?
В этом случае вы хотите знать, что делает ваша оболочка, когда вы вызываете команду, фактически не вызывая команду.
В сценариях оболочки это обычно бывает по-другому. В сценарии оболочки нет причины, по которой вы хотели бы знать, где или что представляет команда, если все, что вы хотите сделать, это запустить ее. Как правило, вам нужно знать путь к исполняемому файлу, чтобы вы могли получить из него больше информации (например, путь к другому файлу относительно этого или прочитать информацию из содержимого исполняемого файла по этому пути).
Интерактивный, вы можете захотеть узнать о всех тех my-cmdкомандах , доступных в системе, в сценариях, редко так.
Большинство доступных инструментов (как это часто бывает) были разработаны для интерактивного использования.
история
Сначала немного истории.
Ранние оболочки Unix до конца 70-х не имели никаких функций или псевдонимов. Только традиционный поиск исполняемых файлов в $PATH. cshв 1978 году были введены псевдонимы (хотя cshвпервые был выпущен в 2BSDмае 1979 года), а также обработка .cshrcдля пользователей настройки оболочки (каждая оболочка, как cshсчитывается, .cshrcдаже когда она не интерактивна, как в сценариях).
Хотя оболочка Bourne была впервые выпущена в Unix V7 в начале 1979 года, поддержка функций была добавлена гораздо позже (1984 в SVR2), и в любом случае у нее никогда не было rcфайла ( .profileпредназначенного для настройки среды, а не оболочки как таковой ).
csh стала намного более популярной, чем оболочка Bourne, поскольку (хотя она имела ужасно худший синтаксис, чем оболочка Bourne), она добавила множество более удобных и приятных функций для интерактивного использования.
В 3BSD1980 году для пользователей был добавлен whichсценарий csh,csh помогающий идентифицировать исполняемый файл, и это едва ли другой сценарий, который вы можете найти, как и whichво многих коммерческих Unices в настоящее время (например, Solaris, HP / UX, AIX или Tru64).
Этот сценарий читает пользователя ~/.cshrc(как и все cshсценарии, если только он не вызывается csh -f) и ищет предоставленные имена команд в списке псевдонимов и в $path(массив, который cshподдерживается на основе $PATH).
Итак, вы whichпришли первым для самой популярной оболочки в то время (и cshвсе еще были популярны до середины 90-х), что является основной причиной, почему она была задокументирована в книгах и до сих пор широко используется.
Обратите внимание, что даже для cshпользователя этот whichсценарий csh не обязательно дает вам правильную информацию. Он получает псевдонимы, определенные в ~/.cshrc, а не те, которые вы, возможно, определили позже в приглашении или, например, с помощью sourceдругого cshфайла, и (хотя это не будет хорошей идеей) PATHмогут быть переопределены в ~/.cshrc.
Выполнение этой whichкоманды из оболочки Bourne будет по-прежнему искать псевдонимы, определенные в вашем ~/.cshrc, но если их нет, потому что вы их не используете csh, это все равно, вероятно, даст вам правильный ответ.
Подобная функциональность не была добавлена в оболочку Bourne до 1984 года в SVR2 с помощью typeвстроенной команды. Тот факт, что он встроен (в отличие от внешнего скрипта), означает, что он может дать вам правильную информацию (в некоторой степени), поскольку он имеет доступ к внутренним компонентам оболочки.
Первоначальная typeкоманда страдала от проблемы, аналогичной whichскрипту, в том, что она не возвращала состояние завершения сбоя, если команда не была найдена. Кроме того, для исполняемых файлов, напротив which, он выводит что-то вроде, ls is /bin/lsа не просто, /bin/lsчто делает его менее простым в использовании в скриптах.
В оболочке Bourne версии 8 Unix (не выпущенной в дикой природе) ее typeвстроенное имя было переименовано whatis. И оболочка Plan9 (некогда преемник Unix) rc(и ее производные, такие как akangaи es) также имеют whatis.
Оболочка Korn (подмножество, на котором основано определение POSIX sh), разработанная в середине 80-х годов, но не получившая широкого распространения до 1988 года, добавила многие cshфункции (редактор строк, псевдонимы ...) поверх оболочки Bourne. , Он добавил свою собственную whenceвстроенную функцию (в дополнение к type), которая приняла несколько опций ( -vдля обеспечения typeподробного вывода -подобного вывода и -pпоиска только исполняемых файлов (не псевдонимов / функций ...)).
Совсем случайно, в связи с проблемами авторского права между AT & T и Беркли, в конце 80-х начале 90-х годов появилось несколько реализаций оболочки свободного программного обеспечения . Вся оболочка Almquist (пепел, которая должна быть заменой оболочки Bourne в BSD), общественная реализация ksh (pdksh) bash(спонсируемая FSF), была выпущена zshв период между 1989 и 1991 годами.
У Ash, хотя и предназначенного для замены оболочки Bourne, не было typeвстроенной функции намного позже (в NetBSD 1.3 и FreeBSD 2.3), хотя она и была hash -v. OSF / 1 /bin/shимеет typeвстроенную функцию, которая всегда возвращает 0 до OSF / 1 v3.x. bashне добавил, whenceно добавил -pопцию, чтобы typeнапечатать путь ( type -pбудет похоже whence -p) и -aсообщить обо всех соответствующих командах. tcshсделал whichвстроенный и добавил whereкоманду, действующую как bash's type -a. zshесть их все.
fishОболочка (2005) имеет typeкоманду реализована в виде функции.
Тем whichвременем сценарий csh был удален из NetBSD (поскольку он был встроен в tcsh и не очень используется в других оболочках), а функциональность была добавлена к нему whereis(когда вызывается как which, whereisведет себя как, whichза исключением того, что он просматривает только исполняемые файлы $PATH). В OpenBSD и FreeBSD whichтакже был изменен на тот, что написан на C, который ищет только команды $PATH.
Реализации
Существуют десятки реализаций whichкоманды в различных Unices с разным синтаксисом и поведением.
В Linux (кроме встроенных в tcshand zsh) мы находим несколько реализаций. Например, в последних системах Debian это простой сценарий оболочки POSIX, который ищет команды в $PATH.
busyboxтакже есть whichкоманда.
Существует, GNU whichпожалуй, самый экстравагантный. Он пытается распространить действие whichсценария csh на другие оболочки: вы можете сказать ему, каковы ваши псевдонимы и функции, чтобы он мог дать вам лучший ответ (и я полагаю, что некоторые дистрибутивы Linux устанавливают некоторые глобальные псевдонимы для bashэтого) ,
zshимеет несколько операторов для расширения до пути исполняемых файлов: оператор = расширения имени файла и :cмодификатор расширения истории (здесь применяется к расширению параметра ):
$ print -r -- =ls
/bin/ls
$ cmd=ls; print -r -- $cmd:c
/bin/ls
zshВ zsh/parametersмодуле также вносится команда хэш-таблицы в качестве commandsассоциативного массива:
$ print -r -- $commands[ls]
/bin/ls
whatisУтилита (для одного в оболочке или плане 9 Unix V8 Bourne , кроме rc/ es) на самом деле не связана , как это только для документации (базы данных отбирает Whatis, то есть человек , страница синопсис).
whereisОн также был добавлен в 3BSDто же время, как whichесли бы он был написан C, но не cshиспользуется для одновременного поиска исполняемого файла, справочной страницы и исходного кода, но не на основе текущей среды. Итак, еще раз, это отвечает другой необходимости.
Теперь, на стандартном фронте, POSIX определяет command -vи -Vкоманды (которые используются, чтобы быть необязательным до POSIX.2008). UNIX определяет typeкоманду (без опции). Это все ( where, which, whenceне указаны в любом стандарте)
До некоторой версии, typeи command -vбыли необязательными в спецификации Linux Standard Base, которая объясняет, почему, например, некоторые старые версии posh(хотя и основанные на pdkshкоторых имели обе) не имели ни того, ни другого. command -vтакже был добавлен в некоторые реализации оболочки Bourne (например, в Solaris).
Статус сегодня
В настоящее время статус таков typeи command -vвездесущ во всех оболочках, похожих на Bourne (хотя, как отмечает @jarno, обратите внимание на caveat / bug, bashкогда он не в режиме POSIX или некоторые потомки оболочки Almquist ниже в комментариях). tcshэто единственная оболочка, которую вы хотели бы использовать which(поскольку ее там нет typeи whichона встроена).
В других , чем оболочках tcshи zsh, whichможет сказать вам путь данного исполняемого файла, пока нет псевдонима или функции к тому же имени в любом из наших ~/.cshrc, ~/.bashrcили любого другого файла запуск оболочки и не определить $PATHв вашем ~/.cshrc. Если у вас есть псевдоним или функция, определенная для него, он может или не может сказать вам об этом, или сказать вам неправильную вещь.
Если вы хотите знать обо всех командах с заданным именем, ничего переносимого нет. Вы можете использовать whereв tcshили zsh, type -aв bashили zsh, whence -aв ksh93 и в других оболочках, которые вы можете использовать typeв сочетании с which -aкоторым может работать.
рекомендации
Получение пути к исполняемому файлу
Теперь, чтобы получить путь к исполняемому файлу в скрипте, есть несколько предостережений:
ls=$(command -v ls)
был бы стандартный способ сделать это.
Однако есть несколько проблем:
- Невозможно узнать путь к исполняемому файлу без его выполнения. Все
type, which, command -v... все используют эвристики , чтобы узнать путь. Они перебирают $PATHкомпоненты и находят первый не каталогный файл, для которого у вас есть разрешение на выполнение. Однако, в зависимости от оболочки, когда дело доходит до выполнения команды, многие из них (Bourne, AT & T ksh, zsh, ash ...) будут просто выполнять их в порядке, $PATHпока execveсистемный вызов не вернется с ошибкой , Например, если $PATHсодержится /foo:/barи вы хотите выполнить ls, они сначала будут пытаться выполнить, /foo/lsили если это не удается /bar/ls. Сейчас исполнение/foo/lsможет произойти сбой, потому что у вас нет разрешения на выполнение, но и по многим другим причинам, например, это не допустимый исполняемый файл. command -v lsсообщит, /foo/lsесли у вас есть разрешение на выполнение /foo/ls, но запуск lsможет фактически выполняться, /bar/lsесли /foo/lsон не является допустимым исполняемым файлом.
- if
fooявляется встроенной функцией или функцией или псевдонимом, command -v fooвозвращает foo. С некоторыми оболочками, такими как ash, pdkshили zsh, он также может возвращать, fooесли $PATHвключает пустую строку и fooв текущем каталоге есть исполняемый файл. Есть некоторые обстоятельства, когда вам может понадобиться принять это во внимание. Имейте в виду, например, что список встроенных функций зависит от реализации оболочки (например, mountиногда встроен для busybox sh) и, например, bashможет получать функции из среды.
- Если
$PATHсодержит компоненты относительного пути (обычно .или пустую строку, которые оба ссылаются на текущий каталог, но могут быть чем угодно), в зависимости от оболочки, command -v cmdможет не выводиться абсолютный путь. Таким образом, путь, который вы получите в то время, когда вы бежите command -v, больше не будет действительным после вас cdгде-то еще.
- Анекдотические: с ksh93 оболочки, если
/opt/ast/bin(хотя точный путь может варьироваться в разных системах , я считаю) в вас $PATH, ksh93 будет предоставлять несколько дополнительных встроенных команд ( chmod, cmp, cat...), но command -v chmodвернется , /opt/ast/bin/chmodдаже если этот путь Безразлично» не существует.
Определение, существует ли команда
Чтобы узнать, существует ли данная команда стандартно, вы можете сделать:
if command -v given-command > /dev/null 2>&1; then
echo given-command is available
else
echo given-command is not available
fi
Где можно использовать which
(t)csh
В cshи у tcshвас нет большого выбора. В tcsh, это нормально, как whichвстроено. Во- первых csh, это будет системная whichкоманда, которая в некоторых случаях может не выполнять то, что вы хотите.
найти команды только в некоторых оболочках
Случай , когда это возможно , имеет смысл использовать which, если вы хотите знать , путь команды, игнорируя потенциальные внутренние команды или функции оболочки в bash, csh(не tcsh), dashили Bourneсценарии оболочки, то есть снаряды , которые не имеют whence -p(например , kshили zsh) , command -ev(like yash), whatis -p( rc, akanga) или встроенный which(like tcshили zsh) в системах, где whichдоступен и не является cshсценарием.
Если эти условия соблюдены, то:
echo=$(which echo)
даст вам путь к первому echoвходу $PATH(за исключением угловых случаев), независимо от того, является ли echoтакже встроенная оболочка / псевдоним / функция или нет.
В других оболочках вы бы предпочли:
- zsh :
echo==echoили echo=$commands[echo]илиecho=${${:-echo}:c}
- кш , зш :
echo=$(whence -p echo)
- Яш :
echo=$(command -ev echo)
- rc , akanga :
echo=`whatis -p echo`(остерегайтесь путей с пробелами)
- рыба :
set echo (type -fp echo)
Обратите внимание, что если все, что вы хотите сделать, это запустить эту echoкоманду, вам не нужно указывать ее путь, вы можете просто сделать:
env echo this is not echoed by the builtin echo
Например, с помощью tcsh, чтобы предотвратить использование встроенной функции which:
set Echo = "`env which echo`"
когда вам нужна внешняя команда
Другой случай, когда вы можете захотеть использовать whichэто когда вам действительно нужна внешняя команда. POSIX требует, чтобы все встроенные функции оболочки (например command) также были доступны как внешние команды, но, к сожалению, это не так во commandмногих системах. Например, редко можно найти commandкоманду в операционных системах на основе Linux, в то время как у большинства из них есть whichкоманда (хотя разные с разными параметрами и поведением).
Случаи, когда вам может понадобиться внешняя команда, будут выполняться там, где вы будете выполнять команду без вызова оболочки POSIX.
system("some command line"), popen()... функции C или различных языках действительно вызвать оболочку для разбора этой командной строки, так что system("command -v my-cmd")работают в них. Исключением является то, perlчто оптимизируется оболочка, если она не видит никаких специальных символов оболочки (кроме пробела). Это также относится к его оператору обратного вызова:
$ perl -le 'print system "command -v emacs"'
-1
$ perl -le 'print system ":;command -v emacs"'
/usr/bin/emacs
0
$ perl -e 'print `command -v emacs`'
$ perl -e 'print `:;command -v emacs`'
/usr/bin/emacs
Добавление :;вышеупомянутого заставляет perlвызывать там оболочку. Используя which, вам не придется использовать этот трюк.
whichпредполагают наличие интерактивного контекста оболочки. Этот вопрос помечен / переносимость. Поэтому я интерпретирую вопрос в этом контексте как «что использовать вместо того,whichчтобы найти первый исполняемый файл с заданным именем в$PATH». Большинство ответов и аргументов противwhichпсевдонимов, встроенных функций и функций, которые в большинстве реальных переносимых сценариев оболочки представляют только академический интерес. Локально определенные псевдонимы не наследуются при запуске сценария оболочки (если вы его не используете.).