Как проверить, существует ли программа из скрипта Bash? - PullRequest
1888 голосов
/ 27 февраля 2009

Как мне проверить, что программа существует, таким образом, что она либо возвратит ошибку и завершится, либо продолжит работу со сценарием?

Кажется, это должно быть легко, но это меня озадачило.

Ответы [ 34 ]

2635 голосов
/ 24 марта 2009

Ответ

POSIX совместимый:

command -v <the_command>

Для bash особых сред:

hash <the_command> # For regular commands. Or...
type <the_command> # To check built-ins and keywords

Объяснение

Избегайте which. Это не только внешний процесс, который вы запускаете для выполнения очень мало (то есть встроенные функции, такие как hash, type или command, значительно дешевле), вы также можете полагаться на встроенные функции, которые действительно делают то, что вы хотите, в то время как эффекты внешних команд могут легко варьироваться от системы к системе.

Зачем это нужно?

  • Многие операционные системы имеют which, который даже не устанавливает статус выхода , то есть if which foo даже не будет работать и всегда сообщает, что foo существует, даже если его нет (обратите внимание, что некоторые оболочки POSIX, кажется, делают это и для hash).
  • Многие операционные системы заставляют which делать нестандартные и злые вещи, например, изменять вывод или даже подключаться к менеджеру пакетов.

Итак, не используйте which. Вместо этого используйте один из них:

$ command -v foo >/dev/null 2>&1 || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }
$ type foo >/dev/null 2>&1 || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }
$ hash foo 2>/dev/null || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }

(Незначительное замечание: некоторые предполагают, что 2>&- - это то же самое 2>/dev/null, но короче - это не соответствует действительности . 2>&- закрывает FD 2, что вызывает ошибку в программе, когда он пытается записать в stderr, что сильно отличается от успешной записи в него и отбрасывания вывода (и опасно!))

Если ваш хэш-удар равен /bin/sh, вам следует позаботиться о том, что говорит POSIX. Коды выхода type и hash не очень хорошо определены в POSIX, и hash успешно завершается, когда команда не существует (еще не видел это с type). Состояние выхода command хорошо определено POSIX, так что, вероятно, наиболее безопасным является использование.

Если ваш скрипт использует bash, правила POSIX больше не имеют значения, и оба type и hash становятся совершенно безопасными для использования. type теперь имеет -P для поиска только PATH, а hash имеет побочный эффект, что расположение команды будет хэшировано (для более быстрого поиска при следующем использовании), что обычно хорошо, так как вы, вероятно, проверяете его существование, чтобы реально использовать его.

В качестве простого примера, вот функция, которая запускает gdate, если она существует, в противном случае date:

gnudate() {
    if hash gdate 2>/dev/null; then
        gdate "$@"
    else
        date "$@"
    fi
}
403 голосов
/ 05 ноября 2014

Ниже представлен переносимый способ проверить, существует ли команда в $PATH и является ли исполняемой:

[ -x "$(command -v foo)" ]

Пример:

if ! [ -x "$(command -v git)" ]; then
  echo 'Error: git is not installed.' >&2
  exit 1
fi

Проверка исполняемого файла необходима, потому что bash возвращает неисполняемый файл, если в $PATH.

не найдено исполняемого файла с таким именем.

Также обратите внимание, что если в $PATH ранее не существует исполняемого файла с тем же именем, что и у исполняемого файла, dash возвращает первый, даже если последний будет выполнен. Это ошибка, которая нарушает стандарт POSIX. [ Отчет об ошибке ] [ Стандарт ]

Кроме того, произойдет сбой, если искомая команда была определена как псевдоним.

196 голосов
/ 24 января 2011

Я согласен с lhunath не рекомендовать использование which, и его решение совершенно действительно для пользователей BASH . Однако, чтобы быть более переносимым, вместо него следует использовать command -v:

$ command -v foo >/dev/null 2>&1 || { echo "I require foo but it's not installed.  Aborting." >&2; exit 1; }

Команда command совместима с POSIX, ее спецификация приведена здесь: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html

Примечание: type соответствует POSIX, но type -P - нет.

84 голосов
/ 14 октября 2010

У меня есть функция, определенная в моем .bashrc, которая делает это проще.

command_exists () {
    type "$1" &> /dev/null ;
}

Вот пример того, как он используется (из моего .bash_profile.)

if command_exists mvim ; then
    export VISUAL="mvim --nofork"
fi
73 голосов
/ 27 февраля 2009

Это зависит от того, хотите ли вы узнать, существует ли он в одном из каталогов в переменной $PATH или знаете ли вы его абсолютное местоположение. Если вы хотите узнать, находится ли она в переменной $PATH, используйте

if which programname >/dev/null; then
    echo exists
else
    echo does not exist
fi

в противном случае используйте

if [ -x /path/to/programname ]; then
    echo exists
else
    echo does not exist
fi

Перенаправление на /dev/null/ в первом примере подавляет вывод программы which.

31 голосов
/ 08 декабря 2015

Расширяя ответы @ lhunath и @ GregV, вот код для людей, которые хотят легко поставить эту проверку в выражении if:

exists()
{
  command -v "$1" >/dev/null 2>&1
}

Вот как это использовать:

if exists bash; then
  echo 'Bash exists!'
else
  echo 'Your system does not have Bash'
fi
21 голосов
/ 27 февраля 2009

Попробуйте использовать:

test -x filename

или

[ -x filename ]

На странице руководства bash в разделе Условные выражения :

 -x file
          True if file exists and is executable.
15 голосов
/ 24 июня 2011

Чтобы использовать hash, как @ lhunath предлагает , в скрипте bash:

hash foo &> /dev/null
if [ $? -eq 1 ]; then
    echo >&2 "foo not found."
fi

Этот скрипт запускает hash и затем проверяет, равен ли код завершения самой последней команды значению, сохраненному в $?, 1. Если hash не найдет foo, код выхода будет 1. Если присутствует foo, код выхода будет 0.

&> /dev/null перенаправляет стандартную ошибку и стандартный вывод из hash, чтобы он не отображался на экране, а echo >&2 записывает сообщение в стандартную ошибку.

10 голосов
/ 11 июля 2009

Я никогда не заставлял вышеуказанные решения работать на коробке, к которой у меня есть доступ. Для одного типа был установлен (делает то, что делает больше). Так что встроенная директива необходима. Эта команда работает для меня:

if [ `builtin type -p vim` ]; then echo "TRUE"; else echo "FALSE"; fi
8 голосов
/ 08 июля 2013

Если вы проверите наличие программы, вы, вероятно, все равно будете запускать ее позже. Почему бы не попробовать запустить его в первую очередь?

if foo --version >/dev/null 2>&1; then
    echo Found
else
    echo Not found
fi

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

Кроме того, вы можете получить полезный результат от вашей программы, например, от ее версии.

Конечно, недостатки в том, что некоторые программы могут быть тяжелыми для запуска, а некоторые не имеют опции --version для немедленного (и успешного) выхода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...