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 .
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}
@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.
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 $PATHund ausführbar ist:
[-x "$(command -v foo)"]
Beispiel:
if![-x "$(command -v git)"];then
echo 'Error: git is not installed.'>&2
exit 1fi
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.
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;}
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
&>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.
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
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.
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.
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.
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
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
fidone
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 Foundelse
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.
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.
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.
@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):
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.
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.
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':
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 ...
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.
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.
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'
"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
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
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;}
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.
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 1fi# 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
#!/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 ||return1
hash $mycomm 2>/dev/null || \
printf "\xe2\x9c\x98 [ABRT]: $mycomm: command does not exist\n";return1;}
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.
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.
which
gibt für diese true zurück.type
ohne 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 .Antworten:
Antworten
POSIX-kompatibel:
Für Bash-spezifische Umgebungen:
Erläuterung
Vermeiden Sie
which
. Es ist nicht nur ein externer Prozess, den Sie starten, um sehr wenig zu tun (was bedeutethash
, dass integrierte Funktionen viel billiger sindtype
odercommand
viel 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?
which
, der nicht einmal einen Exit-Status festlegt , was bedeutet,if which foo
dass er dort nicht einmal funktioniert und immer meldet, dass erfoo
existiert, auch wenn dies nicht der Fall ist (beachten Sie, dass einige POSIX-Shells dies anscheinend auch tunhash
).which
benutzerdefinierte 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:(Kleinere Randnotiz: Einige schlagen vor, dass
2>&-
es dasselbe ist,2>/dev/null
aber 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.type
undhash
‚s Exit - Codes sind nicht sehr gut von POSIX definiert, undhash
werden erfolgreich gesehen zu verlassen , wenn der Befehl nicht vorhanden ist (habe dies nicht gesehentype
noch) nicht .command
Der 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 beidestype
undhash
sind absolut sicher zu verwenden.type
Jetzt muss-P
nur noch dasPATH
und 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,
gdate
wenn sie existiert, andernfallsdate
:quelle
2>&-
("Close Output File Descriptor 2", was stderr ist) hat das gleiche Ergebnis wie2> /dev/null
; 2)>&2
ist eine Verknüpfung für1>&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 .while read element ; do .. done <<< $(echo ${ArrayVar[*]})
,for word in $(fgrep -l $ORIGINAL *.txt)
,ls -l "$directory" | sed 1d
, {{für einen inseq $BEGIN $END
}}, ... Viele haben versucht , die Autoren zu kontaktieren und Verbesserungen vorzuschlagen , aber es ist kein Wiki und Anfragen haben auf taube Ohren gelandet.2>&-
ist nicht dasselbe wie2>/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.Das Folgende ist eine tragbare Methode, um zu überprüfen, ob ein Befehl in vorhanden
$PATH
und ausführbar ist:Beispiel:
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.
quelle
command -v
ein Pfad auch für eine nicht ausführbare Datei erstellt? Das heißt, das -x wirklich notwendig?-x
testet, ob die Datei ausführbar ist, was die Frage war.command
es selbst testen wird, ob es ausführbar ist - nicht wahr ?$PATH
beim Ausführen eines Befehls nicht ausführbare Dateien . Das Verhalten voncommand -v
ist 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.dash
ist 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=874264Ich 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 -v
sollte stattdessen Folgendes verwendet werden:Der Befehl
command
ist POSIX-kompatibel. Die Spezifikation finden Sie hier: Befehl - Führen Sie einen einfachen Befehl ausHinweis:
type
Ist POSIX-kompatibel, jedochtype -P
nicht.quelle
exit 1;
tötet ein xterm, wenn es von dort aus aufgerufen wird.&>/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
.In meiner .bashrc ist eine Funktion definiert, die dies erleichtert.
Hier ist ein Beispiel, wie es verwendet wird (von meinem
.bash_profile
.)quelle
&>
?&>
leitet sowohl stdout als auch stderr zusammen um.&>
ist möglicherweise in Ihrer Version von Bash nicht verfügbar. Marcellos Code sollte gut funktionieren. es macht das gleiche.then
. Sehen Sie diese Antwort , wenn Sie die ausführbare Datei benötigen in existieren$PATH
.Dies hängt davon ab, ob Sie wissen möchten, ob es in einem der Verzeichnisse der
$PATH
Variablen vorhanden ist oder ob Sie den absoluten Speicherort kennen. Wenn Sie wissen möchten, ob es sich um eine$PATH
Variable handelt, verwenden Sieandernfalls verwenden
Die Umleitung zu
/dev/null/
im ersten Beispiel unterdrückt die Ausgabe deswhich
Programms.quelle
Um die Antworten von @ lhunath und @ GregV zu erweitern, hier der Code für die Personen, die diesen Scheck einfach in eine
if
Erklärung einfügen möchten :So verwenden Sie es:
quelle
command
auch 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.shopt -u expand_aliases
Aliase zum Ignorieren / Ausblenden getestet und verwendet (wiealias ls='ls -F'
in einer anderen Antwort erwähnt) undshopt -s expand_aliases
sie über aufgelöstcommand -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.Versuchen Sie es mit:
oder
Aus der Bash-Manpage unter Bedingte Ausdrücke :
quelle
So verwenden Sie
hash
, wie @lhunath vorschlägt , in einem Bash-Skript:Dieses Skript wird ausgeführt
hash
und prüft dann, ob der Exit-Code des letzten Befehls, der darin gespeicherte Wert$?
, gleich ist1
. Wennhash
nicht gefunden wirdfoo
, lautet der Exit-Code1
. Wennfoo
vorhanden, lautet der Exit-Code0
.&> /dev/null
Leitet Standardfehler und Standardausgabe vonhash
so um, dass sie nicht auf dem Bildschirm angezeigt werden, undecho >&2
schreibt die Nachricht in Standardfehler.quelle
if hash foo &> /dev/null; then ...
?Ich habe nie die vorherigen Antworten erhalten, um an der Box zu arbeiten, auf die ich Zugriff habe. Zum einen
type
wurde installiert (tun wasmore
tut). Die eingebaute Direktive wird also benötigt. Dieser Befehl funktioniert bei mir:quelle
if
Syntax, verwenden Sie einfachif builtin type -p vim; then ...
. Und die Backticks sind wirklich alte und veraltete Syntax,$()
wird sogar vonsh
allen modernen Systemen unterstützt.Suchen Sie nach mehreren Abhängigkeiten und informieren Sie den Endbenutzer über den Status
Beispielausgabe:
Stellen Sie die
10
auf 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
apt
Pakete mitdpkg -s
installiert 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?
quelle
column -t
leiten Sie Ihre for-Schleife an (Teil von util-linux).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?
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
--version
Option zum sofortigen (und erfolgreichen) Beenden haben.quelle
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-p
als Argument interpretiert ).command -v foo
: Funktioniert mit Z-Shell, Bash, Dash, aber nicht mit Ash (BusyBox) (-ash: command: not found
).Beachten Sie auch, dass
builtin
dies mit Asche und Dash nicht möglich ist.quelle
Verwenden Sie Bash Builtins, wenn Sie können:
...
quelle
which
ist kein Bash eingebaut.-P
das nicht POSIX ist. Warum wirdtype -P
bevorzugt?Der Befehl
-v
funktioniert 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:
Da es auf drei Dinge testet: Pfad, Existenz und Ausführungsberechtigung.
quelle
test -x $(which ls)
Gibt 0 zurück,test -x $(which sudo)
obwohl diesls
installiert und ausführbar ist undsudo
nicht einmal in dem Docker-Container installiert ist, in dem ich ausgeführt werde.test -x "$(which <command>)"
ls
ist Alias? Ich glaube nicht, dass es funktionieren würde, wenn der Befehl Parameter hat.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):
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.
quelle
cmd; if [ $? -eq 0 ]; then
sollte jedochif cmd; then
dpkg
oderapt
Hier gibt es eine Menge Optionen, aber ich war überrascht, dass es keine schnellen Einzeiler gab. Folgendes habe ich zu Beginn meiner Skripte verwendet:
Dies basiert auf der hier ausgewählten Antwort und einer anderen Quelle.
quelle
Ich würde sagen, es gibt keinen tragbaren und 100% zuverlässigen Weg, weil es baumelt
alias
. Zum Beispiel:Natürlich ist nur der letzte problematisch (keine Beleidigung für Ringo!). Aber alle von ihnen sind
alias
aus der Sicht von gültigcommand -v
.Um baumelnde wie abzulehnen
ringo
, müssen wir die Ausgabe des eingebauten Shell-alias
Befehls analysieren und in sie zurückgreifen (command -v
ist hier nicht überlegenalias
). 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'
:quelle
shopt -u expand_aliases
diese Aliase getestet und mit ignoriert / versteckt undshopt -s expand_aliases
zeige sie übercommand -v
.Dies zeigt je nach Speicherort an, ob das Programm vorhanden ist oder nicht:
quelle
Der
which
Befehl könnte nützlich sein. Mann welcherEs gibt 0 zurück, wenn die ausführbare Datei gefunden wird, und 1, wenn sie nicht gefunden wird oder nicht ausführbar ist:
Das Schöne daran
which
ist, dass es herausfindet, ob die ausführbare Datei in der Umgebung verfügbar ist, in der sie ausgeführtwhich
wird - es erspart ein paar Probleme ...quelle
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:quelle
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.
Zweite. Es kann Ihnen überhaupt keine Ausgabe geben.
quelle
.bashrc
ich ein[ -z "$PS1" ] && return
vorangestelltes,# If not running interactively, don't do anything
also 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.sh
aber das ist nicht eine Sache erinnern möchte jedes Mal eingeben.Die Hash-Variante hat eine Gefahr: In der Kommandozeile können Sie beispielsweise eingeben
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:
quelle
$PATH
" - Dies ist völlig ungenau. Versuch es. Damit dies funktioniert, muss sich one_folder im aktuellen Verzeichnis befinden .Ich unterstütze die Verwendung von "Befehl -v". ZB so:
quelle
Ich musste überprüfen, ob Git im Rahmen der Bereitstellung unseres CI- Servers installiert wurde . Mein letztes Bash-Skript war wie folgt (Ubuntu-Server):
quelle
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.Um Bashs nachzuahmen
type -P cmd
, können wir POSIX-konform verwendenenv -i type cmd 1>/dev/null 2>&1
.quelle
type
scheint zu sein ,builtin
in den meisten Schalen so nicht funktionieren kann , weilenv
Anwendungenexecvp
laufencommand
socommand
kann ein nicht seinbuiltin
(und diebuiltin
in der gleichen Umgebung wird immer ausgeführt werden). Dies nicht für mich inbash
,ksh93
,zsh
,busybox [a]sh
unddash
alle lieferntype
als Shell - builtin.Wenn kein externer
type
Befehl verfügbar ist (wie hier als selbstverständlich vorausgesetzt ), können wir POSIX-konform verwendenenv -i sh -c 'type cmd 1>/dev/null 2>&1'
:Zumindest unter Mac OS X 10.6.8 (Snow Leopard) stimmt die Verwendung von Bash 4.2.24 (2)
command -v ls
nicht mit einem Verschieben überein/bin/ls-temp
.quelle
Im Fall , dass Sie prüfen , ob ein Programm existiert und ist wirklich ein Programm, kein Bash eingebauten Befehl , dann
command
,type
undhash
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
quelle
Ich wollte die gleiche Frage beantworten, aber innerhalb eines Makefiles laufen.
quelle
Skript
Ergebnis
quelle
Ich benutze das, weil es sehr einfach ist:
oder
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.
quelle