Skrypt powłoki Linux: Uruchom program tylko wtedy, gdy istnieje, zignoruj ​​go, jeśli nie istnieje

15

Programuję skrypt powłoki Linuksa , który wydrukuje banery stanu podczas jego wykonywania tylko wtedy, gdy zainstalowanefiglet jest odpowiednie narzędzie (powiedzmy : osiągalne na ścieżce systemowej) ).

Przykład:

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

Chciałbym, aby mój skrypt działał bez błędów, nawet jeśli niefiglet jest zainstalowany .

Co może być praktyczną metodą ?

Sopalajo de Arrierez
źródło
@sudodus: po prostu zignorowanie polecenia „figlet” (i jego parametrów) byłoby OK. Oczywiście kontynuacja wykonania.
Sopalajo de Arrierez
2
Tytuł tego pytania
wprawił
2
Czy chcesz zignorować wszystkie błędy? Po prostu użyj figlet ... || true.
Giacomo Alzetta
Jeśli nie przejmujesz się kodami wyjścia, skorzystaj ze skrótu figlet || true, ale w twoim przypadku prawdopodobnie jest to funkcja powłoki, którą w postaci zwykłego tekstu Echos Jeśli nie można wydrukować banera, jest bardziej prawdopodobne, że chcesz.
eckes

Odpowiedzi:

30

Moja interpretacja użyłaby funkcji otoki nazwanej tak samo jak narzędzie; w tej funkcji uruchom prawdziwe narzędzie, jeśli istnieje:

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

Wtedy możesz mieć figlet arg1 arg2...niezmieniony w swoim skrypcie.

@Olorin zaproponował prostszą metodę: zdefiniuj funkcję otoki tylko wtedy, gdy jest to konieczne (jeśli narzędzie nie istnieje):

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

Jeśli chcesz, aby argumenty figletbyły drukowane, nawet jeśli figlet nie jest zainstalowany, dostosuj sugestię Olorin w następujący sposób:

if ! command -v figlet > /dev/null; then figlet() { printf '%s\n' "$*"; }; fi
Jeff Schaller
źródło
1
Skąd commandpochodzi? Nie mam go w moich instalacjach (Red Hat 6.8 Enterprise i Cygwin64).
eewanco
1
@eewanco, patrz unix.stackexchange.com/a/85250/117549 ; Krótko mówiąc, jest wbudowany w bash, który prawdopodobnie jest twoją powłoką.
Jeff Schaller
4
commandjest wbudowaną powłoką POSIX Bourne. Zwykle wykonuje podaną komendę, ale -vflaga sprawia, że ​​zachowuje się bardziej jak typekolejna wbudowana powłoka.
wyrm
@eewanco type -a commandpokaże ci. whichpokaże tylko pliki wykonywalne na Twoich $PATH, nie wbudowanych, słowach kluczowych, funkcjach lub aliasach.
l0b0
@ l0b0: na RedHat-rodziny z bash i profilu domyślnym (ów) which ma znaleźć stosowną alias, bo aliasy whichsię uruchomić /usr/bin/which(!), a rura jest lista aliasów skorupa jest spojrzeć w (oczywiście \whichhamuje alias i używa tylko programu, który nie wyświetla aliasów.)
dave_thompson_085
14

Możesz sprawdzić, czy figletistnieje

if type figlet >/dev/null 2>&1
then
    echo Figlet is installed
fi
roaima
źródło
6

Częstym sposobem na to jest test -xaka [ -x. Oto przykład wzięty z /etc/init.d/ntpsystemu Linux:

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

Ten wariant polega na znajomości pełnej ścieżki pliku wykonywalnego. W /bin/lesspipeznalazłem przykład, który działa w ten sposób, łącząc -xi whichpolecenie:

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

W ten sposób zadziała to bez uprzedniego wiedzieć, gdzie znajduje PATHsię bunzipplik wykonywalny.

kasperd
źródło
4
Nie używaćwhich . I nawet jeśli whichzadziałało, użycie test -xna jego wyjściu jest głupie: jeśli uzyskasz ścieżkę which, istnieje.
Gilles „SO- przestań być zły”,
1
@Gilles: Technicznie rzecz biorąc, test -xnie tylko sprawdza, czy plik istnieje (po to test -ejest). Sprawdza również, czy plik ma ustawione uprawnienia do wykonywania.
comfreak
@comfreak Tak samo which.
Gilles „SO- przestań być zły”
5

Na początku skryptu sprawdź, czy figletistnieje, a jeśli nie, zdefiniuj funkcję powłoki, która nic nie robi:

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

typesprawdza, czy figletistnieje jako wbudowana powłoka, funkcja, alias lub słowo kluczowe, >/dev/null 2>&1odrzuca stdin i stdout, aby nie uzyskać żadnych danych wyjściowych, a jeśli nie istnieje, figlet() { :; }określafiglet jako funkcję, która nic nie robi.

W ten sposób nie musisz edytować każdej linii używanego skryptu figletani sprawdzać, czy istnieje za każdym razemfiglet wywołaniu.

Możesz dodać komunikat diagnostyczny, jeśli chcesz:

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

Jako bonus, ponieważ nie wspomniałeś, której powłoki używasz, uważam, że jest ona zgodna z POSIX, więc powinna działać na większości powłok.

Chris
źródło
Podoba mi się prostota tej odpowiedzi. Można również wymienić type figlet >/dev/null 2>&1ze hash figlet 2>/dev/nulljeśli używasz bash. (OP powiedział „jeśli jest w mojej ŚCIEŻCE”).
Joe
5

Inna alternatywa - wzorzec, który widziałem w skryptach automatycznej konfiguracji projektu:

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

$FIGLET "Hello, world!"

W twoim konkretnym przypadku możesz nawet zrobić,

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!"

Jeśli nie znasz konkretnej ścieżki, możesz wypróbować wiele elif(patrz wyżej), aby wypróbować znane lokalizacje, lub po prostu użyć polecenia, PATHaby zawsze rozwiązać polecenie:

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

Ogólnie, pisząc skrypty, wolę wywoływać polecenia tylko w określonych przeze mnie lokalizacjach. Nie podoba mi się niepewność / ryzyko związane z tym, co użytkownik końcowy mógł w to włożyć PATH, być może sam ~/bin.

Jeśli na przykład piszę skomplikowany skrypt dla innych, który może usuwać pliki na podstawie danych wyjściowych określonego polecenia, które wywołuję, nie chciałbym przypadkowo odebrać czegoś, ~/binco mogłoby lub nie być poleceniem Oczekiwałem.

rrauenza
źródło
3
Co jeśli figletbył w /usr/local/binlub /home/bob/stuff/programs/executable/figlet?
Gilles „SO- przestań być zły”
3
type -p figlet > /dev/null && figlet "foo"

Polecenie bash typeznajduje polecenie, funkcję, alias, słowo kluczowe lub wbudowane (patrz help type) i drukuje lokalizację lub definicję. Zwraca również kod powrotu reprezentujący wynik wyszukiwania; true (0) jeśli znaleziono. Więc staramy się znaleźć figletna ścieżce ( -poznacza to tylko wyszukiwanie plików, a nie wbudowane funkcje i funkcje, a także tłumienie komunikatów o błędach), odrzucanie danych wyjściowych (to jest to, co > /dev/nulldziała) i jeśli zwraca wartość true ( &&) , wykona sięfiglet .

Jest to prostsze, jeśli figletznajduje się w stałej lokalizacji:

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

Tutaj używamy testpolecenia (aka [), aby sprawdzić, czy /usr/bin/figletjest wykonywalny ( -x), a jeśli tak ( &&), wykonaj go. Myślę, że to rozwiązanie jest bardziej przenośne niż używanietypeWydaje mi się, że co uważam za bashizm.

Możesz stworzyć funkcję, która zrobi to za Ciebie:

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

(Cytaty są konieczne ze względu na potencjalne spacje)

Następnie wystarczy zrobić:

x figlet "foo"
eewanco
źródło
0

o /, powiedziałbym coś takiego

#!/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_
źródło
0

Możesz wykonać test, pomijając wszelkie dane wyjściowe i przetestować kod sukcesu / niepowodzenia. Wybierz argumenty, aby ten test był niedrogi. -? lub --help lub --version to oczywiste możliwości.

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

Dodano w odpowiedzi na komentarz poniżej: jeśli naprawdę chcesz przetestować, że figlet istnieje, a nie to, że jest użyteczny, to zrobiłbyś to

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
źródło
Problem polega na tym, że figlet może się nie powieść z powodów innych niż brak instalacji, więc samo testowanie kodu wyjścia nie wystarczy.
Chris
Jeśli naprawdę chcesz przetestować tylko pod kątem instalacji, to chcesz sprawdzić, czy $?wynosi 127 (w moim systemie Linux). 127 to „polecenie nie znaleziono”. Ale myślę, że w przypadku racjonalnych poleceń, jeśli się command --helpnie powiedzie, instalacja jest na tyle wykręcona, że ​​równie dobrze może jej nie być!
nigel222
1
126 jest „poleceniem znalezionym, ale nie wykonalnym”, dlatego też chciałbyś przetestować również to. Niestety nie możesz polegać na --helpdostępności. Po pierwsze, narzędzia Posix go nie mają, a wytyczne Posix faktycznie go nie zalecają . at --helpkończy się niepowodzeniem at: invalid option -- '-', na przykład, i stanem wyjścia 130w moim systemie.
Chris
Oczywiście, jeśli figlet może wyjść ze statusem 127, może to również stanowić problem.
Chris
Trzeba przyznać, że około 126. Większość rozsądnych narzędzi ma jakieś lekkie, nieszkodliwe opcje poleceń, takie jak -? --help --version... lub możesz dowiedzieć się, figlet -0 --illegalz których wyjść, i potraktować to jako wskaźnik sukcesu (o ile nie jest to 127, do którego zaliczę klasę jako sabotaż).
nigel222