Як я можу перевірити, чи існує програма зі сценарію Bash?


2209

Як би я підтвердив, що програма існує таким чином, що або поверне помилку та вихід, або продовжить сценарій?

Здається, це повинно бути легко, але це мене натикає.


Що таке "програма"? Чи включає він функції та псевдоніми? whichдля них повертає істину. typeбез аргументів додатково повернеться істина для зарезервованих слів та вбудованих оболонок. Якщо "програма" означає "виправдання в $PATH", то дивіться цю відповідь .
Том Хейл

Відповіді:


3049

Відповідь

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
}

35
@Geert: Частина &> / dev / null приховує, що повідомлення "type" видається, коли "foo" не існує. >> 2 на ехо-сигналі обов'язково надсилає повідомлення про помилку на стандартну помилку замість стандартного виводу; тому що це умовність. Вони обидва з'являються у вашому терміналі, але стандартна помилка, безумовно, є кращим виходом для повідомлень про помилки та несподіваних попереджень.
lhunath

5
прапор -P не працює в «ш», наприклад , stackoverflow.com/questions/2608688 / ...
momeara

130
Для тих, хто не знайомий з "розширеним" перенаправленням вводу-виводу в bash: 1) 2>&- ("закрити дескриптор вихідного файлу 2", який є stderr) має той же результат, що і 2> /dev/null; 2) >&2- це ярлик для 1>&2, який ви можете визнати "перенаправлення stdout на stderr". Докладнішу інформацію див. На сторінці перенаправлення вводу / виводу вдосконаленого діапазону .
mikewaters

9
@mikewaters ABS виглядає досить вдосконалено і описує широкий спектр функціональних та несамостійних CLI-функцій, але в багатьох аспектах дуже недбалий і не дотримується належної практики. У мене в цьому коментарі майже не вистачає місця для написання запису; але я можу вставити кілька випадкових прикладів BAD коду: while read element ; do .. done <<< $(echo ${ArrayVar[*]}), for word in $(fgrep -l $ORIGINAL *.txt), ls -l "$directory" | sed 1d , {{для а seq $BEGIN $END}}, ... Багато хто намагався зв'язатися з авторами і запропонувати поліпшення , але це не вики та запити висадилися на глухі вуха.
lhunath

56
@mikewaters 2>&-- це не те саме, що 2>/dev/null. Перший закриває дескриптор файлу, а другий просто перенаправляє його на /dev/null. Можливо, ви не побачите помилку, оскільки програма намагається повідомити вас на stderr про те, що stderr закритий.
nyuszika7h

575

Далі наведено портативний спосіб перевірити, чи існує команда в $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. [ Звіт про помилку ] [ Стандарт ]

Крім того, це не вдасться, якщо шукану команду було визначено як псевдонім.


4
Чи command -vстворить шлях навіть для невиконаного файлу? Тобто, -x насправді необхідний?
einpoklum

5
@einpoklum -xперевіряє, що файл виконується, саме до цього і було питання.
Кен Шарп

3
@KenSharp: Але це здається зайвим, оскільки commandбуде сам тест на його виконання - чи не так?
einpoklum

13
@einpoklum Так, це необхідно. Насправді навіть таке рішення може порушитися в одному крайовому випадку. Дякую за те, що ви звернули на це увагу. тире, bash і zsh всі пропускають файли, що не виконуються, під $PATHчас виконання команди. Однак поведінка Росії command -vдуже непослідовна. У тирі він повертає перший файл, що відповідає $PATH, незалежно від того, виконаний він чи ні. У bash, він повертає першу виконувану відповідність $PATH, але якщо такої немає, вона може повернути невиконаний файл. І в zsh він ніколи не поверне не виконуваний файл.
nyuszika7h

4
Наскільки я можу сказати, dashце єдиний із цих трьох, які не сумісні з POSIX; [ -x "$(command -v COMMANDNAME)"]буде працювати в двох інших. Схоже, про цю помилку вже повідомлялося, але відповіді досі не отримано: bugs.debian.org/cgi-bin/bugreport.cgi?bug=874264
nyuszika7h

209

Я погоджуюся з 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це не так.


3
Те саме, що вище - exit 1;вбиває xterm, якщо його викликають звідти.
користувач невідомий

1
Це не працює в стандартному режимі: ви &> не є вказівкою щодо переадресації.
jyavenard

7
@jyavenard: Питання позначене баш , отже, більш лаконічні позначення перенаправлення, специфічні для bash &>/dev/null. Однак я згоден з вами, що насправді має значення портативність, я відповідно відредагував свою відповідь, використовуючи стандартне перенаправлення sh >/dev/null 2>&1.
GregV

щоб ще більше вдосконалити цю відповідь, я би зробив дві речі: 1: використати "&>" для спрощення її, як відповідь Джоша. 2: розбити {} на додатковий рядок, поставивши вкладку перед відлунням, для читабельності
knocte

Я просто поставити цей один вкладиш в функцію Баша , якщо хто -то хоче його ... github.com/equant/my_bash_tools/blob/master/tarp.bash
EQUANT

94

У мене .bashrc визначена функція, яка полегшує це.

command_exists () {
    type "$1" &> /dev/null ;
}

Ось приклад того, як він використовується (від мого .bash_profile.)

if command_exists mvim ; then
    export VISUAL="mvim --nofork"
fi

Що робить &>?
Саад Малик


&>може бути недоступним у вашій версії Bash. Код Марчелло повинен добре працювати; це робить те саме.
Джош Стратер

3
Помилка вбудованих і зарезервованих слів: Спробуйте це, наприклад, зі словом then. Дивіться цю відповідь, якщо вам потрібно виконати виконуваний файл $PATH.
Том Хейл

84

Це залежить від того, чи хочете ви знати, чи існує він в одному з каталогів $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програми.


22
Ви дійсно не повинні використовувати "які" з причин, викладених у моєму коментарі.
lhunath

39

Розгортаючись на відповіді @ 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

13
Готовність вчитися та вдосконалюватися повинна винагороджуватися. +1 Це чисто і просто. Єдине, що я можу додати, - це commandуспіх навіть для псевдонімів, які можуть бути дещо протизаконними. Перевірка наявності в інтерактивній оболонці дасть різні результати, ніж при переміщенні її в сценарій.
Палець

1
Я просто перевіряв і використання shopt -u expand_aliasesІгнорує / приховування псевдонімами (як alias ls='ls -F'згадувалося в іншому відповіді) і shopt -s expand_aliasesвирішує їх з допомогою command -v. Тому, можливо, його слід встановити до перевірки та скидання після, хоча це може вплинути на значення повернення функції, якщо ви не чітко захоплюєте та не повертаєте висновок командного виклику.
dragon788

24

Спробуйте використовувати:

test -x filename

або

[ -x filename ]

З сторінки сторінки Bash у розділі Умовні вирази :

 -x file
          True if file exists and is executable.

26
Це означає, що вам потрібно вже знати повний шлях до програми.
lhunath

12
ОП не вказав, чи хотів би він перевірити конкретний екземпляр чи який-небудь виконуваний екземпляр ... Я відповів на нього так, як я його прочитав.
dmckee --- кошеня колишнього модератора

16

Використовувати 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записує повідомлення в стандартний потік помилок.


8
Чому б не просто if hash foo &> /dev/null; then ...?
Бені Чернявський-Паскін

9

Я ніколи не отримував попередніх відповідей, щоб працювати над коробкою, до якої я маю доступ. Для одного typeвстановлено (робить те, що moreробить). Тому потрібна вбудована директива. Ця команда працює для мене:

if [ `builtin type -p vim` ]; then echo "TRUE"; else echo "FALSE"; fi

3
Дужки не є частиною ifсинтаксису, просто використовуйте if builtin type -p vim; then .... А зворотній зв'язок справді давній і застарілий синтаксис, $()який підтримується навіть shу всіх сучасних системах.
nyuszika7h

9

Перевірте наявність кількох залежностей та повідомте про стан кінцевих користувачів

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?


1
Немовний спосіб зробити це: 1) позбутися від специфікатора ширини; 2) додайте пробіл після printf імені вашої команди; 3) передайте для циклу column -t(частина util-linux).
Патріс Левеск

8

Якщо ви перевірите наявність програми, ви, ймовірно, все одно запустите її пізніше. Чому б не спробувати запустити його в першу чергу?

if foo --version >/dev/null 2>&1; then
    echo Found
else
    echo Not found
fi

Це більш достовірна перевірка, що програма працює, ніж просто перегляд каталогів PATH та дозволів на отримання файлів.

Крім того, ви можете отримати корисний результат від вашої програми, наприклад, її версію.

Звичайно, недоліки полягають у тому, що деякі програми можуть бути важкими для запуску, а деякі не мають --versionможливості негайно (і успішно) вийти.


6

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це не доступно для золи та тире.


4

Використовуйте вбудовані Bash, якщо можете:

which programname

...

type -P programname

15
Так? whichне є вбудованим Bash.
tripleee

type -P програму слід віддати перевагу, див. прийняту відповідь
RobertG

@RobertG Я бачу лише те, що -Pце не POSIX. Чому type -Pвіддається перевага?
mikemaccana

Я мав би висловити фразу, що "віддати перевагу в середовищі баш", - як я мав намір відповісти на попередній коментар, специфічний для bash. Так чи інакше, це було років тому - я, мабуть, я повинен ще раз вказувати на відповідь, позначений як "прихильний"
RobertG

4

Команда -vпрацює нормально, якщо для тестування встановлено параметр POSIX_BUILTINS <command>, але він може вийти з ладу, якщо ні. (Він працював на мене роками, але нещодавно я наткнувся на той, де він не працював.)

Я вважаю таке, що є більш невдалим:

test -x $(which <command>)

Оскільки він перевіряє три речі: шлях, існування та дозвіл на виконання.


Не працює. test -x $(which ls)повертає 0, як це робить test -x $(which sudo), навіть якщо lsвстановлений і працездатний і sudoнавіть не встановлений в Докер контейнера я біг в.
водоростевому

@algal Вам потрібно використовувати цитати, я думаю, такtest -x "$(which <command>)"
JoniVR

@algal Можливо, lsце псевдонім? Я не думаю, що це буде працювати, якщо команда має параметр.
AnthonyC

3

Для тих, хто цікавиться, жодна з методологій попередніх відповідей не працює, якщо ви хочете виявити встановлену бібліотеку. Я думаю, що вам залишається або фізично перевірити шлях (можливо, для файлів заголовків тощо), або щось подібне (якщо ви користуєтеся дистрибутивом на основі Debian):

dpkg --status libdb-dev | grep -q not-installed

if [ $? -eq 0 ]; then
    apt-get install libdb-dev
fi

Як видно з вищесказаного, відповідь "0" із запиту означає, що пакет не встановлений. Це функція "grep" - "0" означає, що збіг знайдено, "1" означає, що збіг не знайдено.


10
Однак cmd; if [ $? -eq 0 ]; thenif cmd; then
антидіаграму

Це працює лише для ліб, встановлених через dpkgабоapt
Вейджун Чжоу

3

Тут є безліч варіантів, але мене не здивували швидкі однолінійки. Це те, що я використовував на початку своїх сценаріїв:

[[ "$(command -v mvn)" ]] || { echo "mvn is not installed" 1>&2 ; exit 1; }
[[ "$(command -v java)" ]] || { echo "java is not installed" 1>&2 ; exit 1; }

Це ґрунтується на обраній тут відповіді та іншому джерелі.


2

Я б сказав, що не існує жодного портативного та 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 }

Гарна думка. Однак при запуску зсередини баш-скрипту псевдоніми не видно.
Василь Муса

1
Існує також проблема, вона повернеться помилковою, коли буде перевірена команда "псевдонім". Коли воно повинно повернути правду. Приклад: тест "псевдонім"
Василь Муса

2
Я просто тестував і використовував shopt -u expand_aliasesігнорування / приховування цих псевдонімів і shopt -s expand_aliasesпоказує їх через command -v.
dragon788

2

Це покаже відповідно до місця, якщо програма існує чи ні:

    if [ -x /usr/bin/yum ]; then
        echo "This is Centos"
    fi

Так, я додав цю команду, якщо вам потрібно встановити пакет у sevrer, Open suse, centos, Debian
Клевін Кона

Підкреслення синтаксису вимкнено в рядку "відлуння". Яке рішення? Чи припускає, що сценарій Bash має бути іншим?
Пітер Мортенсен

Виділення синтаксису @PeterMortensen вимкнено, оскільки він не розпізнає, що це рядок.
Адрієн

1

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він працює - це економить кілька проблем ...


Скористайтеся яким, якщо ви шукаєте який-небудь виконуваний файл з ім'ям foo, але подивіться мою відповідь, якщо ви хочете перевірити певний файл / шлях / до / а / названий / foo. Також зауважте, що це може бути недоступно в деяких мінімальних системах, хоча воно повинно бути присутнім у будь-якій повноцінній установці ...
dmckee --- кошеня колишнього модератора

9
Не покладайтеся на статус виходу з якого. У багатьох операційних системах є такий, який навіть не встановлює статус виходу, крім 0.
lhunath

1

Моя настройка для сервера 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
}

1

Якщо ви, хлопці / дівчата, не можете отримати відповіді на відповіді, і вони витягують волосся зі спини, спробуйте виконати ту саму команду, використовуючи 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 читається лише тоді, коли оболонка не входить у систему та інтерактивна. Однак другий виглядає дивно, тому що це повинно бути викликано різницею змінної середовища PATH, але підшари успадковують середовище.
Palec

У моєму випадку .bashrcмаю [ -z "$PS1" ] && returnнаперед # If not running interactively, don't do anythingприпущення, тому я думаю, що це причина, чому навіть явний пошук bashrc у неінтерактивному режимі не допомагає. Проблему можна усунути, зателефонувавши до сценарію з оператором крапок ss64.com/bash/source.html,. ./script.sh але це не те, про що хотілося б пам’ятати кожен раз.
user619271

1
Сценарії пошуку джерел, які не мають бути поставлені, є поганою ідеєю. Все, що я намагався сказати, - це те, що ваша відповідь має мало спільного з питанням, яке задають, і багато спільного з Bash та його (не) інтерактивним режимом.
Palec

Якщо б це пояснило, що відбувається в цих випадках, це було б корисним доповненням до відповіді.
Palec

0

Хеш-варіант має один підводний камінь: у командному рядку ви можете, наприклад, ввести

one_folder/process

щоб процес був виконаний. Для цього батьківська папка one_folder повинна бути в $ PATH . Але коли ви спробуєте зафіксувати цю команду, вона завжди матиме успіх:

hash one_folder/process; echo $? # will always output '0'

4
"Для цього батьківська папка one_folder повинна бути в $PATH" - це абсолютно неточно. Спробуй це. Щоб це працювало, одна_папка повинна бути в поточному каталозі .
Wildcard

0

Я друге використання "команда -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

0

Мені довелося перевірити, чи встановлено Git як частину розгортання нашого сервера CI . Мій остаточний сценарій Bash був такий (сервер Ubuntu):

if ! builtin type -p git &>/dev/null; then
  sudo apt-get -y install git-core
fi

3
Умовно досить марно, модулюйте час запуску для запуску apt-get, оскільки apt-get буде задоволений і вийде, якщо git-core вже встановлений.
tripleee

3
Час його запуску є незначним, але важливішою є мотивація sudo: без умовного режиму він завжди зупинявся б і запитував пароль (якщо ви нещодавно не робили судо). До речі, це може бути корисно, sudo -p "Type your password to install missing git-core: "щоб підказка не виходила з синього боку.
Бені Чернявський-Паскін

0

Щоб наслідувати 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; }

7
Чому це рекомендується? На яких системах це працює насправді? typeздається, builtinв більшості оболонок , так що це не може працювати , тому що envвикористовує execvpдля запуску , commandтак commandне може бути builtinbuiltinзавжди буде працювати в межах однієї і тієї ж середовищі). Це не може для мене bash, ksh93, zsh, busybox [a]shі dashвсе , що забезпечують typeяк вбудована команда.
Адріан Фрюхвітрт

0

Якщо немає якихось - або зовнішніх 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.


0

У разі , якщо ви хочете перевірити , якщо програма існує , і це дійсно програма, яка не 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

-1

Сценарій

#!/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.

-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

Він використовує вбудовані оболонки та ехо-стан програм у стандартний вихід та нічого не має стандартної помилки. З іншого боку, якщо команда не знайдена, вона повторює статус лише до стандартної помилки.


-1

Якщо припустити, що ви вже дотримуєтесь безпечних методів оболонки :

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існування перебуває поза руками автор сценарію. Крім того, йому не вистачає пояснень, як це працює і що він змінює налаштування середовища виконання.
Палець

Це питання Bash, але, можливо, варто згадати, що shoptвиклик set -f, який не є POSIX , можна замінити на POSIX , який коротший і портативний. Опція " pipefail" не підтримується в POSIX , хоча і альтернативи немає AFAIK.
Палець

@Palec Я не впевнений, як це важко. Насправді простіше, оскільки він покладається на оболонку, щоб повідомити користувачеві про те, що команду неможливо знайти та виходить із помилкою, як цього вимагає ОП. Дайте мені знати, що я пропустив :)
Адрієн
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.