Hier ist alles, was Sie nie gedacht hätten, dass Sie jemals davon nichts wissen wollen:
Zusammenfassung
So rufen Sie den Pfadnamen einer ausführbaren Datei in einem Bourne-ähnlichen Shell-Skript ab (es gibt einige Vorsichtsmaßnahmen; siehe unten):
ls=$(command -v ls)
So stellen Sie fest, ob ein bestimmter Befehl vorhanden ist:
if command -v given-command > /dev/null 2>&1; then
echo given-command is available
else
echo given-command is not available
fi
An der Eingabeaufforderung einer interaktiven Bourne-ähnlichen Shell:
type ls
Der whichBefehl ist ein zerbrochenes Erbe der C-Shell und sollte besser in Bourne-ähnlichen Shells allein gelassen werden.
Anwendungsfälle
Es gibt einen Unterschied, ob Sie diese Informationen als Teil eines Skripts oder interaktiv an der Shell-Eingabeaufforderung suchen.
An der Shell-Eingabeaufforderung lautet der typische Anwendungsfall: Dieser Befehl verhält sich merkwürdig. Verwende ich den richtigen? Was genau ist passiert, als ich getippt habe mycmd? Kann ich mir genauer ansehen, was es ist?
In diesem Fall möchten Sie wissen, was Ihre Shell tut, wenn Sie den Befehl aufrufen, ohne ihn tatsächlich aufzurufen.
In Shell-Skripten sieht es meistens ganz anders aus. In einem Shell-Skript gibt es keinen Grund, warum Sie wissen möchten, wo oder was ein Befehl ist, wenn Sie ihn nur ausführen möchten. Im Allgemeinen möchten Sie den Pfad der ausführbaren Datei kennen, damit Sie mehr Informationen daraus abrufen können (z. B. den Pfad zu einer anderen Datei im Verhältnis dazu oder Informationen aus dem Inhalt der ausführbaren Datei unter diesem Pfad lesen).
Interaktiv, können Sie darüber wissen alle die my-cmdBefehle auf dem System, in Skripten, selten so.
Die meisten verfügbaren Tools wurden (wie so oft) für die interaktive Verwendung entwickelt.
Geschichte
Ein bisschen Geschichte zuerst.
Die frühen Unix-Shells hatten bis Ende der 70er Jahre keine Funktionen oder Aliase. Nur das traditionelle Nachschlagen von ausführbaren Dateien in $PATH. csheingeführt Aliase um 1978 (obwohl cshwurde zuerst veröffentlicht in 2BSD, der im Mai 1979), und auch die Verarbeitung einer .cshrcfür die Benutzer die Schale gestalten (jede Schale, wie cshliest .cshrcauch wenn sie nicht interaktiv wie in Skripten).
Während die Bourne-Shell 1979 zum ersten Mal in Unix V7 veröffentlicht wurde, wurde die Funktionsunterstützung erst viel später hinzugefügt (1984 in SVR2), und es gab sowieso nie eine rcDatei (die .profiledient zum Konfigurieren Ihrer Umgebung, nicht die Shell an sich ).
csh wurde viel populärer als die Bourne-Shell, da sie (obwohl sie eine schrecklich schlechtere Syntax als die Bourne-Shell hatte) viele bequemere und schönere Funktionen für den interaktiven Gebrauch hinzufügte.
In 3BSD(1980), ein whichcsh - Skript wurde für die hinzugefügten cshBenutzer eine ausführbare Datei zu identifizieren, und es ist ein kaum anderes Skript Sie finden whichauf vielen kommerziellen Unix - Varianten heute (wie Solaris, HP / UX, AIX oder Tru64).
Dieses Skript liest den Benutzer ~/.cshrc(wie alle cshSkripte, sofern sie nicht mit aufgerufen werden csh -f) und sucht die angegebenen Befehlsnamen in der Liste der Aliase und in $path(dem Array, cshdas auf der Grundlage verwaltet wird $PATH).
Los geht's, whichkam als erstes für die damals beliebteste Shell (und cshwar bis Mitte der 90er Jahre immer noch beliebt), was der Hauptgrund ist, warum sie in Büchern dokumentiert wurde und immer noch weit verbreitet ist.
Beachten Sie csh, dass dieses whichcsh-Skript auch für einen Benutzer nicht unbedingt die richtigen Informationen enthält. Es werden die in definierten Aliase abgerufen ~/.cshrc, nicht die Aliase , die Sie möglicherweise später an der Eingabeaufforderung oder beispielsweise durch die sourceEingabe einer anderen cshDatei definiert haben, und (obwohl dies keine gute Idee wäre) PATHmöglicherweise in neu definiert ~/.cshrc.
Wenn Sie diesen whichBefehl über eine Bourne-Shell ausführen, werden die in Ihrer definierten Aliase weiterhin gesucht. ~/.cshrcWenn Sie jedoch keinen haben, weil Sie ihn nicht verwenden csh, erhalten Sie wahrscheinlich immer noch die richtige Antwort.
Eine ähnliche Funktionalität wurde der Bourne-Shell erst 1984 in SVR2 mit dem typeeingebauten Befehl hinzugefügt. Die Tatsache, dass es (im Gegensatz zu einem externen Skript) eingebaut ist, bedeutet, dass es Ihnen (in gewissem Umfang) die richtigen Informationen geben kann , da es Zugriff auf die Interna der Shell hat.
Der ursprüngliche typeBefehl hatte ein ähnliches Problem wie das whichSkript, da er keinen Fehlerbeendigungsstatus zurückgab, wenn der Befehl nicht gefunden wurde. Im Gegensatz dazu wird für ausführbare Dateien nicht nur das whichausgegeben, was die Verwendung in Skripten erschwert.ls is /bin/ls/bin/ls
Die Bourne-Shell von Unix Version 8 (nicht im typeUmlauf) wurde umbenannt whatis. Und die Plan9-Shell rc( der ehemalige Nachfolger von Unix) (und ihre Derivate wie akangaund es) haben es whatisebenfalls.
Die Korn-Shell (eine Teilmenge, auf der die POSIX-sh-Definition basiert), die Mitte der 80er Jahre entwickelt wurde, aber vor 1988 nicht allgemein verfügbar war, fügte viele der cshFunktionen (Zeileneditor, Aliase ...) der Bourne-Shell hinzu . Es wurde whence(zusätzlich zu type) ein eigenes eingebautes Programm hinzugefügt , das mehrere Optionen -vzur Verfügung stellte ( um die typeähnlich ausführliche Ausgabe bereitzustellen und -pnur nach ausführbaren Dateien zu suchen (keine Aliase / Funktionen ...)).
In den späten 80er Jahren, Anfang der 90er Jahre, kamen einige freie Software- Shell-Implementierungen heraus , die mit den Turbulenzen in Bezug auf die Urheberrechtsprobleme zwischen AT & T und Berkeley einhergingen. Die gesamte Almquist-Shell (ash, die die Bourne-Shell in BSDs ersetzen soll), die gemeinfreie Implementierung von ksh (pdksh) bash(gesponsert von der FSF), zshwurde zwischen 1989 und 1991 veröffentlicht.
Obwohl Ash als Ersatz für die Bourne-Shell typegedacht war, wurde es erst viel später (in NetBSD 1.3 und FreeBSD 2.3) eingebaut hash -v. OSF / 1 /bin/shhatte eine typeeingebaute Version, die immer 0 bis OSF / 1 v3.x zurückgab. bashfügte kein hinzu whence, fügte aber eine -pOption hinzu type, um den Pfad auszudrucken ( type -pwürde lauten whence -p) und alle übereinstimmenden Befehle -azu melden . made builtin und fügte einen Befehl hinzu , der sich wie 's verhält . hat sie alle.tcshwhichwherebashtype -azsh
In der fishShell (2005) ist ein typeBefehl als Funktion implementiert.
Das whichcsh-Skript wurde in der Zwischenzeit aus NetBSD entfernt (da es in tcsh eingebaut und in anderen Shells nicht sehr nützlich ist) und die Funktionalität wurde hinzugefügt whereis(wenn es als aufgerufen wird which, whereisverhält es sich so, whichdass es nur ausführbare Dateien in sucht $PATH). In OpenBSD und FreeBSD whichwurde auch eine in C geschriebene geändert, die $PATHnur Befehle in sucht .
Implementierungen
Es gibt Dutzende von Implementierungen eines whichBefehls auf verschiedenen Unices mit unterschiedlicher Syntax und unterschiedlichem Verhalten.
Unter Linux (neben den in tcshund eingebauten zsh) gibt es mehrere Implementierungen. Auf neueren Debian-Systemen ist es beispielsweise ein einfaches POSIX-Shell-Skript, das nach Befehlen in sucht $PATH.
busyboxhat auch einen whichBefehl.
Es gibt eine, GNU whichdie wahrscheinlich die extravaganteste ist. Es versucht, das, was das whichcsh-Skript getan hat, auf andere Shells auszudehnen : Sie können sagen, was Ihre Aliase und Funktionen sind, damit Sie eine bessere Antwort erhalten (und ich glaube, dass einige Linux-Distributionen einige globale Aliase setzen, um dies bashzu tun). .
zshEs gibt einige Operatoren , die auf den Pfad der ausführbaren Dateien erweitert werden müssen: den = Dateinamenerweiterungsoperator und den :cÄnderungswert für die Verlaufserweiterung (hier angewendet auf die Parametererweiterung ):
$ print -r -- =ls
/bin/ls
$ cmd=ls; print -r -- $cmd:c
/bin/ls
zshzsh/parametersmacht im Modul auch die Kommando-Hash-Tabelle zum commandsassoziativen Array:
$ print -r -- $commands[ls]
/bin/ls
Das whatisHilfsprogramm (mit Ausnahme des Hilfsprogramms in der Unix V8 Bourne-Shell oder Plan 9 rc/ es) ist nicht wirklich verwandt, da es nur zur Dokumentation dient (greift auf die whatis-Datenbank zu, dh auf die Manpage-Übersicht).
whereisWurde auch 3BSDzur gleichen Zeit hinzugefügt, als whichwäre es in geschrieben C, nicht cshund wird verwendet, um zur gleichen Zeit die ausführbare Datei, die Manpage und den Quellcode zu suchen, aber nicht basierend auf der aktuellen Umgebung. Das entspricht einem anderen Bedürfnis.
An der Standardfront gibt POSIX nun die Befehle command -vund an -V(die bis POSIX.2008 optional waren). UNIX gibt den typeBefehl an (keine Option). Das ist alles ( where, which, whencesind in keiner Norm festgelegt)
Bis zu einer bestimmten Version typeund command -voptional in der Linux Standard Base-Spezifikation, was erklärt, warum zum Beispiel einige alte Versionen von posh(obwohl auf pdkshdenen beide basierten ) keine hatten. command -vwurde auch zu einigen Bourne-Shell-Implementierungen hinzugefügt (wie unter Solaris).
Status Heute
Heutzutage ist dies der Status typeund er command -vist in allen Bourne-ähnlichen Shells allgegenwärtig (beachten Sie jedoch, wie von @jarno bemerkt, die Einschränkung / den Fehler, bashwenn Sie sich nicht im POSIX-Modus befinden, oder einige Nachkommen der Almquist-Shell, die in den Kommentaren unten aufgeführt sind). tcshist die einzige Shell, die Sie verwenden möchten which(da keine typevorhanden und whicheingebaut ist).
In den Schalen ausgenommen tcshund zsh, whichman kann sagen , den Pfad der gegebenen ausführbaren solange es keine Alias oder eine Funktion von demselben Namen in einem unserer ~/.cshrc, ~/.bashrcoder jede Shell - Startdatei und Sie definieren nicht $PATHin Ihrem ~/.cshrc. Wenn Sie einen Alias oder eine Funktion dafür definiert haben, kann er oder sie Ihnen davon erzählen, oder sie können Ihnen das Falsche sagen.
Wenn Sie über alle Befehle unter einem bestimmten Namen informiert werden möchten, gibt es nichts Tragbares. Sie würden wherein tcshoder zsh, type -ain bashoder zsh, whence -ain ksh93 und in anderen Shells verwenden, die Sie typein Kombination mit which -adenen verwenden können, die möglicherweise funktionieren.
Empfehlungen
Abrufen des Pfadnamens zu einer ausführbaren Datei
Um den Pfadnamen einer ausführbaren Datei in einem Skript zu ermitteln, gibt es einige Vorsichtsmaßnahmen:
ls=$(command -v ls)
wäre der Standard Weg, um es zu tun.
Es gibt jedoch einige Probleme:
- Es ist nicht möglich, den Pfad der ausführbaren Datei zu kennen, ohne sie auszuführen. Alle
type, which, command -v... alle Heuristik verwendet den Weg , um herauszufinden. Sie durchlaufen die $PATHKomponenten und suchen die erste Nicht-Verzeichnis-Datei, für die Sie die Berechtigung zum Ausführen haben. Abhängig von der Shell führen viele von ihnen (Bourne, AT & T ksh, zsh, ash ...) die Ausführung in der Reihenfolge aus, $PATHbis der execveSystemaufruf nicht mehr mit einem Fehler zurückkehrt . Zum Beispiel, wenn $PATHenthält /foo:/barund Sie ausführen möchten ls, werden sie zuerst versuchen, auszuführen, /foo/lsoder wenn dies fehlschlägt /bar/ls. Jetzt Ausführung von/foo/lskann fehlschlagen, weil Sie keine Ausführungsberechtigung haben, aber auch aus vielen anderen Gründen, zum Beispiel, weil es keine gültige ausführbare Datei ist. command -v lswürde melden, /foo/lsob Sie über die Ausführungsberechtigung für verfügen /foo/ls, aber lsmöglicherweise tatsächlich ausgeführt werden, /bar/lswenn /foo/lskeine gültige ausführbare Datei vorhanden ist.
- Wenn
fooes sich um eine integrierte Funktion oder einen Alias handelt, wird command -v foozurückgegeben foo. Mit einigen Muscheln mögen ash, pdkshoder zshkann es auch zurückkehren , foowenn $PATHdie leere Zeichenfolge enthält und es gibt eine ausführbare fooDatei im aktuellen Verzeichnis. Unter bestimmten Umständen müssen Sie dies möglicherweise berücksichtigen. Denken Sie zum Beispiel daran, dass die Liste der integrierten Funktionen von der Shell-Implementierung abhängt (zum Beispiel mountwird sie manchmal für busybox integriert sh) und bashFunktionen aus der Umgebung abrufen kann.
- Wenn
$PATHrelative Pfadkomponenten enthalten sind (normalerweise .oder die leere Zeichenfolge, die sich beide auf das aktuelle Verzeichnis beziehen, aber alles command -v cmdMögliche sein können), wird je nach Shell möglicherweise kein absoluter Pfad ausgegeben. Daher ist der Pfad, den Sie zum Zeitpunkt Ihres Laufs erhalten command -v, nicht mehr gültig, nachdem Sie sich an einem cdanderen Ort befinden.
- Anekdotische: mit der ksh93 Schale, wenn
/opt/ast/bin(obwohl das genaue Pfad auf verschiedenen Systemen unterschiedlich sein kann glaube ich) in dir ist $PATH, wird ksh93 zur Verfügung , ein paar zusätzliche builtins machen ( chmod, cmp, cat...), aber command -v chmodwird zurückkehren , /opt/ast/bin/chmodauch wenn dieser Weg doesn‘ existiert nicht.
Bestimmen, ob ein Befehl vorhanden ist
Um herauszufinden, ob ein bestimmter Befehl standardmäßig vorhanden ist, können Sie Folgendes tun:
if command -v given-command > /dev/null 2>&1; then
echo given-command is available
else
echo given-command is not available
fi
Wo könnte man verwenden wollen which
(t)csh
In cshund tcshhast du nicht viel Auswahl. In tcsh, das ist in Ordnung, wie whiches eingebaut ist. Dies cshist der Systembefehl which, der in einigen Fällen möglicherweise nicht die gewünschten Aktionen ausführt.
finde Befehle nur in einigen Shells
Ein Fall , in dem es könnte sinnvoll nutzen machen which, wenn Sie den Pfad einen Befehl wissen wollen, in potenziell Shell builtins oder Funktionen zu ignorieren bash, csh(nicht tcsh), dashoder BourneShell - Scripts, also Schalen, die nicht über whence -p(wie kshoder zsh) , command -ev(like yash), whatis -p( rc, akanga) oder ein eingebautes which(like tcshoder zsh) auf Systemen, wo whichverfügbar ist und nicht das cshSkript.
Wenn diese Bedingungen erfüllt sind, dann:
echo=$(which echo)
würde dir den Pfad des Ersten echoin $PATH(außer in echoEckfällen ) geben, unabhängig davon, ob es sich auch um eine eingebaute / alias / Funktion handelt oder nicht.
In anderen Shells bevorzugen Sie:
- zsh :
echo==echooder echo=$commands[echo]oderecho=${${:-echo}:c}
- ksh , zsh :
echo=$(whence -p echo)
- Yash :
echo=$(command -ev echo)
- rc , akanga :
echo=`whatis -p echo`(Vorsicht vor Pfaden mit Leerzeichen)
- Fisch :
set echo (type -fp echo)
Beachten Sie, dass Sie, wenn Sie nur diesen echoBefehl ausführen möchten , den Pfad nicht abrufen müssen, sondern einfach Folgendes tun können:
env echo this is not echoed by the builtin echo
Zum Beispiel mit tcsh, um zu verhindern, dass das eingebaute Element whichverwendet wird:
set Echo = "`env which echo`"
wenn Sie einen externen Befehl benötigen
Ein weiterer Fall, den Sie möglicherweise verwenden möchten, whichist, wenn Sie tatsächlich einen externen Befehl benötigen . POSIX setzt voraus, dass alle Shell-Builtins (wie command) auch als externe Befehle verfügbar sind, was commandauf vielen Systemen leider nicht der Fall ist. Beispielsweise ist es selten, commandunter Linux-basierten Betriebssystemen einen Befehl zu finden , während die meisten von ihnen einen whichBefehl haben (wenn auch verschiedene mit unterschiedlichen Optionen und Verhaltensweisen).
Fälle, in denen Sie einen externen Befehl benötigen, sind alle Fälle, in denen Sie einen Befehl ausführen, ohne eine POSIX-Shell aufzurufen.
Die system("some command line"), popen()... -Funktionen von C oder verschiedenen Sprachen rufen eine Shell auf, um diese Befehlszeile zu analysieren. system("command -v my-cmd")Arbeiten Sie also mit ihnen. Eine Ausnahme wäre, perldie die Shell optimiert, wenn sie kein Shell-Sonderzeichen (außer Leerzeichen) sieht. Das gilt auch für seinen Backtick-Betreiber:
$ perl -le 'print system "command -v emacs"'
-1
$ perl -le 'print system ":;command -v emacs"'
/usr/bin/emacs
0
$ perl -e 'print `command -v emacs`'
$ perl -e 'print `:;command -v emacs`'
/usr/bin/emacs
Die Hinzufügung des :;oben perlGesagten zwingt dazu, dort eine Shell aufzurufen. Wenn Sie verwenden which, müssten Sie diesen Trick nicht anwenden.
whichgehen von einem interaktiven Shell-Kontext aus. Diese Frage ist markiert / Portabilität. Daher interpretiere ich die Frage in diesem Zusammenhang als "Was soll man verwenden, anstattwhichdie erste ausführbare Datei eines bestimmten Namens in der zu finden$PATH". Die meisten Antworten und Gründe gegen denwhichUmgang mit Aliasen, Builtins und Funktionen, die in den meisten realen portablen Shell-Skripten nur von akademischem Interesse sind. Lokal definierte Aliase werden beim Ausführen eines Shell-Skripts nicht vererbt (es sei denn, Sie geben sie als Quelle an.).