Как проверить, существует ли программа из сценария Bash?
Как я могу проверить, что программа существует, таким образом, чтобы либо вернуть ошибку и выйти, либо продолжить сценарий?
кажется, это должно быть легко, но это ставило меня в тупик.
30 ответов
ответ
совместимость с POSIX:
command -v <the_command>
на bash
особых условиях:
hash <the_command> # For regular commands. Or...
type <the_command> # To check built-ins and keywords
объяснение
избежать which
. Мало того, что это внешний процесс, который вы запускаете, делая очень мало (что означает builtins, такие как hash
, type
или command
путь дешевле), вы можете также положиться на builtins фактически для того чтобы сделать чего вы хотите, пока влияния внешних команд могут легко поменять от системы к система.
зачем?
- многие операционные системы имеют
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
}
ниже приведен портативный способ проверить, существует ли команда в $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. [сообщить об ошибке] [стандартный]
кроме того, это не удастся, если команда, которую вы ищете, была определена в качестве псевдонима.
Я согласен с лхунатхом, чтобы препятствовать использованию 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
нет.
у меня есть функция, определенная в мой .bashrc, что делает это проще.
command_exists () {
type "" &> /dev/null ;
}
вот пример того, как он используется (из моего .bash_profile
.)
if command_exists mvim ; then
export VISUAL="mvim --nofork"
fi
это зависит от того, хотите ли вы знать, существует ли он в одном из каталогов $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/
в первом примере подавляет вывод
расширяя ответы @lhunath и @GregV, вот код для людей, которые хотят легко поместить эту проверку внутри if
о себе:
exists()
{
command -v "" >/dev/null 2>&1
}
вот как его использовать:
if exists bash; then
echo 'Bash exists!'
else
echo 'Your system does not have Bash'
fi
попробуйте использовать:
test -x filename
или
[ -x filename ]
из bash manpage под Условные Выражения:
-x file True if file exists and is executable.
использовать hash
, as @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
записывает сообщение в стандартную ошибку.
Я никогда не получал выше решения для работы на поле у меня есть доступ. Во-первых, тип был установлен (что делает больше). Поэтому необходима директива builtin. Эта команда работает для меня:
if [ `builtin type -p vim` ]; then echo "TRUE"; else echo "FALSE"; fi
Если вы проверяете наличие программы, вы, вероятно, собираетесь запустить ее позже в любом случае. Почему бы не попробовать запустить его в первую очередь?
if foo --version >/dev/null 2>&1; then
echo Found
else
echo Not found
fi
это более надежная проверка, что программа работает, чем просто глядя на каталоги пути и разрешения файлов.
плюс вы можете получить полезный результат от вашей программы, такие как версия.
конечно, недостатки в том, что некоторые программы могут быть тяжелыми для запуска, а некоторые не имеют для немедленно (и успешно) выйти.
проверьте наличие нескольких зависимостей и сообщите статус конечным пользователям
for cmd in "latex" "pandoc"; do
printf "%-10s" "$cmd"
if hash "$cmd" 2>/dev/null; then printf "OK\n"; else printf "missing\n"; fi
done
пример вывода:
latex OK
pandoc missing
изменить 10
максимальной длины команды. Не автоматический, потому что я не вижу многословного способа POSIX сделать это:
как выровнять столбцы таблицы, разделенной пробелом, в Bash?
для тех, кто заинтересован, ни одна из вышеперечисленных методологий не работает, если вы хотите обнаружить установленную библиотеку. Я предполагаю, что вам остается либо физически проверить путь (потенциально для заголовочных файлов и тому подобного), либо что-то вроде этого (если вы находитесь в дистрибутиве на основе Debian):
dpkg --status libdb-dev | grep -q not-installed
if [ $? -eq 0 ]; then
apt-get install libdb-dev
fi
Как вы можете видеть из вышеизложенного, ответ "0" из запроса означает, что пакет не установлен. Это функция "grep" - " 0 "означает, что совпадение было найдено," 1 " означает, что совпадение не было найдено.
hash foo 2>/dev/null
: работы с ЗШ, Баш, черточкой и золой.
type -p foo
: похоже, он работает с zsh, bash и ash (busybox), но не dash (он интерпретирует -p
в качестве аргумента).
command -v foo
: работает с zsh, bash, dash, но не ash (busybox) (-ash: command: not found
).
также обратите внимание, что builtin
не работает с ash
и dash
.
на 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
хорошая вещь о том, что он выясняет, доступен ли исполняемый файл в среде, в которой он запущен, - экономит несколько проблем...
Адам
Я бы сказал, что нет портативного и 100% надежного способа из-за болтающихся alias
es. Например:
alias john='ls --color'
alias paul='george -F'
alias george='ls -h'
alias ringo=/
конечно, только последний из них проблематичен (не обижайтесь на Ринго!) Но все они действительны alias
ES с точки зрения command -v
.
для того, чтобы отклонить болтающиеся, как ringo
, мы должны проанализировать выход встроенной оболочки и рекурсия в них (command -v
не превосходит alias
здесь.) Там нет портативных решение для него, и даже Bash-специфическое решение довольно утомительно.
обратите внимание, что такое решение будет безоговорочно отклонять alias ls='ls -F'
test() { command -v | grep -qv alias }
если нет внешнего type
доступна команда (как само собой разумеющееся здесь), мы можем использовать POSIX совместимый env -i sh -c 'type cmd 1>/dev/null 2>&1'
:
# portable version of Bash's type -P cmd (without output on stdout)
typep() {
command -p env -i PATH="$PATH" sh -c '
export LC_ALL=C LANG=C
cmd=""
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
' _ "" || 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
.
имитировать Баша 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; }
хэш-вариант имеет одну ловушку: в командной строке вы можете, например, ввести
one_folder/process
для выполнения процесса. Для этого родительская папка one_folder должна быть в $PATH. Но когда вы пытаетесь хэшировать эту команду, она всегда будет успешной:
hash one_folder/process; echo $? # will always output '0'
Я поддерживаю использование "command-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
моя настройка для сервера debian. у меня была проблема, когда несколько пакетов содержат одно и то же имя. например apache2 не. так что это было мое решение.
function _apt_install() {
apt-get install -y > /dev/null
}
function _apt_install_norecommends() {
apt-get install -y --no-install-recommends > /dev/null
}
function _apt_available() {
if [ `apt-cache search | grep -o "" | uniq | wc -l` = "1" ]; then
echo "Package is available : "
PACKAGE_INSTALL="1"
else
echo "Package is NOT available for install"
echo "We can not continue without this package..."
echo "Exitting now.."
exit 0
fi
}
function _package_install {
_apt_available
if [ "${PACKAGE_INSTALL}" = "1" ]; then
if [ "$(dpkg-query -l | tail -n1 | cut -c1-2)" = "ii" ]; then
echo "package is already_installed: "
else
echo "installing package : , please wait.."
_apt_install
sleep 0.5
fi
fi
}
function _package_install_no_recommends {
_apt_available
if [ "${PACKAGE_INSTALL}" = "1" ]; then
if [ "$(dpkg-query -l | tail -n1 | cut -c1-2)" = "ii" ]; then
echo "package is already_installed: "
else
echo "installing package : , please wait.."
_apt_install_norecommends
sleep 0.5
fi
fi
}
Если вы, ребята, не можете заставить вещи выше / ниже работать и вытаскивать волосы из спины, попробуйте выполнить ту же команду, используя bash -c
. Просто посмотрите на этот somnambular бред, это то, что на самом деле происходит, когда вы запустите $(подкоманды):
первый. Это может дать вам совершенно другой выход.
$ 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
в случае, если вы хотите проверить, существует ли программа и это действительно программа, а не встроенная команда bash, потом command
, type
и hash
не подходят для тестирования, поскольку все они возвращают 0 статус выхода для встроенных команд.
например, есть времени программа, которая предлагает больше возможностей, чем времени встроенная команда. Чтобы проверить, существует ли программа, Я бы предложил использовать 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
здесь есть тонна опций, но я был удивлен, что нет быстрых однострочных, это то, что я использовал в начале своих сценариев:
[[ "$(command -v mvn)" ]] || { echo "mvn is not installed" 1>&2 ; exit 1; }
[[ "$(command -v java)" ]] || { echo "java is not installed" 1>&2 ; exit 1; }
это основано на выбранном ответе здесь и другом источнике (и я немного играю).
надеюсь, что это будет удобно для других.
Я использую это, потому что это очень легко:
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 с другой стороны, если команда не найдена, она повторяет статус только для stderr.
скрипт
#!/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=; 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.
command-v отлично работает, если параметр POSIX_BUILTINS установлен для <command>
для проверки, но может потерпеть неудачу, если нет. (он работал для меня в течение многих лет, но недавно столкнулся с тем, где он не работал).
Я считаю, что следующее является более отказоустойчивым:
test -x $(which <command>)
поскольку он проверяет 3 вещи: путь, выполнение и разрешение.
Я использую очень удобную и короткую версию:
dpkg -s curl 2>/dev/null >/dev/null || apt-get -y install curl
так легко, если нужно проверить только одну программу.
удивительный ответ и объяснение @lhunath. Спас мне жизнь. Я немного его раздвинул. Не мог контролировать себя, разделяя его-надеясь, что это может быть полезно для кого-то. Если кому-то нужно проверить (массив) несколько программ, вот быстрый сниппет.
что он делает? (1) Читайте множество программ. (2) показать сообщения за неудачная программа. (3) предложите пользователю продолжить (принудительный цикл) параметры y/n для утверждения остальных программы.
#!/bin/bash
proginstalldir=/full/dir/path/of/installation
progsbindir=$proginstalldir/bin
echo -e "\nMy install directory - $proginstalldir"
echo -e "My binaries directory - $progsbindir"
VerifyInstall () {
clear
myprogs=( program1 program2 program3 program4 program5 programn );
echo -e "\nValidation of my programs started...."
for ((i=0; i<${#myprogs[@]}; i++)) ; do
command -v $progsbindir/${myprogs[i]} >/dev/null && echo -e "Validating....\t${myprogs[i]}\tSUCCESSFUL" || { echo -e "Validating.... \t${myprogs[i]}\tFAILED" >&2;
while true; do
printf "%s: " "ERROR.... Validation FAILED for ${myprogs[i]} !!!! Continue?"; read yn;
case $yn in [Yy] ) echo -e "Please wait..." ; break;;
[Nn]) echo -e "\n\n#################################\n## Validation Failed .. !! ##\n#################################\n\n" ; exit 1; break;;
*) echo -e "\nPlease answer y or n then press Enter\n"; esac; done; >&2; }; done
sleep 2
}
VerifyInstall