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ą ?



@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

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

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.


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.


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.


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"

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

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

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.