Wie kann ich anhand eines Bash-Skripts überprüfen, ob ein Programm vorhanden ist?

2209

Wie würde ich überprüfen, ob ein Programm vorhanden ist, das entweder einen Fehler zurückgibt und beendet oder mit dem Skript fortfährt?

Es scheint, als sollte es einfach sein, aber es hat mich verblüfft.

gregh
quelle
Was ist ein "Programm"? Enthält es Funktionen und Aliase? whichgibt für diese true zurück. typeohne Argumente gibt zusätzlich true für reservierte Wörter und eingebaute Shell zurück. Wenn "Programm" "auslösbar in $PATH" bedeutet, sehen Sie diese Antwort .
Tom Hale

Antworten:

3055

Antworten

POSIX-kompatibel:

command -v <the_command>

Für Bash-spezifische Umgebungen:

hash <the_command> # For regular commands. Or...
type <the_command> # To check built-ins and keywords

Erläuterung

Vermeiden Sie which. Es ist nicht nur ein externer Prozess, den Sie starten, um sehr wenig zu tun (was bedeutet hash, dass integrierte Funktionen viel billiger sind typeoder commandviel billiger sind), sondern Sie können sich auch darauf verlassen, dass die integrierten Funktionen tatsächlich das tun, was Sie möchten, während die Auswirkungen externer Befehle leicht davon abweichen können System zu System.

Warum sich darum kümmern?

  • Viele Betriebssysteme haben einen which, der nicht einmal einen Exit-Status festlegt , was bedeutet, if which foodass er dort nicht einmal funktioniert und immer meldet, dass er fooexistiert, auch wenn dies nicht der Fall ist (beachten Sie, dass einige POSIX-Shells dies anscheinend auch tun hash).
  • Viele Betriebssysteme machen whichbenutzerdefinierte und böse Dinge wie das Ändern der Ausgabe oder sogar das Einhängen in den Paketmanager.

Also nicht benutzen which. Verwenden Sie stattdessen eine der folgenden Optionen:

$ command -v foo >/dev/null 2>&1 || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }
$ type foo >/dev/null 2>&1 || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }
$ hash foo 2>/dev/null || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }

(Kleinere Randnotiz: Einige schlagen vor, dass 2>&-es dasselbe ist, 2>/dev/nullaber kürzer - dies ist nicht wahr . 2>&-Schließt FD 2, was einen Fehler im Programm verursacht, wenn versucht wird, in stderr zu schreiben, was sich stark vom erfolgreichen Schreiben und Verwerfen der Ausgabe unterscheidet (und gefährlich!))

Wenn Ihr Hash Bang ist /bin/sh, sollten Sie sich darum kümmern, was POSIX sagt. typeund hash‚s Exit - Codes sind nicht sehr gut von POSIX definiert, und hashwerden erfolgreich gesehen zu verlassen , wenn der Befehl nicht vorhanden ist (habe dies nicht gesehen typenoch) nicht . commandDer Exit-Status von POSIX ist gut definiert, sodass er wahrscheinlich am sichersten zu verwenden ist.

Wenn Ihr Skript jedoch verwendet bash, spielen POSIX-Regeln keine Rolle mehr und beides typeund hashsind absolut sicher zu verwenden. typeJetzt muss -Pnur noch das PATHund gesucht werdenhash hat den Nebeneffekt, dass der Speicherort des Befehls gehasht wird (für eine schnellere Suche bei der nächsten Verwendung), was normalerweise eine gute Sache ist, da Sie wahrscheinlich nach seiner Existenz , um ihn tatsächlich zu verwenden .

Als einfaches Beispiel ist hier eine Funktion, die ausgeführt wird, gdatewenn sie existiert, andernfalls date:

gnudate() {
    if hash gdate 2>/dev/null; then
        gdate "$@"
    else
        date "$@"
    fi
}
lhunath
quelle
35
@Geert: Der Teil &> / dev / null verbirgt die Nachricht 'type', die ausgegeben wird, wenn 'foo' nicht existiert. Das> & 2 im Echo stellt sicher, dass die Fehlermeldung anstelle der Standardausgabe an den Standardfehler gesendet wird. denn das ist Konvention. Beide werden auf Ihrem Terminal angezeigt, aber Standardfehler sind definitiv die bevorzugte Ausgabe für Fehlermeldungen und unerwartete Warnungen.
lhunath
5
Das -P-Flag funktioniert nicht in 'sh', z. B. stackoverflow.com/questions/2608688/…
momeara
130
Für diejenigen, die mit 'erweiterter' I / O-Umleitung in Bash nicht vertraut sind: 1) 2>&- ("Close Output File Descriptor 2", was stderr ist) hat das gleiche Ergebnis wie 2> /dev/null; 2) >&2ist eine Verknüpfung für 1>&2, die Sie möglicherweise als "stdout nach stderr umleiten" erkennen. Weitere Informationen finden Sie auf der I / O-Umleitungsseite des Advanced Bash Scripting Guide .
Mikewaters
9
@mikewaters Das ABS sieht ziemlich fortschrittlich aus und beschreibt eine breite Palette von Bash- und Nicht-Bash-CLI-Funktionen, ist jedoch in vielerlei Hinsicht sehr nachlässig und folgt nicht den bewährten Verfahren. Ich habe fast nicht genug Platz in diesem Kommentar, um etwas zu schreiben. aber ich kann ein paar zufälligen Beispiele von fehlerhaftem Code einfügen: while read element ; do .. done <<< $(echo ${ArrayVar[*]}), for word in $(fgrep -l $ORIGINAL *.txt), ls -l "$directory" | sed 1d , {{für einen in seq $BEGIN $END}}, ... Viele haben versucht , die Autoren zu kontaktieren und Verbesserungen vorzuschlagen , aber es ist kein Wiki und Anfragen haben auf taube Ohren gelandet.
lhunath
56
@mikewaters 2>&-ist nicht dasselbe wie 2>/dev/null. Ersteres schließt den Dateideskriptor, während letzteres ihn einfach umleitet /dev/null. Möglicherweise wird kein Fehler angezeigt, da das Programm versucht, Sie auf stderr darüber zu informieren, dass stderr geschlossen ist.
Nyuszika7h
576

Das Folgende ist eine tragbare Methode, um zu überprüfen, ob ein Befehl in vorhanden $PATH und ausführbar ist:

[ -x "$(command -v foo)" ]

Beispiel:

if ! [ -x "$(command -v git)" ]; then
  echo 'Error: git is not installed.' >&2
  exit 1
fi

Die Prüfung der ausführbaren Datei ist erforderlich, da bash eine nicht ausführbare Datei zurückgibt, wenn keine ausführbare Datei mit diesem Namen in gefunden wird $PATH.

Beachten Sie auch, dass, wenn eine nicht ausführbare Datei mit demselben Namen wie die ausführbare Datei früher vorhanden ist $PATH, dash die erstere zurückgibt, obwohl die letztere ausgeführt wird. Dies ist ein Fehler und verstößt gegen den POSIX-Standard. [ Fehlerbericht ] [ Standard ]

Darüber hinaus schlägt dies fehl, wenn der gesuchte Befehl als Alias ​​definiert wurde.

nyuszika7h
quelle
4
Wird command -vein Pfad auch für eine nicht ausführbare Datei erstellt? Das heißt, das -x wirklich notwendig?
Einpoklum
5
@einpoklum -xtestet, ob die Datei ausführbar ist, was die Frage war.
Ken Sharp
3
@ KenSharp: Aber das scheint überflüssig zu sein, da commandes selbst testen wird, ob es ausführbar ist - nicht wahr ?
Einpoklum
13
@einpoklum Ja, das ist notwendig. Tatsächlich kann sogar diese Lösung in einem Randfall brechen. Vielen Dank, dass Sie mich darauf aufmerksam gemacht haben. dash, bash und zsh überspringen $PATHbeim Ausführen eines Befehls nicht ausführbare Dateien . Das Verhalten von command -vist jedoch sehr inkonsistent. Im Bindestrich wird die erste übereinstimmende Datei zurückgegeben $PATH, unabhängig davon, ob sie ausführbar ist oder nicht. In bash gibt es die erste ausführbare Übereinstimmung in zurück $PATH, aber wenn es keine gibt, kann es eine nicht ausführbare Datei zurückgeben. Und in zsh wird niemals eine nicht ausführbare Datei zurückgegeben.
Nyuszika7h
5
Soweit ich das beurteilen kann, dashist es das einzige von diesen drei, das nicht POSIX-konform ist. [ -x "$(command -v COMMANDNAME)"]wird in den anderen beiden arbeiten. Es sieht so aus, als ob dieser Fehler bereits gemeldet wurde, aber noch keine Antworten erhalten hat: bugs.debian.org/cgi-bin/bugreport.cgi?bug=874264
nyuszika7h
210

Ich bin mit lhunath einverstanden, von der Verwendung abzuraten which, und seine Lösung ist für Bash-Benutzer vollkommen gültig . Um jedoch tragbarer zu sein, command -vsollte stattdessen Folgendes verwendet werden:

$ command -v foo >/dev/null 2>&1 || { echo "I require foo but it's not installed.  Aborting." >&2; exit 1; }

Der Befehl commandist POSIX-kompatibel. Die Spezifikation finden Sie hier: Befehl - Führen Sie einen einfachen Befehl aus

Hinweis: typeIst POSIX-kompatibel, jedoch type -Pnicht.

GregV
quelle
3
Gleich wie oben - exit 1;tötet ein xterm, wenn es von dort aus aufgerufen wird.
Benutzer unbekannt
1
Dies würde auf einem Standard-sh nicht funktionieren: Sie &> sind keine gültigen Weiterleitungsanweisungen.
Jyavenard
7
@jyavenard: Die Frage ist mit bash markiert , daher die präzisere bash-spezifische Weiterleitungsnotation &>/dev/null. Ich stimme Ihnen jedoch zu, was wirklich wichtig ist, ist die Portabilität. Ich habe meine Antwort entsprechend bearbeitet und verwende jetzt die Standard-Weiterleitung >/dev/null 2>&1.
GregV
Um diese Antwort noch weiter zu verbessern, würde ich zwei Dinge tun: 1: Verwenden Sie "&>", um sie zu vereinfachen, wie Joshs Antwort. 2: brechen Sie das {} in eine zusätzliche Zeile, setzen Sie einen Tabulator vor das Echo, um die Lesbarkeit zu
gewährleisten
Ich habe diesen einen Liner einfach in eine Bash-Funktion gesteckt, wenn jemand es will ... github.com/equant/my_bash_tools/blob/master/tarp.bash
entspricht dem
94

In meiner .bashrc ist eine Funktion definiert, die dies erleichtert.

command_exists () {
    type "$1" &> /dev/null ;
}

Hier ist ein Beispiel, wie es verwendet wird (von meinem .bash_profile.)

if command_exists mvim ; then
    export VISUAL="mvim --nofork"
fi
Josh Strater
quelle
Was macht das &>?
Saad Malik
7
Das &> leitet sowohl stdout als auch stderr zusammen um.
Josh Strater
&>ist möglicherweise in Ihrer Version von Bash nicht verfügbar. Marcellos Code sollte gut funktionieren. es macht das gleiche.
Josh Strater
3
Fehler bei eingebauten und reservierten Wörtern: Versuchen Sie dies beispielsweise mit dem Wort then. Sehen Sie diese Antwort , wenn Sie die ausführbare Datei benötigen in existieren $PATH.
Tom Hale
84

Dies hängt davon ab, ob Sie wissen möchten, ob es in einem der Verzeichnisse der $PATHVariablen vorhanden ist oder ob Sie den absoluten Speicherort kennen. Wenn Sie wissen möchten, ob es sich um eine $PATHVariable handelt, verwenden Sie

if which programname >/dev/null; then
    echo exists
else
    echo does not exist
fi

andernfalls verwenden

if [ -x /path/to/programname ]; then
    echo exists
else
    echo does not exist
fi

Die Umleitung zu /dev/null/im ersten Beispiel unterdrückt die Ausgabe des whichProgramms.

Dreamlax
quelle
22
Sie sollten aus den in meinem Kommentar genannten Gründen wirklich nicht "which" verwenden.
lhunath
39

Um die Antworten von @ lhunath und @ GregV zu erweitern, hier der Code für die Personen, die diesen Scheck einfach in eine ifErklärung einfügen möchten :

exists()
{
  command -v "$1" >/dev/null 2>&1
}

So verwenden Sie es:

if exists bash; then
  echo 'Bash exists!'
else
  echo 'Your system does not have Bash'
fi
Romário
quelle
13
Lernbereitschaft und Verbesserung müssen belohnt werden. +1 Das ist sauber und einfach. Das einzige, was ich hinzufügen kann, ist, dass dies commandauch für Aliase erfolgreich ist, was möglicherweise etwas uninteressant ist. Das Überprüfen der Existenz in einer interaktiven Shell führt zu anderen Ergebnissen als beim Verschieben in ein Skript.
Palec
1
Ich habe gerade shopt -u expand_aliasesAliase zum Ignorieren / Ausblenden getestet und verwendet (wie alias ls='ls -F'in einer anderen Antwort erwähnt) und shopt -s expand_aliasessie über aufgelöst command -v. Daher sollte es möglicherweise vor der Prüfung festgelegt und danach deaktiviert werden. Dies kann sich jedoch auf den Funktionsrückgabewert auswirken, wenn Sie die Ausgabe des Befehlsaufrufs nicht explizit erfassen und zurückgeben.
Dragon788
24

Versuchen Sie es mit:

test -x filename

oder

[ -x filename ]

Aus der Bash-Manpage unter Bedingte Ausdrücke :

 -x file
          True if file exists and is executable.
dmckee --- Ex-Moderator Kätzchen
quelle
26
Das bedeutet, dass Sie bereits den vollständigen Pfad zur Anwendung kennen müssen.
lhunath
12
Das OP hat nicht angegeben, ob er nach einer bestimmten Instanz oder nach einer ausführbaren Instanz suchen möchte ... Ich habe es so beantwortet, wie ich es gelesen habe.
dmckee --- Ex-Moderator Kätzchen
16

So verwenden Sie hash, wie @lhunath vorschlägt , in einem Bash-Skript:

hash foo &> /dev/null
if [ $? -eq 1 ]; then
    echo >&2 "foo not found."
fi

Dieses Skript wird ausgeführt hashund prüft dann, ob der Exit-Code des letzten Befehls, der darin gespeicherte Wert $?, gleich ist 1. Wenn hashnicht gefunden wird foo, lautet der Exit-Code 1. Wenn foovorhanden, lautet der Exit-Code 0.

&> /dev/nullLeitet Standardfehler und Standardausgabe von hashso um, dass sie nicht auf dem Bildschirm angezeigt werden, und echo >&2schreibt die Nachricht in Standardfehler.

Dcharles
quelle
8
Warum nicht einfach if hash foo &> /dev/null; then ...?
Beni Cherniavsky-Paskin
9

Ich habe nie die vorherigen Antworten erhalten, um an der Box zu arbeiten, auf die ich Zugriff habe. Zum einen typewurde installiert (tun was moretut). Die eingebaute Direktive wird also benötigt. Dieser Befehl funktioniert bei mir:

if [ `builtin type -p vim` ]; then echo "TRUE"; else echo "FALSE"; fi
Magnus
quelle
3
Die Klammern sind nicht Teil der ifSyntax, verwenden Sie einfach if builtin type -p vim; then .... Und die Backticks sind wirklich alte und veraltete Syntax, $()wird sogar von shallen modernen Systemen unterstützt.
Nyuszika7h
9

Suchen Sie nach mehreren Abhängigkeiten und informieren Sie den Endbenutzer über den Status

for cmd in latex pandoc; do
  printf '%-10s' "$cmd"
  if hash "$cmd" 2>/dev/null; then
    echo OK
  else
    echo missing
  fi
done

Beispielausgabe:

latex     OK
pandoc    missing

Stellen Sie die 10auf die maximale Befehlslänge ein. Es ist nicht automatisch, da ich keine nicht ausführliche POSIX-Methode dafür sehe: Wie kann ich die Spalten einer durch Leerzeichen getrennten Tabelle in Bash ausrichten?

Überprüfen Sie, ob einige aptPakete mit dpkg -sinstalliert sind, und installieren Sie sie anderweitig .

Siehe: Überprüfen Sie, ob ein apt-get-Paket installiert ist, und installieren Sie es, wenn es nicht unter Linux ausgeführt wird

Es wurde bereits erwähnt unter: Wie kann ich anhand eines Bash-Skripts überprüfen, ob ein Programm vorhanden ist?

Ciro Santilli 冠状 病毒 审查 六四 事件 法轮功
quelle
1
Non-verbose-Methode: 1) Entfernen Sie den Breitenbezeichner. 2) Fügen Sie nach printf Ihres Befehlsnamens ein Leerzeichen ein. 3) column -tleiten Sie Ihre for-Schleife an (Teil von util-linux).
Patrice Levesque
8

Wenn Sie prüfen, ob ein Programm vorhanden ist, werden Sie es wahrscheinlich trotzdem später ausführen. Warum nicht gleich versuchen, es auszuführen?

if foo --version >/dev/null 2>&1; then
    echo Found
else
    echo Not found
fi

Es ist eine vertrauenswürdigere Überprüfung, ob das Programm ausgeführt wird, als nur PATH-Verzeichnisse und Dateiberechtigungen zu betrachten.

Außerdem können Sie mit Ihrem Programm einige nützliche Ergebnisse erzielen, z. B. die Version.

Die Nachteile sind natürlich, dass einige Programme schwer zu starten sein können und andere keine --versionOption zum sofortigen (und erfolgreichen) Beenden haben.

0xF
quelle
6

hash foo 2>/dev/null: funktioniert mit Z Shell (Zsh), Bash, Dash und Ash .

type -p foo: Es scheint mit Z-Shell, Bash und Ash ( BusyBox ) zu funktionieren , aber nicht mit Dash (es wird -pals Argument interpretiert ).

command -v foo: Funktioniert mit Z-Shell, Bash, Dash, aber nicht mit Ash (BusyBox) ( -ash: command: not found).

Beachten Sie auch, dass builtindies mit Asche und Dash nicht möglich ist.

blau gefärbt
quelle
4

Verwenden Sie Bash Builtins, wenn Sie können:

which programname

...

type -P programname
Peter Mortensen
quelle
15
Huh? whichist kein Bash eingebaut.
Tripleee
Typ -P Programmname ist zu bevorzugen, siehe akzeptierte Antwort
RobertG
@ RobertG Alles was ich sehe ist, dass -Pdas nicht POSIX ist. Warum wird type -Pbevorzugt?
Mikemaccana
Ich hätte sagen sollen, dass "in Bash-Umgebungen bevorzugt werden soll" - da ich beabsichtigte, den Bash-spezifischen vorherigen Kommentar zu beantworten. Wie auch immer, das war vor Jahren - ich denke, ich sollte Sie noch einmal auf die Antwort verweisen, die als "erledigt" markiert ist
RobertG
4

Der Befehl -vfunktioniert einwandfrei, wenn die Option POSIX_BUILTINS für festgelegt ist<command> Test kann er fehlschlagen. (Es hat jahrelang bei mir funktioniert, aber ich bin kürzlich auf eines gestoßen, bei dem es nicht funktioniert hat.)

Ich finde Folgendes ausfallsicherer:

test -x $(which <command>)

Da es auf drei Dinge testet: Pfad, Existenz und Ausführungsberechtigung.

AnthonyC
quelle
Funktioniert nicht test -x $(which ls)Gibt 0 zurück, test -x $(which sudo)obwohl dies lsinstalliert und ausführbar ist und sudonicht einmal in dem Docker-Container installiert ist, in dem ich ausgeführt werde.
Alge
@algal Sie müssen Anführungszeichen verwenden, denke ich, alsotest -x "$(which <command>)"
JoniVR
@algal Vielleicht lsist Alias? Ich glaube nicht, dass es funktionieren würde, wenn der Befehl Parameter hat.
AnthonyC
3

Für Interessenten funktioniert keine der Methoden in den vorherigen Antworten, wenn Sie eine installierte Bibliothek erkennen möchten. Ich kann mir vorstellen, dass Sie entweder den Pfad physisch überprüfen müssen (möglicherweise auf Header-Dateien und dergleichen) oder so etwas (wenn Sie sich auf einer Debian-basierten Distribution befinden):

dpkg --status libdb-dev | grep -q not-installed

if [ $? -eq 0 ]; then
    apt-get install libdb-dev
fi

Wie Sie oben sehen können, bedeutet eine "0" -Antwort aus der Abfrage, dass das Paket nicht installiert ist. Dies ist eine Funktion von "grep" - eine "0" bedeutet, dass eine Übereinstimmung gefunden wurde, eine "1" bedeutet, dass keine Übereinstimmung gefunden wurde.

Nathan Crause
quelle
10
Das Anti-Pattern cmd; if [ $? -eq 0 ]; thensollte jedoch if cmd; then
überarbeitet werden
Dies funktioniert nur für Bibliotheken, die über dpkgoderapt
Weijun Zhou
3

Hier gibt es eine Menge Optionen, aber ich war überrascht, dass es keine schnellen Einzeiler gab. Folgendes habe ich zu Beginn meiner Skripte verwendet:

[[ "$(command -v mvn)" ]] || { echo "mvn is not installed" 1>&2 ; exit 1; }
[[ "$(command -v java)" ]] || { echo "java is not installed" 1>&2 ; exit 1; }

Dies basiert auf der hier ausgewählten Antwort und einer anderen Quelle.

Keisar
quelle
2

Ich würde sagen, es gibt keinen tragbaren und 100% zuverlässigen Weg, weil es baumelt alias. Zum Beispiel:

alias john='ls --color'
alias paul='george -F'
alias george='ls -h'
alias ringo=/

Natürlich ist nur der letzte problematisch (keine Beleidigung für Ringo!). Aber alle von ihnen sind aliasaus der Sicht von gültig command -v.

Um baumelnde wie abzulehnen ringo, müssen wir die Ausgabe des eingebauten Shell- aliasBefehls analysieren und in sie zurückgreifen ( command -vist hier nicht überlegen alias). Es gibt keine tragbare Lösung dafür und sogar eine Bash- spezifische Lösung ist ziemlich langwierig.

Beachten Sie, dass eine solche Lösung bedingungslos ablehnt alias ls='ls -F':

test() { command -v $1 | grep -qv alias }
Nodakai
quelle
Guter Punkt. Wenn Sie jedoch in einem Bash-Skript ausgeführt werden, sind Aliase nicht sichtbar.
Basil Musa
1
Es gibt auch ein Problem, es wird false zurückgegeben, wenn der Befehl 'alias' aktiviert ist. Wann sollte es true zurückgeben. Beispiel: Test "Alias"
Basil Musa
2
Ich habe gerade shopt -u expand_aliasesdiese Aliase getestet und mit ignoriert / versteckt und shopt -s expand_aliaseszeige sie über command -v.
Dragon788
2

Dies zeigt je nach Speicherort an, ob das Programm vorhanden ist oder nicht:

    if [ -x /usr/bin/yum ]; then
        echo "This is Centos"
    fi
Klevin Kona
quelle
Ja, ich habe diesen Befehl hinzugefügt, wenn Sie ein Paket im Server, Open Suse, Centos, Debian
Klevin Kona
Die Syntaxhervorhebung ist in der Zeile "Echo" deaktiviert. Was ist die Lösung? Schlägt es vor, dass das Bash-Skript anders sein sollte?
Peter Mortensen
@PeterMortensen Die Syntaxhervorhebung ist deaktiviert, da sie keine Zeichenfolge erkennt.
Adrien
1

Der whichBefehl könnte nützlich sein. Mann welcher

Es gibt 0 zurück, wenn die ausführbare Datei gefunden wird, und 1, wenn sie nicht gefunden wird oder nicht ausführbar ist:

NAME

       which - locate a command

SYNOPSIS

       which [-a] filename ...

DESCRIPTION

       which returns the pathnames of the files which would
       be executed in the current environment, had its
       arguments been given as commands in a strictly
       POSIX-conformant shell. It does this by searching
       the PATH for executable files matching the names
       of the arguments.

OPTIONS

       -a     print all matching pathnames of each argument

EXIT STATUS

       0      if all specified commands are 
              found and executable

       1      if one or more specified commands is nonexistent
              or not executable

       2      if an invalid option is specified

Das Schöne daran whichist, dass es herausfindet, ob die ausführbare Datei in der Umgebung verfügbar ist, in der sie ausgeführt whichwird - es erspart ein paar Probleme ...

Adam Davis
quelle
Verwenden Sie diese Option, wenn Sie nach einer ausführbaren Datei mit dem Namen foo suchen, aber meine Antwort lesen, wenn Sie eine bestimmte Datei / path / to / a / named / foo überprüfen möchten. Beachten Sie auch, dass dies auf einigen Minimalsystemen möglicherweise nicht verfügbar ist, obwohl es auf jeder vollwertigen Installation vorhanden sein sollte ...
dmckee --- Ex-Moderator-Kätzchen
9
Verlassen Sie sich nicht auf den Exit-Status. Viele Betriebssysteme haben eine, die nicht einmal einen anderen Exit-Status als 0 setzt.
lhunath
1

Mein Setup für einen Debian- Server:

Ich hatte das Problem, wenn mehrere Pakete den gleichen Namen enthielten.

Zum Beispiel apache2. Das war also meine Lösung:

function _apt_install() {
    apt-get install -y $1 > /dev/null
}

function _apt_install_norecommends() {
    apt-get install -y --no-install-recommends $1 > /dev/null
}
function _apt_available() {
    if [ `apt-cache search $1 | grep -o "$1" | uniq | wc -l` = "1" ]; then
        echo "Package is available : $1"
        PACKAGE_INSTALL="1"
    else
        echo "Package $1 is NOT available for install"
        echo  "We can not continue without this package..."
        echo  "Exitting now.."
        exit 0
    fi
}
function _package_install {
    _apt_available $1
    if [ "${PACKAGE_INSTALL}" = "1" ]; then
        if [ "$(dpkg-query -l $1 | tail -n1 | cut -c1-2)" = "ii" ]; then
             echo  "package is already_installed: $1"
        else
            echo  "installing package : $1, please wait.."
            _apt_install $1
            sleep 0.5
        fi
    fi
}

function _package_install_no_recommends {
    _apt_available $1
    if [ "${PACKAGE_INSTALL}" = "1" ]; then
        if [ "$(dpkg-query -l $1 | tail -n1 | cut -c1-2)" = "ii" ]; then
             echo  "package is already_installed: $1"
        else
            echo  "installing package : $1, please wait.."
            _apt_install_norecommends $1
            sleep 0.5
        fi
    fi
}
ThCTLo
quelle
1

Wenn Sie Jungs / Mädels die Antworten hier nicht zum Laufen bringen können und sich die Haare aus dem Rücken ziehen, versuchen Sie, denselben Befehl mit auszuführen bash -c. Schauen Sie sich dieses somnambuläre Delirium an. Dies ist, was wirklich passiert, wenn Sie $ (Unterbefehl) ausführen:

Zuerst. Es kann Ihnen eine völlig andere Ausgabe geben.

$ command -v ls
alias ls='ls --color=auto'
$ bash -c "command -v ls"
/bin/ls

Zweite. Es kann Ihnen überhaupt keine Ausgabe geben.

$ command -v nvm
nvm
$ bash -c "command -v nvm"
$ bash -c "nvm --help"
bash: nvm: command not found
user619271
quelle
Die Unterschiede werden durch den Unterschied zwischen dem interaktiven und dem nicht interaktiven Modus der Shell verursacht. Ihr ~ / .bashrc ist schreibgeschützt, wenn die Shell nicht angemeldet und interaktiv ist. Die zweite sieht zwar seltsam aus, da dies durch einen Unterschied in der Umgebungsvariablen PATH verursacht werden muss, aber Subshells die Umgebung erben.
Palec
In meinem Fall habe .bashrcich ein [ -z "$PS1" ] && returnvorangestelltes, # If not running interactively, don't do anythingalso denke ich, das ist ein Grund, warum selbst die explizite Beschaffung von bashrc im nicht interaktiven Modus nicht hilft. Das Problem kann durch den Aufruf eines Skripts mit einem workarounded werden ss64.com/bash/source.html Punkt - Operator , . ./script.shaber das ist nicht eine Sache erinnern möchte jedes Mal eingeben.
user619271
1
Sourcing-Skripte, die nicht bezogen werden sollen, sind eine schlechte Idee. Ich wollte nur sagen, dass Ihre Antwort wenig mit der gestellten Frage zu tun hat und viel mit Bash und seinem (nicht) interaktiven Modus.
Palec
Wenn erklärt würde, was in diesen Fällen vor sich geht, wäre dies eine hilfreiche Ergänzung zu einer Antwort.
Palec
0

Die Hash-Variante hat eine Gefahr: In der Kommandozeile können Sie beispielsweise eingeben

one_folder/process

Prozess ausführen lassen. Dazu muss sich der übergeordnete Ordner von one_folder in $ PATH befinden . Wenn Sie jedoch versuchen, diesen Befehl zu hashen, ist er immer erfolgreich:

hash one_folder/process; echo $? # will always output '0'
anycast.cw
quelle
4
"Dazu muss sich der übergeordnete Ordner von one_folder befinden $PATH" - Dies ist völlig ungenau. Versuch es. Damit dies funktioniert, muss sich one_folder im aktuellen Verzeichnis befinden .
Wildcard
0

Ich unterstütze die Verwendung von "Befehl -v". ZB so:

md=$(command -v mkdirhier) ; alias md=${md:=mkdir}  # bash

emacs="$(command -v emacs) -nw" || emacs=nano
alias e=$emacs
[[ -z $(command -v jed) ]] && alias jed=$emacs
user2961933
quelle
0

Ich musste überprüfen, ob Git im Rahmen der Bereitstellung unseres CI- Servers installiert wurde . Mein letztes Bash-Skript war wie folgt (Ubuntu-Server):

if ! builtin type -p git &>/dev/null; then
  sudo apt-get -y install git-core
fi
Greg K.
quelle
3
Die Bedingung ist ziemlich nutzlos, modulo die Startzeit, um apt-get auszuführen, da apt-get erfüllt ist und beendet wird, wenn git-core bereits installiert ist.
Tripleee
3
Die Startzeit ist nicht zu vernachlässigen, aber die wichtigere Motivation ist sudo: Ohne die Bedingung würde es immer anhalten und nach dem Passwort fragen (es sei denn, Sie haben kürzlich einen Sudo gemacht). Übrigens kann es nützlich sein, sudo -p "Type your password to install missing git-core: "die Eingabeaufforderung nicht aus heiterem Himmel zu erhalten.
Beni Cherniavsky-Paskin
0

Um Bashs nachzuahmen type -P cmd, können wir POSIX-konform verwenden env -i type cmd 1>/dev/null 2>&1.

man env
# "The option '-i' causes env to completely ignore the environment it inherits."
# In other words, there are no aliases or functions to be looked up by the type command.

ls() { echo 'Hello, world!'; }

ls
type ls
env -i type ls

cmd=ls
cmd=lsx
env -i type $cmd 1>/dev/null 2>&1 || { echo "$cmd not found"; exit 1; }
tim
quelle
7
Warum wird das positiv bewertet? Auf welchen Systemen funktioniert das eigentlich für Sie? typescheint zu sein , builtinin den meisten Schalen so nicht funktionieren kann , weil envAnwendungen execvplaufen commandso commandkann ein nicht sein builtin(und die builtinin der gleichen Umgebung wird immer ausgeführt werden). Dies nicht für mich in bash, ksh93, zsh, busybox [a]shund dashalle liefern typeals Shell - builtin.
Adrian Frühwirth
0

Wenn kein externer typeBefehl verfügbar ist (wie hier als selbstverständlich vorausgesetzt ), können wir POSIX-konform verwenden env -i sh -c 'type cmd 1>/dev/null 2>&1':

# Portable version of Bash's type -P cmd (without output on stdout)
typep() {
   command -p env -i PATH="$PATH" sh -c '
      export LC_ALL=C LANG=C
      cmd="$1"
      cmd="`type "$cmd" 2>/dev/null || { echo "error: command $cmd not found; exiting ..." 1>&2; exit 1; }`"
      [ $? != 0 ] && exit 1
      case "$cmd" in
        *\ /*) exit 0;;
            *) printf "%s\n" "error: $cmd" 1>&2; exit 1;;
      esac
   ' _ "$1" || exit 1
}

# Get your standard $PATH value
#PATH="$(command -p getconf PATH)"
typep ls
typep builtin
typep ls-temp

Zumindest unter Mac OS X 10.6.8 (Snow Leopard) stimmt die Verwendung von Bash 4.2.24 (2) command -v lsnicht mit einem Verschieben überein /bin/ls-temp.

Freno
quelle
0

Im Fall , dass Sie prüfen , ob ein Programm existiert und ist wirklich ein Programm, kein Bash eingebauten Befehl , dann command, typeundhash ist nicht für die Prüfung , wie sie für integrierte Befehle all return 0 Exit - Status zu eigen.

Zum Beispiel gibt es das Zeitprogramm , das bietet mehr die Eigenschaften als Zeit integrierten Befehl. Um zu überprüfen, ob das Programm vorhanden ist, würde ich die Verwendung wie im folgenden Beispiel vorschlagen :which

# First check if the time program exists
timeProg=`which time`
if [ "$timeProg" = "" ]
then
  echo "The time program does not exist on this system."
  exit 1
fi

# Invoke the time program
$timeProg --quiet -o result.txt -f "%S %U + p" du -sk ~
echo "Total CPU time: `dc -f result.txt` seconds"
rm result.txt
rpr
quelle
0

Ich wollte die gleiche Frage beantworten, aber innerhalb eines Makefiles laufen.

install:
    @if [[ ! -x "$(shell command -v ghead)" ]]; then \
        echo 'ghead does not exist. Please install it.'; \
        exit -1; \
    fi
Richard A Quadling
quelle
-1

Skript

#!/bin/bash

# Commands found in the hash table are checked for existence before being
# executed and non-existence forces a normal PATH search.
shopt -s checkhash

function exists() {
 local mycomm=$1; shift || return 1

 hash $mycomm 2>/dev/null || \
 printf "\xe2\x9c\x98 [ABRT]: $mycomm: command does not exist\n"; return 1;
}
readonly -f exists

exists notacmd
exists bash
hash
bash -c 'printf "Fin.\n"'

Ergebnis

 [ABRT]: notacmd: command does not exist
hits    command
   0    /usr/bin/bash
Fin.
ecwpz91
quelle
-1

Ich benutze das, weil es sehr einfach ist:

if [ `LANG=C type example 2>/dev/null|wc -l` = 1 ];then echo exists;else echo "not exists";fi

oder

if [ `LANG=C type example 2>/dev/null|wc -l` = 1 ];then
echo exists
else echo "not exists"
fi

Es verwendet den eingebauten Shell-Status und den Echo-Status der Programme für die Standardausgabe und nichts für den Standardfehler. Wenn andererseits ein Befehl nicht gefunden wird, wird der Status nur als Standardfehler angezeigt.

EIN
quelle