Как проверить, существует ли программа из скрипта Bash?
Как я могу проверить, существует ли программа, которая будет либо возвращать ошибку и выйти, либо продолжить с помощью script?
Кажется, что это должно быть легко, но это меня колотило.
Ответы
Ответ 1
Ответ
Совместимость с 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 not installed. Aborting."; exit 1; }
$ type foo >/dev/null 2>&1 || { echo >&2 "I require foo but it not installed. Aborting."; exit 1; }
$ hash foo 2>/dev/null || { echo >&2 "I require foo but it not installed. Aborting."; exit 1; }
(Незначительная сторона примечания: некоторые предполагают, что 2>&-
является тем же самым 2>/dev/null
, но короче - это неверно. 2>&-
закрывает FD 2, который вызывает ошибку в программе, когда он пытается писать в stderr, что сильно отличается от успешной записи на него и отбрасывания вывода (и опасного!))
Если ваш хеш-трек /bin/sh
, тогда вам следует заботиться о том, что говорит POSIX. type
и hash
коды выхода не очень хорошо определены POSIX, и hash
, как видно, успешно завершает работу, когда команда не существует (еще не видели этого с type
). command
статус выхода хорошо определен POSIX, так что, вероятно, самый безопасный для использования.
Если ваш script использует bash
, хотя правила POSIX больше не имеют значения, и оба type
и hash
становятся совершенно безопасными в использовании. type
теперь имеет -P
для поиска только PATH
, а hash
имеет побочный эффект, что местоположение команды будет хэшировано (для более быстрого поиска в следующий раз, когда вы его используете), что обычно хорошо, поскольку вы, вероятно, проверяете его существование, чтобы фактически использовать его.
Как простой пример, здесь функция, которая запускает gdate
, если она существует, в противном случае date
:
gnudate() {
if hash gdate 2>/dev/null; then
gdate "[email protected]"
else
date "[email protected]"
fi
}
Ответ 2
Ниже приведен переносимый способ проверить, существует ли команда в $PATH
и является исполняемым:
[ -x "$(command -v foo)" ]
Пример:
if ! [ -x "$(command -v git)" ]; then
echo 'Error: git is not installed.' >&2
exit 1
fi
Требуется выполнить проверку, потому что bash возвращает неисполняемый файл, если в $PATH
нет исполняемого файла с этим именем.
Также обратите внимание, что если неиспользуемый файл с тем же именем, что и исполняемый файл, существует ранее в $PATH
, черточка возвращает первый, хотя последний будет выполнен. Это ошибка и является нарушением стандарта POSIX. [Отчет об ошибках] [Стандарт]
Кроме того, это не удастся, если команда, которую вы ищете, была определена как псевдоним.
Ответ 3
Я согласен с lhunath, чтобы препятствовать использованию which
, и его решение отлично подходит для пользователей BASH. Однако, чтобы быть более портативным, вместо этого следует использовать command -v
:
$ command -v foo >/dev/null 2>&1 || { echo "I require foo but it not installed. Aborting." >&2; exit 1; }
Команда command
совместима с POSIX, см. здесь для ее спецификации: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html
Примечание: type
совместим с POSIX, но type -P
нет.
Ответ 4
У меня есть функция, определенная в моем .bashrc, что делает это проще.
command_exists () {
type "$1" &> /dev/null ;
}
Вот пример того, как он использовался (из моего .bash_profile
.)
if command_exists mvim ; then
export VISUAL="mvim --nofork"
fi
Ответ 5
Это зависит от того, хотите ли вы знать, существует ли она в одном из каталогов в переменной $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
.
Ответ 6
Развернувшись на ответах @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
Ответ 7
Попробуйте использовать:
test -x filename
или
[ -x filename ]
На странице bash man Условные выражения:
-x file
True if file exists and is executable.
Ответ 8
Чтобы использовать hash
, как @lhunath предлагает, в bash script:
hash foo &> /dev/null
if [ $? -eq 1 ]; then
echo >&2 "foo not found."
fi
Этот script запускает hash
, а затем проверяет, является ли код выхода самой последней команды, значение, хранящееся в $?
, равно 1
. Если hash
не находит foo
, код выхода будет 1
. Если присутствует foo
, код выхода будет 0
.
&> /dev/null
перенаправляет стандартную ошибку и стандартный вывод из hash
, чтобы он не отображался на экране, а echo >&2
записывал сообщение в стандартную ошибку.
Ответ 9
Я никогда не получал вышеуказанные решения для работы с коробкой, к которой у меня есть доступ. Например, тип был установлен (что делает больше). Поэтому необходима встроенная директива. Эта команда работает для меня:
if [ `builtin type -p vim` ]; then echo "TRUE"; else echo "FALSE"; fi
Ответ 10
Если вы проверяете существование программы, вы, вероятно, собираетесь запустить ее позже. Почему бы не попробовать запустить его в первую очередь?
if foo --version >/dev/null 2>&1; then
echo Found
else
echo Not found
fi
Это более надежная проверка, что программа работает, а не просто просмотр каталогов PATH и разрешений файлов.
Кроме того, вы можете получить полезный результат из своей программы, например, ее версию.
Конечно, недостатки в том, что некоторые программы могут быть тяжелыми для запуска, а некоторые не имеют опции --version
для немедленного (и успешного) выхода.
Ответ 11
Проверьте наличие нескольких зависимостей и сообщите статус конечным пользователям.
for cmd in latex pandoc; do
printf '%-10s' "$cmd"
if hash "$cmd" 2>/dev/null; then
echo OK
else
echo missing
fi
done
Образец вывода:
latex OK
pandoc missing
Отрегулируйте 10
до максимальной длины команды. Не автоматический, потому что я не вижу не многословного способа POSIX сделать это: Как выровнять столбцы разделенной пробелами таблицы в Bash?
Ответ 12
Почему бы не использовать встроенные функции Bash, если вы можете?
which programname
...
type -P programname
Ответ 13
hash foo 2>/dev/null
: работает с zsh, bash, тире и золе.
type -p foo
: он работает с zsh, bash и ash (busybox), но не тире (он интерпретирует -p
как аргумент).
command -v foo
: работает с zsh, bash, тире, но не золой (busybox) (-ash: command: not found
).
Также обратите внимание, что builtin
недоступно с ash
и dash
.
Ответ 14
Для тех, кто заинтересован, ни одна из вышеперечисленных методологий не работает, если вы хотите обнаружить установленную библиотеку. Я полагаю, что вам остается либо физически проверять путь (возможно, для файлов заголовков и т.д.), Либо что-то вроде этого (если вы используете дистрибутив на основе Debian):
dpkg --status libdb-dev | grep -q not-installed
if [ $? -eq 0 ]; then
apt-get install libdb-dev
fi
Как видно из вышесказанного, ответ "0" из запроса означает, что пакет не установлен. Это функция "grep" - "0" означает совпадение, "1" означает, что совпадение не найдено.
Ответ 15
Команда which
может быть полезна. человек, который
Он возвращает 0, если исполняемый файл найден, 1, если он не найден или не выполним:
NAME
which - locate a command
SYNOPSIS
which [-a] filename ...
DESCRIPTION
which returns the pathnames of the files which would be executed in the
current environment, had its arguments been given as commands in a
strictly POSIX-conformant shell. It does this by searching the PATH
for executable files matching the names of the arguments.
OPTIONS
-a print all matching pathnames of each argument
EXIT STATUS
0 if all specified commands are found and executable
1 if one or more specified commands is nonexistent or not exe-
cutable
2 if an invalid option is specified
Хорошая вещь в том, что она выясняет, доступен ли исполняемый файл в среде, в которой выполняется, - сохраняет несколько проблем...
-Adam
Ответ 16
Я бы сказал, что нет портативного и 100% надежного способа из-за зависания alias
es. Например:
alias john='ls --color'
alias paul='george -F'
alias george='ls -h'
alias ringo=/
Конечно, только последний является проблематичным (без обид на Ringo!) Но все они действительны alias
es с точки зрения command -v
.
Чтобы отклонить оборванные, такие как ringo
, мы должны проанализировать вывод встроенной команды alias
и recurse в оболочку оболочки (command -v
здесь не превосходит alias
). Там нет портативное решение для него, и даже Bash -специфическое решение довольно утомительно.
Обратите внимание, что такое решение безоговорочно отвергает alias ls='ls -F'
test() { command -v $1 | grep -qv alias }
Ответ 17
Если внешняя команда type
недоступна (как принято в виде здесь), мы можем использовать POSIX-совместимый env -i sh -c 'type cmd 1>/dev/null 2>&1'
:
# portable version of Bash type -P cmd (without output on stdout)
typep() {
command -p env -i PATH="$PATH" sh -c '
export LC_ALL=C LANG=C
cmd="$1"
cmd="`type "$cmd" 2>/dev/null || { echo "error: command $cmd not found; exiting ..." 1>&2; exit 1; }`"
[ $? != 0 ] && exit 1
case "$cmd" in
*\ /*) exit 0;;
*) printf "%s\n" "error: $cmd" 1>&2; exit 1;;
esac
' _ "$1" || exit 1
}
# get your standard $PATH value
#PATH="$(command -p getconf PATH)"
typep ls
typep builtin
typep ls-temp
По крайней мере, в Mac OS X 10.6.8 с использованием Bash 4.2.24 (2) command -v ls
не соответствует перемещенному /bin/ls-temp
.
Ответ 18
Чтобы подражать Bash type -P cmd
, мы можем использовать POSIX-совместимый env -i type cmd 1>/dev/null 2>&1
.
man env
# "The option '-i' causes env to completely ignore the environment it inherits."
# In other words, there are no aliases or functions to be looked up by the type command.
ls() { echo 'Hello, world!'; }
ls
type ls
env -i type ls
cmd=ls
cmd=lsx
env -i type $cmd 1>/dev/null 2>&1 || { echo "$cmd not found"; exit 1; }
Ответ 19
В хэш-варианте есть одна ошибка: в командной строке вы можете, например, ввести
one_folder/process
чтобы выполнить процесс. Для этого родительская папка one_folder должна находиться в $PATH. Но когда вы пытаетесь хешировать эту команду, она всегда будет успешной:
hash one_folder/process; echo $? # will always output '0'
Ответ 20
Во-вторых, использование команды -v. Например. например:
md=$(command -v mkdirhier) ; alias md=${md:=mkdir} # bash
emacs="$(command -v emacs) -nw" || emacs=nano
alias e=$emacs
[[ -z $(command -v jed) ]] && alias jed=$emacs
Ответ 21
моя настройка для сервера debian.
У меня возникла проблема, когда несколько пакетов содержат одно и то же имя.
например apache2.
так это было моим решением.
function _apt_install() {
apt-get install -y $1 > /dev/null
}
function _apt_install_norecommends() {
apt-get install -y --no-install-recommends $1 > /dev/null
}
function _apt_available() {
if [ `apt-cache search $1 | grep -o "$1" | uniq | wc -l` = "1" ]; then
echo "Package is available : $1"
PACKAGE_INSTALL="1"
else
echo "Package $1 is NOT available for install"
echo "We can not continue without this package..."
echo "Exitting now.."
exit 0
fi
}
function _package_install {
_apt_available $1
if [ "${PACKAGE_INSTALL}" = "1" ]; then
if [ "$(dpkg-query -l $1 | tail -n1 | cut -c1-2)" = "ii" ]; then
echo "package is already_installed: $1"
else
echo "installing package : $1, please wait.."
_apt_install $1
sleep 0.5
fi
fi
}
function _package_install_no_recommends {
_apt_available $1
if [ "${PACKAGE_INSTALL}" = "1" ]; then
if [ "$(dpkg-query -l $1 | tail -n1 | cut -c1-2)" = "ii" ]; then
echo "package is already_installed: $1"
else
echo "installing package : $1, please wait.."
_apt_install_norecommends $1
sleep 0.5
fi
fi
}
Ответ 22
Если вы не можете заставить вещи выше/ниже работать и вытаскивать волосы из своей спины, попробуйте выполнить ту же команду, используя bash -c
. Просто посмотрите на этот сомнительный бред, это то, что действительно происходит при запуске $(sub-command):
Во-первых. Это может дать вам совершенно другой выход.
$ command -v ls
alias ls='ls --color=auto'
$ bash -c "command -v ls"
/bin/ls
Во-вторых. Он не может дать вам никакого вывода.
$ command -v nvm
nvm
$ bash -c "command -v nvm"
$ bash -c "nvm --help"
bash: nvm: command not found
Ответ 23
Если вы хотите проверить, существует ли программа и действительно программа, а не встроенная команда bash, то command
, type
и hash
не являются подходящие для тестирования, поскольку все они возвращают 0 статус выхода для встроенных команд.
Например, существует программа time, которая предлагает больше возможностей, чем встроенная команда time. Чтобы проверить, существует ли программа, я бы предложил использовать which
, как в следующем примере:
# first check if the time program exists
timeProg=`which time`
if [ "$timeProg" = "" ]
then
echo "The time program does not exist on this system."
exit 1
fi
# invoke the time program
$timeProg --quiet -o result.txt -f "%S %U + p" du -sk ~
echo "Total CPU time: `dc -f result.txt` seconds"
rm result.txt
Ответ 24
Здесь куча вариантов, но я не удивился, когда не было быстрых однострочных символов, это то, что я использовал в начале своих сценариев: [[ "$(command -v mvn)" ]] || { echo "mvn is not installed" 1>&2; exit 1; } [[ "$(command -v java)" ]] || { echo "java is not installed" 1>&2; exit 1; }
[[ "$(command -v mvn)" ]] || { echo "mvn is not installed" 1>&2; exit 1; } [[ "$(command -v java)" ]] || { echo "java is not installed" 1>&2; exit 1; }
это основано на выбранном здесь ответе и другом источнике (и я немного поигрался).
надеюсь, что это будет полезно для других.
Ответ 25
Он сообщит в зависимости от местоположения, если программа существует или нет
if [ -x /usr/bin/yum ]; then
echo This is Centos
fi
Ответ 26
Я использую это, потому что это очень просто:
if [ `LANG=C type example 2>/dev/null|wc -l` = 1 ];then echo exists;else echo "not exists";fi
или
if [ `LANG=C type example 2>/dev/null|wc -l` = 1 ];then
echo exists
else echo "not exists"
fi
Он использует shell builtin и статус программного эха для stdout и ничего для stderr с другой стороны, если команда не найдена, она имеет статус echos только для stderr.
Ответ 27
Script
#!/bin/bash
# Commands found in the hash table are checked for existence before being
# executed and non-existence forces a normal PATH search.
shopt -s checkhash
function exists() {
local mycomm=$1; shift || return 1
hash $mycomm 2>/dev/null || \
printf "\xe2\x9c\x98 [ABRT]: $mycomm: command does not exist\n"; return 1;
}
readonly -f exists
exists notacmd
exists bash
hash
bash -c 'printf "Fin.\n"'
Результат
✘ [ABRT]: notacmd: command does not exist
hits command
0 /usr/bin/bash
Fin.
Ответ 28
Команда -v работает нормально, если для параметра <command>
задана опция POSIX_BUILTINS, но может произойти сбой, если нет. (он работал у меня годами, но недавно столкнулся с тем, где он не работал).
Я считаю следующее более надежным:
test -x $(which <command>)
Так как он проверяет 3 вещи: путь, существование и разрешение на выполнение.
Ответ 29
Привет:
if [[ 'command --help' ]]; then
echo "This command exists"
else
echo "This command does not exist";
fi
Поместите рабочий переключатель, например, "--help" или "-v" в проверку if: if [[ command --help
]]; затем
Ответ 30
Мне пришлось проверить, был ли установлен git
как часть развертывания нашего CI-сервера. Мой последний bash script был следующим (сервер Ubuntu):
if ! builtin type -p git &>/dev/null; then
sudo apt-get -y install git-core
fi
Надеюсь, что это поможет кому-то еще!