Linux-Shell-Skript: Führen Sie ein Programm nur aus, wenn es vorhanden ist. Ignorieren Sie es, wenn es nicht vorhanden ist

15

Ich programmiere ein Linux-Shell-Skript , das während seiner Ausführung nur Statusbanner druckt, wenn beispielsweise das richtige Tool verwendet wirdfiglet , ist installiert (dies ist: erreichbar über Systempfad ).

Beispiel:

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

Ich möchte, dass mein Skript auch dann fehlerfrei funktioniert , wennfiglet es nicht installiert ist .

Was könnte eine praktische Methode sein ?

Sopalajo de Arrierez
quelle
@sudodus: Das Ignorieren des Befehls 'figlet' (und seiner Parameter) wäre in Ordnung. Natürlich die Ausführung fortsetzen.
Sopalajo de Arrierez
2
Der Titel dieser Frage brachte mich in alle möglichen metaphysischen Probleme
g_uint
2
Möchten Sie alle Fehler ignorieren ? Verwenden Sie einfach figlet ... || true.
Giacomo Alzetta
Wenn Sie sich nicht für Exit-Codes interessieren, ist eine Verknüpfung zu verwenden figlet || true, aber in Ihrem Fall ist wahrscheinlich eine Shell-Funktion, die Echos-Klartext Wenn kein Banner gedruckt werden kann, eher das, was Sie wollen.
31.

Antworten:

30

Meine Interpretation würde eine Wrapper-Funktion verwenden, die den gleichen Namen hat wie das Tool. Führen Sie in dieser Funktion das eigentliche Tool aus, falls es vorhanden ist:

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

Dann können Sie figlet arg1 arg2...unverändert in Ihrem Skript haben.

@Olorin hat eine einfachere Methode entwickelt: Definieren Sie eine Wrapper-Funktion nur, wenn dies erforderlich ist (falls das Tool nicht vorhanden ist):

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

Wenn Sie figletmöchten, dass die Argumente gedruckt werden, auch wenn das Figlet nicht installiert ist, passen Sie Olorins Vorschlag wie folgt an:

if ! command -v figlet > /dev/null; then figlet() { printf '%s\n' "$*"; }; fi
Jeff Schaller
quelle
1
Wo kommt commandher? Ich habe es nicht in meinen Installationen (Red Hat 6.8 Enterprise und Cygwin64).
Eewanco
1
@eewanco, siehe unix.stackexchange.com/a/85250/117549 ; lange Rede kurzer Sinn, es ist eingebaut, um zu schlagen, was wahrscheinlich Ihre Muschel ist.
Jeff Schaller
4
commandist eine POSIX Bourne-Shell. Normalerweise führt es den angegebenen Befehl aus, aber das -vFlag lässt es sich eher wie eine typeandere eingebaute Shell verhalten.
Wyrm
@eewanco type -a commandwird es dir zeigen. whichzeigt nur ausführbare Dateien auf Ihren $PATH, keine eingebauten Schlüsselwörter, Funktionen oder Aliase.
30.
@ l0b0: auf RedHat-family mit bash und dem / den Standardprofil (en) which findet sich ein passender Alias, weil er sich whichselbst /usr/bin/whichals Aliase ansieht, um eine Liste der zu durchsuchenden Aliasnamen der Shell auszuleiten (!) ( \whichUnterdrückt natürlich den Alias ​​und verwendet nur das Programm, das keine Aliase
anzeigt
14

Sie können testen, ob figletvorhanden

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

Ein üblicher Weg, dies zu tun, ist mit test -xaka [ -x. Hier ist ein Beispiel aus /etc/init.d/ntpeinem Linux-System:

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

Diese Variante setzt voraus, dass der vollständige Pfad der ausführbaren Datei bekannt ist. In habe /bin/lesspipeich ein Beispiel gefunden, das das umgeht, indem es -xund den whichBefehl kombiniert :

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

Auf diese Weise funktioniert dies, ohne vorher zu wissen, wo sich PATHdie bunzipausführbare Datei befindet.

Kasperd
quelle
4
Nicht verwendenwhich . Und selbst wenn es whichfunktioniert, ist die Verwendung test -xder Ausgabe dumm: Wenn Sie einen Pfad abrufen which, ist er vorhanden.
Gilles 'SO - hör auf böse zu sein'
1
@Gilles: Technisch gesehen ist test -xes mehr als nur zu überprüfen, ob eine Datei existiert (wofür test -eist das gedacht ). Es wird auch geprüft, ob die Datei über die festgelegten Ausführungsberechtigungen verfügt.
Comfreak
@comfreak So auch which.
Gilles 'SO - hör auf böse zu sein'
5

Überprüfen Sie zu Beginn Ihres Skripts, figletob es eine Shell-Funktion gibt. Wenn dies nicht der Fall ist, definieren Sie eine Shell-Funktion, die nichts bewirkt:

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

typeÜberprüft, ob figleteine integrierte Shell, eine Funktion, ein Alias ​​oder ein Schlüsselwort vorhanden ist, >/dev/null 2>&1verwirft stdin und stdout, sodass Sie keine Ausgabe erhalten, und figlet() { :; }definiert figletals eine Funktion, die nichts tut , wenn sie nicht vorhanden ist .

Auf diese Weise müssen Sie nicht jede Zeile Ihres Skripts bearbeiten figletoder jedes Mal prüfen, ob sie vorhanden istfiglet Aufruf ist.

Sie können eine Diagnosemeldung hinzufügen, wenn Sie möchten:

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

Als Bonus, da Sie nicht erwähnt haben, welche Shell Sie verwenden, glaube ich, dass dies POSIX-kompatibel ist, so dass es auf fast jeder Shell funktionieren sollte.

Chris
quelle
Ich mag die Einfachheit dieser Antwort. Sie können auch ersetzen type figlet >/dev/null 2>&1mit , hash figlet 2>/dev/nullwenn Sie bash verwenden. (Das OP sagte "wenn es in meinem WEG ist".)
Joe
5

Eine weitere Alternative - ein Muster, das ich in Skripten zur automatischen Konfiguration von Projekten gesehen habe:

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

$FIGLET "Hello, world!"

In Ihrem speziellen Fall könnten Sie sogar tun,

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

Wenn Sie den spezifischen Pfad nicht kennen, können Sie mehrere elif(siehe oben) ausprobieren, um bekannte Speicherorte zu ermitteln, oder einfach die verwenden PATH, um den Befehl immer aufzulösen:

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

Im Allgemeinen bevorzuge ich beim Schreiben von Skripten das Aufrufen von Befehlen nur an bestimmten von mir angegebenen Stellen. Ich mag nicht die Ungewissheit / das Risiko, was der Endbenutzer in seine PATH, vielleicht in seine, investiert hat~/bin .

Wenn ich zum Beispiel ein kompliziertes Skript für andere schreibe, das möglicherweise Dateien basierend auf der Ausgabe eines bestimmten Befehls, den ich aufrufe, entfernt, möchte ich nicht versehentlich etwas in ihrem Skript aufgreifen, das möglicherweise ~/binder Befehl ist oder nicht Ich erwartete.

rrauenza
quelle
3
Was wäre wenn figletin /usr/local/binoder /home/bob/stuff/programs/executable/figlet?
Gilles 'SO - hör auf böse zu sein'
3
type -p figlet > /dev/null && figlet "foo"

Der bash- typeBefehl findet einen Befehl, eine Funktion, einen Alias, ein Schlüsselwort oder eine integrierte Funktion (siehe help type) und gibt den Speicherort oder die Definition aus. Es gibt auch einen Rückkehrcode zurück, der das Ergebnis der Suche darstellt. true (0) falls gefunden. Wir versuchen hier also, figletim Pfad zu suchen ( -pdh nur nach Dateien zu suchen, nicht nach eingebauten Dateien oder Funktionen, und Fehlermeldungen zu unterdrücken), die Ausgabe zu verwerfen (das ist es, was es > /dev/nulltut) und wenn es true ( &&) zurückgibt wird ausgeführt figlet.

Dies ist einfacher, wenn Sie figletsich an einem festen Ort befinden:

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

Hier verwenden wir den testBefehl (aka [), um zu sehen, ob er /usr/bin/figletausführbar ist ( -x) und führen ihn gegebenenfalls aus ( &&). Diese Lösung ist meiner Meinung nach portabler als die Verwendung, typewas meiner Meinung nach ein Bashismus ist.

Sie könnten eine Funktion erstellen, die dies für Sie erledigt:

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

(Die Anführungszeichen sind wegen möglicher Leerzeichen notwendig)

Dann machen Sie einfach:

x figlet "foo"
Eewanco
quelle
0

o / ich würde sowas sagen

#!/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_
quelle
0

Sie können eine Testausführung durchführen, jede Ausgabe unterdrücken und den Erfolgs- / Fehlercode testen. Wählen Sie Argumente aus, um diesen Test kostengünstig zu gestalten. -? oder --help oder --version sind offensichtliche Möglichkeiten.

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

Als Antwort auf den Kommentar unten hinzugefügt: Wenn Sie wirklich testen möchten, ob das Figlet existiert, und nicht, ob es verwendbar ist, würden Sie dies tun

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
quelle
Das Problem hierbei ist, dass figlet nicht nur wegen fehlender Installation fehlschlagen kann. Daher reicht es nicht aus, nur den Exit-Code zu testen.
Chris
Wenn Sie wirklich nur auf seine Installation testen möchten, dann möchten Sie testen, ob $?127 (auf meinem Linux-System) entspricht. 127 ist "Befehl nicht gefunden". Aber ich denke, dass bei rationalen Befehlen command --helpdie Installation bei einem Fehlschlag so weit verzweigt ist, dass sie auch nicht vorhanden sein könnte !.
Nigel222
1
126 ist "Befehl gefunden, aber nicht ausführbar", daher sollten Sie auch den Befehl testen. Sie können sich leider nicht darauf verlassen --help, verfügbar zu sein. Posix-Dienstprogramme haben es nicht, und die Posix-Richtlinien empfehlen es tatsächlich . at --helpscheitert at: invalid option -- '-'zum Beispiel mit und einem Exit-Status von 130auf meinem System.
Chris
Auch wenn figlet mit dem Status 127 beendet werden kann, könnte dies ebenfalls ein Problem sein.
Chris
Fair point about 126. Die meisten vernünftigen Hilfsprogramme haben eine Art leichtgewichtiger harmloser Befehlsoptionen, wie -? --help --version... oder Sie könnten herausfinden, was damit herauskommt figlet -0 --illegal, und dies als Ihren Erfolgsindikator behandeln (solange es nicht 127 ist, was ich klassifizieren würde) als Sabotage).
Nigel222