Как проверить, существует ли программа из скрипта Bash?

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

Кажется, что это должно быть легко, но это меня озадачило.

1986
задан 16.10.2018, 17:03

18 ответов

Ответ

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, что сильно отличается от успешной записи в нее и отбрасывания вывода (и это опасно!))

Если ваш hash bang is /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
}
2749
ответ дан 04.10.2019, 10:21
  • 1
    @Geert: & > часть/dev/null скрывает сообщение ' type' испускает когда ' foo' doesn' t существуют. > & 2 на эхе удостоверяется, что отправил сообщение об ошибке в стандартную погрешность вместо стандартного вывода; потому что that' s соглашение. Они оба появляются на Вашем терминале, но стандартная погрешность является определенно предпочтительным выводом для сообщений об ошибках и неожиданных предупреждений. – lhunath 20.07.2010, 03:43
  • 2
    флаг-P не работает в ' sh' например, stackoverflow.com/questions/2608688/… – momeara 02.04.2011, 09:18
  • 3
    Для незнакомых с ' advanced' перенаправление i/o в ударе: 1) 2>&- (" близкий дескриптор выходного файла 2" который является stderr) , имеет тот же результат как 2> /dev/null; 2) >&2 ярлык для 1>&2, который можно распознать как " перенаправьте stdout к stderr". посмотрите Усовершенствованное Руководство по созданию сценариев Bash i/o страница перенаправления для большего количества информации. – mikewaters 22.12.2011, 08:48
  • 4
    @mikewaters ABS выглядит довольно усовершенствованным и описывает широкий спектр удара и неудара функциональность CLI, но это очень небрежно во многих аспектах и не следует хорошей практике. Я don' t имеют почти достаточно пространства в этом комментарии, чтобы сделать рецензию; но я могу вставить несколько случайных примеров ПЛОХОГО кода: while read element ; do .. done <<< $(echo ${ArrayVar[*]}), for word in $(fgrep -l $ORIGINAL *.txt), ls -l "$directory" | sed 1d , {{для в seq $BEGIN $END}}... Многие попытались связаться с авторами и предложить улучшения, но it' s никакая Wiki и запросы приземлились на глухие уши. – lhunath 13.06.2013, 10:00
  • 5
    Большое спасибо Парни... Делает Точно, в чем я нуждался.. Требуемый, чтобы сделать что-то с " Полноценность этого answer" точки для Вас.. Но по-видимому мне нужны 15 точек репутации: ( – Sam 02.01.2020, 16:00

Потрясающий ответ и объяснение от @lhunath. Спас мой день. Я немного расширил это. Не мог контролировать себя, разделяя это - надеясь, что это может быть полезно для кого-то. Если кто-то должен проверить (массив) нескольких программ, вот быстрый фрагмент.

Что он делает? (1) Читать массив программ. (2) Показать сообщение для неудачной программы. (3) Предложить пользователю продолжить (принудительный цикл) опции y / n для проверки остальных программ.

#!/bin/bash

proginstalldir=/full/dir/path/of/installation
progsbindir=$proginstalldir/bin
echo -e "\nMy install directory - $proginstalldir"
echo -e "My binaries directory - $progsbindir"

VerifyInstall () {
clear
myprogs=( program1 program2 program3 program4 program5 programn ); 
echo -e "\nValidation of my programs started...."
for ((i=0; i<${#myprogs[@]}; i++)) ; do 
command -v $progsbindir/${myprogs[i]} >/dev/null && echo -e "Validating....\t${myprogs[i]}\tSUCCESSFUL"  || { echo -e "Validating.... \t${myprogs[i]}\tFAILED" >&2;
while true; do 
printf "%s:  "  "ERROR.... Validation FAILED for ${myprogs[i]} !!!! Continue?"; read yn; 
case $yn in [Yy] )  echo -e "Please wait..." ; break;;
[Nn]) echo -e "\n\n#################################\n##   Validation Failed .. !!   ##\n#################################\n\n" ; exit 1; break;;
*) echo -e "\nPlease answer y or n then press Enter\n"; esac; done; >&2; }; done
sleep 2
}

VerifyInstall
-3
ответ дан 29.08.2019, 18:44
  • 1
    Хорошее решение. Я предполагаю, что мой ответ остается полезным для любого, у кого есть эта проблема, но значение для удаления является ненулевым. – Jon Winstanley 03.10.2019, 02:58

Если вы, ребята, не можете заставить вещи выше / ниже работать и выдергивать волосы из спины, попробуйте выполнить ту же команду, используя 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
1
ответ дан 04.10.2019, 10:21

Я использую это, потому что это очень просто:

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

Он использует встроенную оболочку и программный эхо-статус для stdout и ничего для stderr другой стороны, если команда не найдена, она отображает статус только для stderr.

0
ответ дан 04.10.2019, 10:21
  • 1
    Большое спасибо.. Позвольте мне Начать экспериментировать в этом направлении – Sam 02.01.2020, 15:58

Сценарий

#!/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.
0
ответ дан 04.10.2019, 10:21

Здесь есть множество вариантов, но я не удивился, когда не было быстрых однострочников, это то, что я использовал в начале своих сценариев: [[ "$(command -v mvn)" ]] || { echo "mvn is not installed" 1>&2 ; exit 1; } [[ "$(command -v java)" ]] || { echo "java is not installed" 1>&2 ; exit 1; }

это основано на выбранном здесь ответе, а другой источник (а я немного поиграюсь).

надеюсь, что это будет полезно для других.

1
ответ дан 04.10.2019, 10:21

Я должен был проверить, был ли установлен git как часть развертывания нашего CI-сервера. Мой последний скрипт bash был следующим (сервер Ubuntu):

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

Надеюсь, что это поможет кому-то еще!

0
ответ дан 04.10.2019, 10:21
  • 1
    Условное выражение довольно бесполезно, по модулю время запуска для выполнения склонный - добирается, как склонный - добираются, будет удовлетворен и выход, если ядро мерзавца будет уже установлено. – tripleee 03.09.2011, 22:52
  • 2
    Его время запуска ненезначительно, но более важная мотивация sudo: без условного выражения это всегда останавливалось бы и просило бы пароль (если Вы недавно не сделали sudo). BTW, может быть полезно сделать sudo -p "Type your password to install missing git-core: " так подсказка doesn' t прибывают внезапно. – Beni Cherniavsky-Paskin 16.11.2012, 01:33

Если нет внешней команды 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 с использованием Bash 4.2.24 (2) command -v ls не соответствует перемещению /bin/ls-temp.

2
ответ дан 04.10.2019, 10:21

Хэш-вариант имеет один подводный камень: например, в командной строке вы можете ввести

one_folder/process

, чтобы процесс был выполнен. Для этого родительская папка one_folder должна быть в $ PATH . Но когда вы попытаетесь хешировать эту команду, она всегда будет успешной:

hash one_folder/process; echo $? # will always output '0'
1
ответ дан 04.10.2019, 10:21
  • 1
    Спасибо. I' m пробующий к " серый out" фон, когда модальное появляется. Другими словами, фактическое модальное содержание только поднимает приблизительно 300x200 ПБ на экране. Я хочу область вне содержания " grayed " Когда пользователь коснется вслед за тем, это закроет модальное. – kmiklas 21.06.2014, 11:13

Я согласен с 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, ее спецификация приведена здесь: http://pubs.opengroup.org /onlinepubs/9699919799/utilities/command.html

Примечание: type соответствует POSIX, но type -P - нет.

199
ответ дан 04.10.2019, 10:21
  • 1
    То же как выше - exit 1; уничтожает xterm, если вызвано оттуда. – user unknown 19.02.2012, 06:14
  • 2
    Этот wouldn' t работают над стандартом sh: Вы & > isn' t допустимое перенаправление инструкции. – jyavenard 05.03.2012, 00:19
  • 3
    @jyavenard: вопрос отмечен удар , следовательно более краткая определенная для удара нотация &>/dev/null перенаправления. Однако я соглашаюсь с Вами, что действительно имеет значение, мобильность, I' ve отредактировал мой ответ соответственно, теперь с помощью стандарта sh перенаправление >/dev/null 2>&1. – GregV 05.03.2012, 23:58

Это зависит от того, хотите ли вы узнать, существует ли он в одном из каталогов в переменной $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 программа.

74
ответ дан 04.10.2019, 10:21
  • 1
    Вы действительно shouldn' t использовать " which" по причинам, обрисованным в общих чертах в моем комментарии. – lhunath 25.03.2009, 02:53

Попробуйте использовать:

test -x filename

или

[ -x filename ]

Из справочной страницы bash в разделе Условные выражения :

 -x file
          True if file exists and is executable.
20
ответ дан 04.10.2019, 10:21
  • 1
    Это означает, что необходимо уже знать полный путь к приложению. – lhunath 25.03.2009, 00:45
  • 2
    OP didn' t определяют, хотел ли он проверить на определенный экземпляр или на какой-либо исполняемый экземпляр... Я ответил на него способ, которым я считал его. – dmckee 25.03.2009, 04:42

Я никогда не заставлял вышеуказанные решения работать на коробке, к которой у меня есть доступ. Для одного типа был установлен (делает то, что делает больше). Так что встроенная директива необходима. Эта команда работает для меня:

if [ `builtin type -p vim` ]; then echo "TRUE"; else echo "FALSE"; fi
11
ответ дан 04.10.2019, 10:21

Команда 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  exe-
          cutable

       2      if an invalid option is specified

Приятно то, что он вычисляет если исполняемый файл доступен в той среде, в которой он запущен, - это избавляет от нескольких проблем ...

-Adam

3
ответ дан 04.10.2019, 10:21
  • 1
    Используйте который, если Вы ищущий какой-либо исполняемый файл, названный нечто, но, видите мой ответ, если Вы хотите проверить конкретный файл/path/to/a/named/foo. Также обратите внимание что, который не может быть доступным в некоторых минимальных системах, хотя это должно присутствовать на любой абсолютной установке... – dmckee 27.02.2009, 10:01
  • 2
    Don' t полагаются в статусе выхода который. Много операционных систем имеют который это doesn' t даже устанавливает статус выхода кроме 0. – lhunath 25.03.2009, 00:46

Проверка на наличие нескольких зависимостей и информирование конечных пользователей о состоянии

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?

8
ответ дан 04.10.2019, 10:21

hash foo 2>/dev/null : работает с Zsh, Bash, Dash и Ash.

type -p foo: кажется, что он работает с zsh, bash и ash (busybox), но не с dash (интерпретирует -p как аргумент).

command -v foo: работает с zsh, bash, dash, но не с ash (busybox) (-ash: command: not found).

Также обратите внимание, что builtin недоступно с ash и dash.

6
ответ дан 04.10.2019, 10:21
GIT=/usr/bin/git                     # STORE THE RELATIVE PATH
# GIT=$(which git)                   # USE THIS COMMAND TO SEARCH FOR THE RELATIVE PATH

if [[ ! -e $GIT ]]; then             # CHECK IF THE FILE EXISTS
    echo "PROGRAM DOES NOT EXIST."
    exit 1                           # EXIT THE PROGRAM IF IT DOES NOT
fi

# DO SOMETHING ...

exit 0                               # EXIT THE PROGRAM IF IT DOES
-1
ответ дан 04.10.2019, 10:21

Я бы просто попытался вызвать программу, например, с помощью --version или --help и , чтобы проверить, была ли команда выполнена успешно или нет

Используется с set -e скрипт завершится, если программа не найдена, и вы получите осмысленное сообщение об ошибке:

#!/bin/bash
set -e
git --version >> /dev/null
-2
ответ дан 04.10.2019, 10:21

Теги

Похожие вопросы