Как бы я проверил, что программа существует, таким образом, что либо вернет ошибку и выйдет, либо продолжит работу со сценарием?
Кажется, это должно быть легко, но это меня озадачило.
Как бы я проверил, что программа существует, таким образом, что либо вернет ошибку и выйдет, либо продолжит работу со сценарием?
Кажется, это должно быть легко, но это меня озадачило.
Ответы:
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
», то посмотрите этот ответ .