Як би я підтвердив, що програма існує таким чином, що або поверне помилку та вихід, або продовжить сценарій?
Здається, це повинно бути легко, але це мене натикає.
Як би я підтвердив, що програма існує таким чином, що або поверне помилку та вихід, або продовжить сценарій?
Здається, це повинно бути легко, але це мене натикає.
Відповіді:
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>&-
("закрити дескриптор вихідного файлу 2", який є stderr) має той же результат, що і 2> /dev/null
; 2) >&2
- це ярлик для 1>&2
, який ви можете визнати "перенаправлення stdout на stderr". Докладнішу інформацію див. На сторінці перенаправлення вводу / виводу вдосконаленого діапазону .
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
дуже непослідовна. У тирі він повертає перший файл, що відповідає $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 >/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-оболонкою (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
es. Наприклад:
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 у неінтерактивному режимі не допомагає. Проблему можна усунути, зателефонувавши до сценарію з оператором крапок ss64.com/bash/source.html,. ./script.sh
але це не те, про що хотілося б пам’ятати кожен раз.
Хеш-варіант має один підводний камінь: у командному рядку ви можете, наприклад, ввести
one_folder/process
щоб процес був виконаний. Для цього батьківська папка one_folder повинна бути в $ PATH . Але коли ви спробуєте зафіксувати цю команду, вона завжди матиме успіх:
hash one_folder/process; echo $? # will always output '0'
$PATH
" - це абсолютно неточно. Спробуй це. Щоб це працювало, одна_папка повинна бути в поточному каталозі .
Я друге використання "команда -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 -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 v10.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
Сценарій
#!/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
Він використовує вбудовані оболонки та ехо-стан програм у стандартний вихід та нічого не має стандартної помилки. З іншого боку, якщо команда не знайдена, вона повторює статус лише до стандартної помилки.
Якщо припустити, що ви вже дотримуєтесь безпечних методів оболонки :
set -eu -o pipefail
shopt -s failglob
./dummy --version 2>&1 >/dev/null
Це передбачає, що команду можна викликати таким чином, що вона (майже) нічого не робить, як, наприклад, повідомляти про її версію або показувати довідку.
Якщо dummy
команда не знайдена, Bash виходить із наступною помилкою ...
./my-script: line 8: dummy: command not found
Це корисніше і менш докладно, ніж інші command -v
(і подібні) відповіді, оскільки повідомлення про помилку створюється автоматично, а також містить відповідний номер рядка.
dummy
існування перебуває поза руками автор сценарію. Крім того, йому не вистачає пояснень, як це працює і що він змінює налаштування середовища виконання.
shopt
виклик set -f
, який не є POSIX , можна замінити на POSIX , який коротший і портативний. Опція " pipefail" не підтримується в POSIX , хоча і альтернативи немає AFAIK.
which
для них повертає істину.type
без аргументів додатково повернеться істина для зарезервованих слів та вбудованих оболонок. Якщо "програма" означає "виправдання в$PATH
", то дивіться цю відповідь .