Сценарій оболонки Linux: запускайте програму лише за наявності, ігноруйте її, якщо вона не існує


15

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

Приклад:

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

Я хотів би, щоб мій сценарій працював без помилок, навіть коли figletйого не встановлено .

Що може бути практичним методом ?



@sudodus: просто ігнорувати команду 'figlet' (та її параметри) було б добре. Продовження виконання, звичайно.
Sopalajo de Arrierez

2
Назва цього питання дістала мене у всіляких метафізичних проблемах
g_uint

2
Ви хочете ігнорувати всі помилки? Просто використовуйте figlet ... || true.
Джакомо Альзетта

Якщо ви не переймаєтесь кодами виходу, використовуватимете ярлик figlet || true, але у вашому випадку, швидше за все, вам потрібна функція оболонки, яка надрукується простим текстом Echos Якщо не можна роздрукувати жоден банер.
eckes

Відповіді:


30

У моїй інтерпретації буде використана функція обгортки, названа так само, як і інструмент; у цій функції виконайте реальний інструмент, якщо він існує:

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

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

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

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

Якщо ви хочете, щоб аргументи 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 ; довга історія коротко, вона вбудована в баш, який, ймовірно, ваша оболонка.
Джефф Шаллер

4
commandє вбудованою оболонкою POSIX Bourne. Він, як правило, виконує надану команду, але -vпрапор змушує його поводитись більше, як typeінший вбудований оболонку.
wyrm

@eewanco type -a commandпокаже вам. whichбуде тільки показати виконувані файли на ваших $PATH, а НЕ вбудовані модулі, ключові слова, функції або псевдонімами.
l0b0

@ L0b0: на RedHat-сім'ї з баш і профілем за замовчуванням (и) which дійсно знайти який можна застосовувати псевдонім, бо він псевдоніми whichсебе працювати /usr/bin/which(!) І труби в ньому перелічені псевдоніми оболонкових дивитися в (звичайно \whichпригнічує псевдонім і використовує лише програму, яка не показує псевдоніми.)
dave_thompson_085

14

Ви можете перевірити, чи figletіснує

if type figlet >/dev/null 2>&1
then
    echo Figlet is installed
fi

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/lesspipeI знайшов приклад , який працює по всьому , що шляхом об'єднання -xі whichкоманди:

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

Таким чином , це буде працювати , не знаючи заздалегідь , де в виконуваний.PATHbunzip


4
Не використовуйтеwhich . І навіть якщо це whichпрацює, використання test -xна його виході нерозумно: якщо ви отримуєте шлях which, він існує.
Жиль "ТАК - перестань бути злим"

1
@Gilles: технічно кажучи, 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якщо ви використовуєте Баш. (ОП сказала "якщо це в моїй ПАРТІ".)
Джо

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що може чи не може бути командою Я очікував.


3
Що робити, якщо figletбув /usr/local/binабо /home/bob/stuff/programs/executable/figlet?
перестань бути злим"

3
type -p figlet > /dev/null && figlet "foo"

Команда bash typeзнаходить команду, функцію, псевдонім, ключове слово або вбудований (див. help type) І друкує місцеположення чи визначення. Він також повертає код повернення, що представляє результат пошуку; вірно (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"

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

0

Ви можете виконати тестове виконання, придушивши будь-який вихід, та перевірити код успіху / помилки. Виберіть аргументи для філетки, щоб зробити цей тест недорогим. -? або - допомога або --вертання - це очевидні можливості.

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

Проблема тут полягає в тому, що філетка може вийти з ладу з причин, крім того, що не буде встановлена, тому просто тестування вихідного коду недостатньо.
Кріс

Якщо ви дійсно хочете протестувати лише його встановлення, тоді ви хочете перевірити, чи $?дорівнює 127 (у моїй системі Linux). 127 - "команда не знайдена". Але я думаю, що для раціональних команд, якщо command --helpне вдасться, то установка є достатньою, щоб її не було!
nigel222

1
126 - це "команда знайдена, але не виконується", тому ви також хочете перевірити її. На жаль, ви не можете залежати від --helpнаявності. У утиліт Posix його немає, наприклад, і рекомендації posix насправді рекомендують проти нього . at --helpне відповідає at: invalid option -- '-', наприклад, і статусу виходу 130в моїй системі.
Кріс

Звичайно, якщо філе може вийти зі статусом 127, це теж може бути проблемою.
Кріс

Справедливий момент близько 126. У більшості розумних утиліт є якісь легкі нешкідливі параметри команд, такі як -? --help --version... або ви можете дізнатися, що з цього figlet -0 --illegalвиходить, і вважати це своїм показником успішності (доки це не 127, що я б класифікував як диверсія).
nigel222
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.