Как бы я проверил, что программа существует, таким образом, что либо вернет ошибку и выйдет, либо продолжит работу со сценарием?
Кажется, это должно быть легко, но это меня озадачило.
Как бы я проверил, что программа существует, таким образом, что либо вернет ошибку и выйдет, либо продолжит работу со сценарием?
Кажется, это должно быть легко, но это меня озадачило.
Ответы:
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
}
2>&- вывода в bash: 1) («закрыть дескриптор выходного файла 2», который является stderr), имеет тот же результат, что и 2> /dev/null; 2) >&2ярлык для 1>&2, который вы можете распознать как «перенаправить стандартный вывод в stderr». Для получения дополнительной информации см. Страницу перенаправления ввода-вывода Advanced Bash Scripting Guide .
while read element ; do .. done <<< $(echo ${ArrayVar[*]}), for word in $(fgrep -l $ORIGINAL *.txt), ls -l "$directory" | sed 1d , {{для а seq $BEGIN $END}}, ... Многие пытались связаться с авторами и предложить улучшения , но это не вики и запросы высадились на глухие уши.
2>&-это не то же самое, что 2>/dev/null. Первый закрывает дескриптор файла, а второй просто перенаправляет его на /dev/null. Вы можете не увидеть ошибку, потому что программа пытается сообщить вам на stderr, что stderr закрыт.
Ниже представлен переносимый способ проверить, существует ли команда $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. [ Отчет об ошибке ] [ Стандарт ]
Кроме того, произойдет сбой, если искомая команда была определена как псевдоним.
command -vпуть даже для неисполняемого файла? То есть -x действительно нужен?
-xпроверяет, является ли файл исполняемым, и это был вопрос.
commandсамо по себе будет проверять его исполняемость - не так ли?
$PATHпри выполнении команды. Тем не менее, поведение command -vочень противоречиво. В dash он возвращает первый соответствующий файл $PATH, независимо от того, исполняемый он или нет. В bash он возвращает первое совпадение исполняемого файла $PATH, но если его нет, он может вернуть неисполняемый файл. А в zsh он никогда не вернет неисполняемый файл.
dashединственный из этих трех не поддерживает POSIX; [ -x "$(command -v COMMANDNAME)"]будет работать в двух других. Похоже, что об этой ошибке уже сообщалось, но она еще не получила никаких ответов: bugs.debian.org/cgi-bin/bugreport.cgi?bug=874264
Я согласен с 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. Смотрите здесь для его спецификации: команда - выполнить простую команду
Примечание: typeсовместимо с POSIX, но type -Pэто не так.
exit 1;убивает xterm, если вызывается оттуда.
&>/dev/null. Тем не менее, я согласен с вами, что действительно важно, это переносимость, я соответственно отредактировал свой ответ, теперь используя стандартный sh redirect >/dev/null 2>&1.
У меня есть функция, определенная в моем .bashrc, которая делает это проще.
command_exists () {
type "$1" &> /dev/null ;
}
Вот пример того, как это используется (из моего .bash_profile.)
if command_exists mvim ; then
export VISUAL="mvim --nofork"
fi
&>?
&>может быть недоступно в вашей версии Bash. Код Марчелло должен работать нормально; это делает то же самое.
then. Посмотрите этот ответ, если вам требуется, чтобы исполняемый файл существовал в $PATH.
Это зависит от того, хотите ли вы узнать, существует ли он в одном из каталогов в $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программы.
Расширяя ответы @ 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
commandудается даже для псевдонимов, что может быть несколько нелогично. Проверка существования в интерактивной оболочке даст другие результаты, чем когда вы перемещаете ее в скрипт.
shopt -u expand_aliasesигнорирующие / скрывающие псевдонимы (как alias ls='ls -F'упомянуто в другом ответе) и shopt -s expand_aliasesразрешаю их через command -v. Так что, возможно, его следует установить до проверки и отменить после, хотя это может повлиять на возвращаемое значение функции, если вы не захватите и не вернете выходные данные вызова команды в явном виде.
Попробуйте использовать:
test -x filename
или
[ -x filename ]
Из справочной страницы Bash под условными выражениями :
-x file True if file exists and is executable.
Чтобы использовать 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записывает сообщение в стандартный поток ошибок.
if hash foo &> /dev/null; then ...?
Я никогда не получал предыдущие ответы, чтобы работать над коробкой, к которой у меня есть доступ. Для одного typeбыл установлен (делает то, что moreделает). Так что встроенная директива необходима. Эта команда работает для меня:
if [ `builtin type -p vim` ]; then echo "TRUE"; else echo "FALSE"; fi
ifсинтаксиса, просто используйте if builtin type -p vim; then .... И обратные ссылки - действительно древний и устаревший синтаксис, $()поддерживаемый даже shво всех современных системах.
Проверьте наличие нескольких зависимостей и сообщите статус конечным пользователям.
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?
Проверьте, установлены ли некоторые aptпакеты, dpkg -sи установите их иначе .
См. Проверьте, установлен ли пакет apt-get, а затем установите его, если он не в Linux.
Ранее упоминалось по адресу: Как я могу проверить, существует ли программа из скрипта Bash?
column -t(часть util-linux).
Если вы проверите наличие программы, вы, вероятно, все равно будете запускать ее позже. Почему бы не попробовать запустить его в первую очередь?
if foo --version >/dev/null 2>&1; then
echo Found
else
echo Not found
fi
Это более надежная проверка запуска программы, чем просто просмотр каталогов PATH и прав доступа к файлам.
Кроме того, вы можете получить полезный результат от вашей программы, например, ее версию.
Конечно, недостатки заключаются в том, что некоторые программы могут быть тяжелыми для запуска, а некоторые не имеют --versionвозможности немедленно (и успешно) выйти.
hash foo 2>/dev/null: работает с Z shell (Zsh), Bash, Dash и Ash .
type -p foo: он работает с Z shell, Bash и ash ( BusyBox ), но не с Dash (это интерпретируется -pкак аргумент).
command -v foo: работает с Z shell, Bash, Dash, но не с пеплом (BusyBox) ( -ash: command: not found).
Также обратите внимание, что builtinне доступно с золой и чертой.
Используйте встроенные команды Bash, если можете:
which programname
...
type -P programname
whichэто не встроенный Bash.
-Pэто не POSIX. Почему type -Pпредпочтительнее?
Команда -vработает нормально, если для параметра установлен параметр POSIX_BUILTINS<command> , но сбой. (Он работал у меня годами, но недавно я столкнулся с тем, где он не работал.)
Я считаю следующее более надежным:
test -x $(which <command>)
Поскольку он проверяет три вещи: путь, существование и разрешение на выполнение.
test -x $(which ls)возвращает 0, как это делает test -x $(which sudo), даже если lsустановлен и работоспособный и sudoдаже не установлен в Докер контейнера я бег в.
test -x "$(which <command>)"
lsэто псевдоним? Я не думаю, что это будет работать, если команда имеет параметр.
Для тех, кто интересуется, ни одна из методологий в предыдущих ответах не работает, если вы хотите обнаружить установленную библиотеку. Я полагаю, что вам остается либо физически проверить путь (возможно, для заголовочных файлов и тому подобное), либо что-то вроде этого (если вы используете дистрибутив на основе Debian):
dpkg --status libdb-dev | grep -q not-installed
if [ $? -eq 0 ]; then
apt-get install libdb-dev
fi
Как видно из вышесказанного, ответ «0» на запрос означает, что пакет не установлен. Это функция «grep» - «0» означает, что совпадение найдено, «1» означает, что совпадение не найдено.
cmd; if [ $? -eq 0 ]; thenдолжен быть рефакторинг вif cmd; then
dpkgилиapt
Здесь есть тонна вариантов, но я не удивился, что быстрых однострочников. Вот что я использовал в начале своих сценариев:
[[ "$(command -v mvn)" ]] || { echo "mvn is not installed" 1>&2 ; exit 1; }
[[ "$(command -v java)" ]] || { echo "java is not installed" 1>&2 ; exit 1; }
Это основано на выбранном ответе здесь и другом источнике.
Я бы сказал, что нет никакого портативного и 100% надежного способа из-за болтающихся aliasэс. Например:
alias john='ls --color'
alias paul='george -F'
alias george='ls -h'
alias ringo=/
Конечно, проблематично только последнее (без обид на Ринго!). Но все они действительны aliasс точки зрения command -v.
Чтобы отвергнуть висячие сообщения, такие как ringo, мы должны проанализировать выходные данные встроенной aliasкоманды оболочки и вернуться к ним ( command -vздесь не лучше alias). Для этого нет никакого переносимого решения, и даже Bash- конкретное решение довольно утомительно.
Обратите внимание, что подобное решение безоговорочно отклонит alias ls='ls -F':
test() { command -v $1 | grep -qv alias }
shopt -u expand_aliasesигнорирует / скрывает эти псевдонимы и shopt -s expand_aliasesпоказывает их через command -v.
Это скажет в зависимости от местоположения, если программа существует или нет:
if [ -x /usr/bin/yum ]; then
echo "This is Centos"
fi
Команда 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 executable
2 if an invalid option is specified
Приятно то, whichчто он выясняет, доступен ли исполняемый файл в среде, в whichкоторой он запущен, - это спасает несколько проблем ...
Моя установка для 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
}
Если вы, ребята / девочки, не можете получить ответы на вопросы, приведенные здесь в ответах, и вырываете волосы из спины, попробуйте выполнить ту же команду, используя bash -c . Просто посмотрите на этот сомнамбулярный бред. Вот что действительно происходит, когда вы запускаете $ (подкоманда):
Первый. Это может дать вам совершенно другой вывод.
$ 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
.bashrcесть [ -z "$PS1" ] && returnпредваряющий, # If not running interactively, don't do anythingпоэтому я думаю, что это причина, по которой даже явный источник bashrc в неинтерактивном режиме не помогает. Проблема может быть workarounded путем вызова сценария с ss64.com/bash/source.html оператора точка , . ./script.shно это не вещь , один хотел бы помнить , набирать каждый раз.
Хэш-вариант имеет одну ловушку: в командной строке вы можете, например, набрать
one_folder/process
чтобы процесс был выполнен. Для этого родительская папка one_folder должна быть в $ PATH . Но когда вы попытаетесь хешировать эту команду, она всегда будет успешной:
hash one_folder/process; echo $? # will always output '0'
$PATH» - это совершенно неточно. Попробуй это. Чтобы это работало, one_folder должен быть в текущем каталоге .
Я второй использование "команда -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
Я должен был проверить, был ли Git установлен как часть развертывания нашего CI- сервера. Мой последний скрипт Bash был следующим (сервер Ubuntu):
if ! builtin type -p git &>/dev/null; then
sudo apt-get -y install git-core
fi
sudo: без условия он всегда останавливается и запрашивает пароль (если вы недавно не сделали sudo). Кстати, это может быть полезно, sudo -p "Type your password to install missing git-core: "чтобы подсказка не появлялась на ровном месте.
Чтобы имитировать 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; }
typeКажется, что builtinв большинстве оболочек, это не может работать, потому что envиспользует execvpдля запуска, commandпоэтому commandне может быть builtin(и builtinвсегда будет выполняться в той же среде). Это не может для меня bash, ksh93, zsh, busybox [a]shи dashвсе , которые обеспечивают typeкак встроенная команда.
Если нет 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="$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 (Snow Leopard) использование Bash 4.2.24 (2) command -v lsне соответствует перемещению /bin/ls-temp.
В случае , если вы хотите проверить , если программа существует , и это действительно программа, не 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
Я хотел получить ответ на тот же вопрос, но запустить его в Makefile.
install:
@if [[ ! -x "$(shell command -v ghead)" ]]; then \
echo 'ghead does not exist. Please install it.'; \
exit -1; \
fi
скрипт
#!/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.
Я использую это, потому что это очень просто:
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
Он использует встроенные функции оболочки и эхо-статус программ для стандартного вывода и ничего для стандартной ошибки. С другой стороны, если команда не найдена, она отображает статус только для стандартной ошибки.
whichвозвращает истину за это.typeбез аргументов дополнительно вернет true для зарезервированных слов и встроенных команд оболочки. Если «программа» означает «исполняемый в$PATH», то посмотрите этот ответ .