Сценарий оболочки Linux: запускать программу, только если она существует, игнорировать ее, если она не существует

15

Я программирование Linux сценария оболочки , который будет печатать баннеры состояния во время его выполнения , только если правильный инструмент, скажем figlet, будет установлен (это: достижимы системами пути ).

Пример:

#!/usr/bin/env bash
echo "foo"
figlet "Starting"
echo "moo"
figlet "Working"
echo "foo moo"
figlet "Finished"

Я бы хотел, чтобы мой скрипт работал без ошибок, даже если figletон не установлен .

Какой может быть практический метод ?

Сопалахо де Арриерес
источник
@sudodus: просто игнорирование команды 'figlet' (и ее параметров) будет в порядке. Продолжая казнь, конечно.
Сопалахо де Арриерес
2
Название этого вопроса
дало
2
Хотите игнорировать все ошибки? Просто используйте figlet ... || true.
Джакомо Альзетта
Если вас не интересуют коды выхода, следует использовать ярлык figlet || true, но в вашем случае, вероятно, используется функция оболочки, которая выдает открытый текст, если не может быть напечатан баннер, с большей вероятностью то, что вам нужно.
Екс

Ответы:

30

Моя интерпретация будет использовать функцию-оболочку, названную так же, как инструмент; в этой функции запустите реальный инструмент, если он существует:

figlet() {
  command -v figlet >/dev/null && command figlet "$@"
}

Тогда вы можете figlet arg1 arg2...без изменений в вашем сценарии.

@Olorin предложил более простой метод: определять функцию-обертку, только если нам нужно (если инструмент не существует):

if ! command -v figlet > /dev/null; then figlet() { :; }; fi

Если вы хотите, чтобы аргументы figletбыли напечатаны, даже если figlet не установлен, измените предложение Олорина следующим образом:

if ! command -v figlet > /dev/null; then figlet() { printf '%s\n' "$*"; }; fi
Джефф Шаллер
источник
1
Откуда commandберутся? У меня его нет в моих установках (Red Hat 6.8 Enterprise и Cygwin64).
eewanco
1
@eewanco, см. unix.stackexchange.com/a/85250/117549 ; Короче говоря, он встроен в bash, что, вероятно, ваша оболочка.
Джефф Шаллер
4
commandвстроенная оболочка POSIX Bourne Обычно она выполняет предоставленную команду, но -vфлаг делает ее более похожей на typeдругую встроенную оболочку.
вирм
@eewanco type -a commandпокажет вам. whichбудет отображать только исполняемые файлы на ваших $PATH, а не встроенные, ключевые слова, функции или псевдонимы.
10
@ l0b0: в семействе RedHat с bash и профилями по умолчанию which действительно находит подходящий псевдоним, потому что он создает псевдонимы whichдля запуска /usr/bin/whichи передачи ему списка псевдонимов оболочки для просмотра (!) (конечно, \whichподавляет псевдоним и использует только программу, которая не показывает псевдонимы.)
dave_thompson_085
14

Вы можете проверить, если figletсуществует

if type figlet >/dev/null 2>&1
then
    echo Figlet is installed
fi
roaima
источник
6

Обычный способ сделать это с test -xака [ -x. Вот пример, взятый из /etc/init.d/ntpсистемы Linux:

if [ -x /usr/bin/lockfile-create ]; then
    lockfile-create $LOCKFILE
    lockfile-touch $LOCKFILE &
    LOCKTOUCHPID="$!"
fi

Этот вариант основан на знании полного пути исполняемого файла. В /bin/lesspipeя нашел пример, который работает вокруг этого путем объединения -xи whichкоманды:

if [ -x "`which bunzip`" ]; then bunzip -c "$1"
else echo "No bunzip available"; fi ;;

Таким образом, это будет работать, не зная заранее, где PATHнаходится bunzipисполняемый файл.

kasperd
источник
4
Не используйтеwhich . И даже если whichсработало, использование test -xна его выходе глупо: если вы получаете путь which, он существует.
Жиль "ТАК - прекрати быть злым"
1
@ Жиль: Технически говоря, test -xэто больше, чем просто проверка, существует ли файл (вот для чего test -e). Он также проверяет, есть ли у файла установленные разрешения на выполнение.
Comfreak
@comfreak Так и делает which.
Жиль "ТАК - перестань быть злым"
5

В начале вашего скрипта проверьте, figletсуществует ли , а если нет, определите функцию оболочки, которая ничего не делает:

type figlet >/dev/null 2>&1 || figlet() { :; }

typeпроверяет, figletсуществует ли встроенная оболочка, функция, псевдоним или ключевое слово, >/dev/null 2>&1отбрасывает stdin и stdout, чтобы вы не получили никакого вывода, и, если он не существует, figlet() { :; }определяет figletкак функцию, которая ничего не делает.

Таким образом, вам не нужно редактировать каждую строку вашего скрипта, которая используется figlet, или проверять, существует ли она каждый раз, когда figletвызывается.

Вы можете добавить диагностическое сообщение, если вам нравится:

type figlet >/dev/null 2>&1 || { echo 'figlet not installed.' ; figlet() { :; } ; }

В качестве бонуса, поскольку вы не упомянули, какую оболочку вы используете, я считаю, что она совместима с POSIX, поэтому она должна работать практически на любой оболочке.

Крис
источник
Мне нравится простота этого ответа. Вы также можете заменить type figlet >/dev/null 2>&1на, hash figlet 2>/dev/nullесли вы используете Bash. (ОП сказала «если это в моем ПУТИ».)
Джо,
5

Другая альтернатива - шаблон, который я видел в сценариях автоматической настройки проекта:

if [ -x /usr/bin/figlet ]
then
    FIGLET=/usr/bin/figlet
else
    FIGLET=:
fi

$FIGLET "Hello, world!"

В вашем конкретном случае вы могли бы даже сделать,

if [ -x /usr/bin/figlet ]
then
   SAY=/usr/bin/figlet
elif [ -x /usr/local/bin/figlet ]
then
   SAY=/usr/local/bin/figlet
elif [ -x /usr/bin/banner ]
then
   SAY=/usr/bin/banner
else
   SAY=/usr/bin/echo
fi

$SAY "Hello, world!"

Если вы не знаете конкретный путь, вы можете попробовать несколько elif(см. Выше), чтобы попробовать известные места, или просто использоватьPATH чтобы всегда разрешать команду:

if command -v figlet >/dev/null
then
    SAY=figlet
elif command -v banner >/dev/null
then
    SAY=banner
else
    SAY=echo
fi

В общем, при написании скриптов я предпочитаю вызывать команды только в определенных мной местах. Мне не нравится неопределенность / риск того, что конечный пользователь мог бы положить в ихPATH , возможно, в свои собственные ~/bin.

Если, например, я пишу сложный сценарий для других, который может удалять файлы на основе вывода конкретной команды, которую я вызываю, я бы не хотел случайно брать в них ~/binчто- то, что может или не может быть командой Я ожидал.

rrauenza
источник
3
Что, если figletбыл в /usr/local/binили /home/bob/stuff/programs/executable/figlet?
Жиль "ТАК - перестань быть злым"
3
type -p figlet > /dev/null && figlet "foo"

Команда bash typeнаходит команду, функцию, псевдоним, ключевое слово или встроенную функцию (см. help type) И распечатывает местоположение или определение. Он также возвращает код возврата, представляющий результат поиска; true (0), если найдено. Итак, что мы делаем здесь, это пытаемся найти figletпо пути ( -pозначает искать только файлы, а не встроенные модули или функции, а также подавлять сообщения об ошибках), отбрасывать вывод (это то, что > /dev/nullделает), и если он возвращает true ( &&) , он выполнит figlet.

Это проще, если figletнаходится в фиксированном месте:

[ -x /usr/bin/figlet ] && /usr/bin/figlet "foo"

Здесь мы используем testкоманду (aka [), чтобы увидеть, /usr/bin/figletявляется ли исполняемый ( -x) и если да (&& ). Я думаю, что это решение более портативное, чем использование, typeи это, как мне кажется, башмизм.

Вы можете сделать функцию, которая сделает это за вас:

function x() {
    if type -p "$1" >/dev/null; then
        cmd="$1"
        shift
        "$cmd" "$@"
    fi
}

(Кавычки необходимы из-за потенциальных пробелов)

Тогда вы просто сделаете:

x figlet "foo"
eewanco
источник
0

о /, я бы сказал что-то вроде

#!/usr/bin/env bash
# if figlet is installed :
if [ "$(which figlet 2>/dev/null)" ]; then
       # do what you wanted to do
       echo "foo"
       figlet "Starting"
       echo "moo"
       figlet "Working"
       echo "foo moo"
       figlet "Finished"
# if not
else
       # exit program with an error
       echo "please install figlet"
       exit 1
fi
Jus de Patate_
источник
0

Вы можете выполнить тестирование, подавив любой вывод и протестировать код успеха / сбоя. Выберите аргументы для figlet, чтобы сделать этот тест недорогим. -? или --help или --version - очевидные возможности.

if figlet --help >/dev/null 2>&1 ; then
    # figlet is available
    echo "foo"
    figlet "starting"
    #etc
else
    rc=$?
    echo "figlet is not installed or not working correctly (return code ${rc})"
fi

Добавлено в ответ на комментарий ниже: если вы действительно хотите проверить, существует ли фиглет, а не то, что он пригоден для использования, то вы бы сделали

figlet --help >/dev/null 2>&1 
rc=$?
if [ $rc -eq 127 ] ; then # 127 is "command not found" on linux bash 4.4.23(1)
    echo "command figlet not found"
else
    figlet "whatever" # use figlet
fi
nigel222
источник
Проблема здесь в том, что figlet может дать сбой по причинам, не установленным, поэтому простого тестирования кода выхода недостаточно.
Крис
Если вы действительно хотите проверить только его установку, то вы хотите проверить, $?равно ли оно 127 (в моей системе Linux). 127 «команда не найдена». Но я думаю, что для рациональных команд, если command --helpне получится, то установки достаточно, чтобы ее не было!
nigel222
1
126 - это «команда найдена, но не исполняемая», так что вы также можете проверить это. К сожалению, вы не можете зависеть от --helpдоступности. У утилиты Posix, к примеру, его нет, и руководящие принципы posix действительно рекомендуют его . at --helpсбой at: invalid option -- '-', например, и состояние выхода в 130моей системе.
Крис
Также, конечно, если figlet может выйти со статусом 127, это тоже может быть проблемой.
Крис
Справедливая точка зрения о 126. У большинства здравомыслящих утилит есть какие-то легкие безобидные опции команд, такие как -? --help --version... или вы можете узнать, с чем выходить figlet -0 --illegal, и воспринимать это как показатель успеха (если это не 127, что я бы оценил как саботаж).
nigel222