Ось все, про що ви ніколи не думали, що ніколи не захочете знати про це:
Підсумок
Щоб отримати ім'я виконавчого файлу у сценарії оболонки, подібному до 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
отримав набагато більшу популярність, ніж оболонка Борна, оскільки (хоча він мав жахливо синтаксис, ніж оболонка Борна), він додав багато більш зручних і приємних функцій для інтерактивного використання.
У 3BSD
1980 році для користувачів було додано 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
робить його менш простим у використанні в сценаріях.
Оболонка Борна для Unix версії 8 (не випущена в дикій природі) повинна була type
перейменована в whatis
. І оболонка Plan9 (колишній наступник Unix) rc
(і її похідні, як akanga
і es
) також є whatis
.
Оболонка Korn (підмножина, на якій засноване визначення POSIX sh), розроблена в середині 80-х, але не була широко доступною до 1988 року, додала багато csh
функцій (редактор рядків, псевдоніми ...) поверх оболонки Bourne . Він додав власний whence
вбудований (на додаток до type
), який взяв кілька варіантів ( -v
забезпечити type
подібний багатослівний вихід і -p
шукати тільки виконувані файли (не псевдоніми / функції ...)).
Одночасно з потрясінням щодо проблем з авторським правом між AT&T та Berkeley, в кінці 80-х на початку 90-х з'явилося кілька реалізацій безкоштовних програмних оболонок. Вся оболонка Almquist (зола, яка повинна бути заміною оболонки Борна в BSD), реалізація ksh (pdksh) bash
(спонсорована FSF) у публічному доступі , zsh
з'явилася між 1989 і 1991 роками.
Еш, хоч і мала бути заміною оболонки Борна, не мала 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 (поруч із вбудованими в tcsh
та в 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
є всюдисущим у всіх оболонках Борна (хоча, як зазначає @jarno, зверніть увагу на застереження / помилку, bash
коли вони не перебувають у режимі POSIX або деякі нащадки оболонки Алквіста нижче в коментарях). 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
не є дійсним виконуваним файлом.
- якщо
foo
вбудований або функція чи псевдонім, command -v foo
повертається foo
. З такими оболонками, як ash
, pdksh
або zsh
, він також може повернутися, foo
якщо він $PATH
містить порожній рядок і foo
в поточному каталозі є виконавчий файл. Є деякі обставини, коли вам може знадобитися це врахувати. Майте на увазі, наприклад, що список вбудованих файлів змінюється залежно від реалізації оболонки (наприклад, mount
іноді вбудований для зайнятої скриньки 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
навіть якщо цей шлях Байдуже » t існують.
Визначення, чи існує команда
Щоб дізнатися, чи дана команда існує стандартно, ви можете зробити:
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
(як yash
), whatis -p
( rc
, akanga
) або вбудований which
(як tcsh
або zsh
) в системах, де which
доступний і не є csh
сценарієм.
Якщо ці умови виконані, то:
echo=$(which echo)
дасть вам шлях першого echo
в $PATH
(за винятком кутових випадків), незалежно від того, чи echo
буває оболонка вбудована / псевдонім / функція чи ні.
В інших оболонках вам краще:
- zsh :
echo==echo
або echo=$commands[echo]
абоecho=${${:-echo}:c}
- ksh , zsh :
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
оптимізацією оболонки, якщо вона не бачить спеціального символу оболонки (крім пробілу). Це стосується також оператора backtick:
$ 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
спілкування з псевдонімами, вбудованими функціями та функціями, які в більшості реальних портативних скриптів оболонки викликають науковий інтерес. Локально визначені псевдоніми не успадковуються під час запуску сценарію оболонки (якщо ви не використовуєте його.
).