Як би я підтвердив, що програма існує таким чином, що або поверне помилку та вихід, або продовжить сценарій?
Здається, це повинно бути легко, але це мене натикає.
Як би я підтвердив, що програма існує таким чином, що або поверне помилку та вихід, або продовжить сценарій?
Здається, це повинно бути легко, але це мене натикає.
Відповіді:
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 ]; thenif 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% надійного способу завдяки звисаючим aliases. Наприклад:
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", то дивіться цю відповідь .