Wenn Sie nach dem Pfad zu einer ausführbaren Datei suchen oder prüfen, was passieren würde, wenn Sie einen Befehlsnamen in einer Unix-Shell eingeben, gibt es eine Fülle verschiedener Dienstprogramme (which
, type
, command
, whence
, where
, whereis
, whatis
, hash
usw.).
Wir hören oft dieses which
sollte vermieden werden. Wieso den? Was sollten wir stattdessen verwenden?
Akzeptierte Antwort:
Hier ist alles, was Sie nie für möglich gehalten hätten:
Zusammenfassung
Um den Pfadnamen einer ausführbaren Datei in einem Bourne-ähnlichen Shell-Skript zu erhalten (es gibt ein paar Vorbehalte; siehe unten):
ls=$(command -v ls)
Um herauszufinden, ob ein bestimmter Befehl existiert:
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 which
command ist ein gebrochenes Erbe der C-Shell und sollte in Bourne-ähnlichen Shells besser allein gelassen werden.
Anwendungsfälle
Es gibt einen Unterschied zwischen der Suche nach diesen Informationen als Teil eines Skripts oder interaktiv am Shell-Prompt.
Am Shell-Prompt ist der typische Anwendungsfall:dieser Befehl verhält sich seltsam, verwende ich den richtigen? Was genau passiert ist, als ich mycmd
eingetippt habe ? Kann ich weiter nachsehen, was es ist?
In diesem Fall möchten Sie wissen, was Ihre Shell tut, wenn Sie den Befehl aufrufen, ohne den Befehl tatsächlich aufzurufen.
In Shell-Skripten sieht es tendenziell 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 wissen, damit Sie weitere Informationen daraus erhalten können (z. B. den Pfad zu einer anderen Datei relativ dazu oder Informationen aus dem Inhalt der ausführbaren Datei unter diesem Pfad).
Interaktiv möchten Sie vielleicht etwas über alle wissen das my-cmd
Befehle, die auf dem System verfügbar sind, selten in Skripten.
Die meisten der verfügbaren Tools (wie es oft der Fall ist) wurden für die interaktive Verwendung entwickelt.
Geschichte
Ein bisschen Geschichte zuerst.
Die frühen Unix-Shells bis Ende der 70er Jahre hatten keine Funktionen oder Aliase. Nur das traditionelle Nachschlagen von ausführbaren Dateien in $PATH
. csh
führte Aliase um 1978 ein (obwohl csh
wurde zuerst veröffentlicht in 2BSD
, im Mai 1979), sowie die Verarbeitung einer .cshrc
für Benutzer zum Anpassen der Shell (jede Shell als csh
, liest .cshrc
auch wenn nicht interaktiv wie in Skripten).
Während die Bourne-Shell erstmals 1979 in Unix V7 veröffentlicht wurde, wurde die Funktionsunterstützung erst viel später hinzugefügt (1984 in SVR2), und außerdem hatte sie nie irgendeinen rc
Datei (die .profile
ist, Ihre Umgebung zu konfigurieren, 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 praktischere und nettere Funktionen für die interaktive Verwendung hinzufügte.
In 3BSD
(1980), ein which
csh-Skript wurde für csh
hinzugefügt Benutzern helfen, eine ausführbare Datei zu identifizieren, und es ist ein kaum anderes Skript, das Sie als which
finden können auf vielen kommerziellen Unices heutzutage (wie Solaris, HP/UX, AIX oder Tru64).
Dieses Skript liest den ~/.cshrc
des Benutzers (wie alle csh
Skripte tun dies, es sei denn, sie werden mit csh -f
aufgerufen ) und sucht den/die bereitgestellten Befehlsnamen in der Liste der Aliase und in $path
(das Array, das csh
verwaltet basierend auf $PATH
).
Bitte schön:which
kam zuerst für die damals beliebteste Shell (und csh
war noch bis Mitte der 90er Jahre beliebt), was der Hauptgrund dafür ist, dass es in Büchern dokumentiert wurde und immer noch weit verbreitet ist.
Beachten Sie das auch für einen csh
Benutzer, das which
csh-Skript gibt Ihnen nicht unbedingt die richtigen Informationen. Es erhält die in ~/.cshrc
definierten Aliase , nicht die, die Sie später an der Eingabeaufforderung oder beispielsweise durch source
definiert haben einen weiteren csh
erstellen file, und (obwohl das keine gute Idee wäre), PATH
kann in ~/.cshrc
neu definiert werden .
Ausführen dieses which
Befehl von einer Bourne-Shell würde immer noch Aliase suchen, die in Ihrem ~/.cshrc
definiert sind , aber wenn Sie keine haben, weil Sie csh
nicht verwenden , das würde Ihnen wahrscheinlich immer noch die richtige Antwort geben.
Eine ähnliche Funktionalität wurde der Bourne-Shell erst 1984 in SVR2 mit dem type
hinzugefügt eingebauter Befehl. Die Tatsache, dass es eingebaut ist (im Gegensatz zu einem externen Skript), bedeutet, dass es kann geben Ihnen (bis zu einem gewissen Grad) die richtigen Informationen, da es Zugriff auf die Interna der Shell hat.
Der anfängliche type
Der Befehl litt unter einem ähnlichen Problem wie der which
Skript dadurch, dass es keinen Fehler-Exit-Status zurückgab, wenn der Befehl nicht gefunden wurde. Auch für ausführbare Dateien, im Gegensatz zu which
, gibt es so etwas wie ls is /bin/ls
aus statt nur /bin/ls
was die Verwendung in Skripten weniger einfach machte.
Die (nicht in freier Wildbahn veröffentlichte) Bourne-Shell von Unix Version 8 hatte ihren type
builtin umbenannt in whatis
und erweitert, um auch über Parameter und Druckfunktionsdefinitionen zu berichten. Es hat auch type
behoben Problem, dass kein Fehler zurückgegeben wird, wenn ein Name nicht gefunden wird.
rc
, die Shell von Plan9 (dem einstigen Nachfolger von Unix) (und seinen Derivaten wie akanga
und es
) haben whatis
auch.
Die Korn-Shell (eine Teilmenge, von der die POSIX sh
Definition basiert auf), Mitte der 80er Jahre entwickelt, aber vor 1988 nicht allgemein verfügbar, fügten viele der csh
hinzu Funktionen (Zeileneditor, Aliase …) auf der Bourne-Shell. Es hat seinen eigenen whence
hinzugefügt builtin (zusätzlich zu type
), die mehrere Optionen benötigte (-v
mit dem type
zu versehen -ähnliche ausführliche Ausgabe und -p
um nur nach ausführbaren Dateien zu suchen (nicht nach Aliasen/Funktionen…)).
Zufällig zu den Turbulenzen in Bezug auf die Urheberrechtsprobleme zwischen AT&T und Berkeley, ein paar freie Software Shell-Implementierungen kamen Ende der 80er Anfang der 90er Jahre heraus. Die gesamte Almquist-Shell (ash
, als Ersatz für die Bourne-Shell in BSDs), die Public-Domain-Implementierung von ksh
(pdksh
), bash
(gesponsert von der FSF), zsh
kam zwischen 1989 und 1991 heraus.
Obwohl Ash als Ersatz für die Bourne-Shell gedacht war, hatte es keinen type
erst viel später eingebaut (in NetBSD 1.3 und FreeBSD 2.3), obwohl es hash -v
hatte . OSF/1 /bin/sh
hatte einen type
Builtin, das bis OSF/1 v3.x immer 0 zurückgab. bash
hat kein whence
hinzugefügt aber ein -p
hinzugefügt Option zum type
um den Pfad auszudrucken (type -p
wäre wie whence -p
) und -a
um alle zu melden die passenden Befehle. tcsh
which
gemacht eingebaut und ein where
hinzugefügt Befehl, der sich wie bash
verhält ‘s type -a
. zsh
hat sie alle.
Der fish
Shell (2005) hat einen type
Befehl als Funktion implementiert.
Der which
Das csh-Skript wurde inzwischen aus NetBSD entfernt (da es in tcsh eingebaut war und in anderen Shells nicht viel Verwendung fand) und die Funktionalität zu whereis
hinzugefügt (bei Aufruf als which
, whereis
verhält sich wie which
außer dass es nur ausführbare Dateien in $PATH
sucht ). In OpenBSD und FreeBSD, which
wurde auch in einen in C geschriebenen geändert, der Befehle in $PATH
nachschlägt nur.
Implementierungen
Es gibt Dutzende von Implementierungen eines which
Befehl auf verschiedenen Unices mit unterschiedlicher Syntax und Verhalten.
Unter Linux (neben den eingebauten in tcsh
und zsh
) finden wir mehrere Implementierungen. Auf neueren Debian-Systemen ist es beispielsweise ein einfaches POSIX-Shell-Skript, das nach Befehlen in $PATH
sucht .
busybox
hat auch einen which
Befehl.
Es gibt ein GNU
which
das ist wohl das extravaganteste. Es versucht, what den which
zu erweitern csh-Skript mit anderen Shells gemacht:Sie können ihm sagen, was Ihre Aliase und Funktionen sind, damit es Ihnen eine bessere Antwort geben kann (und ich glaube, einige Linux-Distributionen setzen einige globale Aliase um diese für bash
um das zu tun).
zsh
hat ein paar Operatoren um den Pfad der ausführbaren Dateien zu erweitern:=
Dateinamenerweiterung -Operator und den :c
Verlaufserweiterungsmodifikator (hier angewendet auf Parametererweiterung ):
$ print -r -- =ls
/bin/ls
$ cmd=ls; print -r -- $cmd:c
/bin/ls
zsh
, in zsh/parameters
-Modul erstellt auch die Befehls-Hash-Tabelle als commands
Assoziatives Array:
$ print -r -- $commands[ls]
/bin/ls
Das whatis
Dienstprogramm (mit Ausnahme des in Unix V8 Bourne Shell oder Plan 9 rc
/es
) ist nicht wirklich verwandt, da es nur zur Dokumentation dient (greps the whatis database, that is the man page synopsis’).
whereis
wurde auch in 3BSD
hinzugefügt gleichzeitig mit which
obwohl es in C
geschrieben wurde , nicht csh
und wird verwendet, um gleichzeitig die ausführbare Datei, die Manpage und die Quelle nachzuschlagen, jedoch nicht basierend auf der aktuellen Umgebung. Das entspricht also wieder einem anderen Bedürfnis.
An der Standardfront gibt POSIX nun den command -v
an und -V
Befehle (die bis POSIX.2008 optional waren). UNIX gibt den type
an Befehl (keine Option). Das ist alles (where
, which
, whence
sind in keiner Norm spezifiziert).
Bis zu einigen Versionen type
und command -v
waren in der Linux Standard Base-Spezifikation optional, was erklärt, warum zum Beispiel einige alte Versionen von posh
(allerdings basierend auf pdksh
die beides hatte) hatte beides nicht. command -v
wurde auch zu einigen Bourne-Shell-Implementierungen hinzugefügt (wie unter Solaris).
Status heute
Der Status heutzutage ist dieser type
und command -v
sind in allen Bourne-ähnlichen Shells allgegenwärtig (beachten Sie jedoch, wie von @jarno angemerkt, den Vorbehalt/Fehler in bash
wenn nicht im POSIX-Modus oder einige Nachkommen der Almquist-Shell unten in den Kommentaren). tcsh
ist die einzige Shell, in der Sie which
verwenden möchten (da es keinen type
gibt dort und which
ist eingebaut).
In anderen Shells als tcsh
und zsh
, which
kann Ihnen den Pfad der angegebenen ausführbaren Datei mitteilen, solange es in keinem unserer ~/.cshrc
einen Alias oder eine Funktion mit demselben Namen gibt , ~/.bashrc
oder eine beliebige Shell-Startdatei und Sie definieren $PATH
nicht in Ihrem ~/.cshrc
. Wenn Sie einen Alias oder eine Funktion dafür definiert haben, kann es Ihnen etwas darüber sagen, oder auch nicht, oder Ihnen das Falsche sagen.
Wenn Sie alle Befehle mit einem bestimmten Namen wissen möchten, gibt es nichts Portierbares. Sie würden where
verwenden in tcsh
oder zsh
, type -a
ein in bash
oder zsh
, whence -a
in ksh93 und in anderen Shells können Sie type
verwenden in Kombination mit which -a
was funktionieren könnte.
Empfehlungen
Ermitteln des Pfadnamens zu einer ausführbaren Datei
Nun, um den Pfadnamen einer ausführbaren Datei in einem Skript zu erhalten, gibt es ein paar Vorbehalte:
ls=$(command -v ls)
wäre der Standardweg, es zu tun.
Es gibt jedoch ein paar Probleme:
- Es ist nicht möglich, den Pfad der ausführbaren Datei zu kennen, ohne sie auszuführen. Alle
type
,which
,command -v
… alle verwenden Heuristiken, um den Pfad herauszufinden. Sie durchlaufen den$PATH
Komponenten und suchen Sie die erste Nicht-Verzeichnisdatei, für die Sie eine Ausführungsberechtigung haben. Abhängig von der Shell führen viele von ihnen (Bourne, AT&T ksh, zsh, ash …) den Befehl jedoch nur in der Reihenfolge von$PATH
aus bis zumexecve
Systemaufruf kommt nicht mit einem Fehler zurück. Wenn beispielsweise$PATH
enthält/foo:/bar
und Siels
ausführen möchten , werden sie zuerst versuchen,/foo/ls
auszuführen oder wenn das fehlschlägt/bar/ls
. Jetzt Ausführung von/foo/ls
kann fehlschlagen, weil Sie keine Ausführungsberechtigung haben, aber auch aus vielen anderen Gründen, z. B. weil es sich nicht um eine gültige ausführbare Datei handelt.command -v ls
würde/foo/ls
melden wenn Sie die Ausführungsberechtigung für/foo/ls
haben , aberls
ausführen könnte tatsächlich/bar/ls
ausführen wenn/foo/ls
ist keine gültige ausführbare Datei. - wenn
foo
ist eine eingebaute Funktion oder ein Alias,command -v foo
gibtfoo
zurück . Mit einigen Shells wieash
,pdksh
oderzsh
, es kann auchfoo
zurückgeben wenn$PATH
enthält den leeren String und es gibt ein ausführbaresfoo
Datei im aktuellen Verzeichnis. Es gibt Situationen, in denen Sie dies berücksichtigen müssen. Denken Sie zum Beispiel daran, dass die Liste der Builtins je nach Shell-Implementierung variiert (zum Beispielmount
ist manchmal für busyboxsh
eingebaut ) und zum Beispielbash
kann Funktionen aus der Umgebung abrufen. - wenn
$PATH
enthält relative Pfadkomponenten (normalerweise.
oder die leere Zeichenfolge, die sich beide auf das aktuelle Verzeichnis beziehen, aber alles sein können), je nach Shell,command -v cmd
gibt möglicherweise keinen absoluten Pfad aus. Also den Pfad, den Sie erhalten, wenn Siecommand -v
ausführen ist nach demcd
nicht mehr gültig woanders. - Anekdotisch:mit der ksh93-Shell, wenn
/opt/ast/bin
(obwohl dieser genaue Pfad auf verschiedenen Systemen variieren kann, glaube ich) ist in Ihnen$PATH
, ksh93 stellt einige zusätzliche Builtins zur Verfügung (chmod
,cmp
,cat
…), sonderncommand -v chmod
gibt/opt/ast/bin/chmod
zurück auch wenn dieser Pfad nicht existiert.
Feststellen, ob ein Befehl existiert
Um herauszufinden, ob ein bestimmter Befehl standardmäßig existiert, 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 man vielleicht which
verwenden möchte
(t)csh
In csh
und tcsh
, du hast keine große Wahl. In tcsh
, das ist in Ordnung als which
ist eingebaut. In csh
, das ist das System which
Befehl, der in einigen Fällen möglicherweise nicht das tut, was Sie möchten.
Befehle nur in einigen Shells finden
Ein Fall, in dem es sinnvoll sein könnte, which
zu verwenden ist, wenn Sie den Pfad eines Befehls wissen möchten und dabei potenzielle Shell-Builts oder -Funktionen in bash
ignorieren , csh
(nicht tcsh
), dash
, oder Bourne
Shell-Skripte, d. h. Shells, die kein whence -p
haben (wie ksh
oder zsh
), command -ev
(wie yash
), whatis -p
(rc
, akanga
) oder ein eingebautes which
(wie tcsh
oder zsh
) auf Systemen, auf denen which
ist verfügbar und ist nicht der csh
Skript.
Wenn diese Bedingungen erfüllt sind, dann:
echo=$(which echo)
würde Ihnen den Pfad des ersten echo
geben in $PATH
(außer in Ausnahmefällen), egal ob echo
zufälligerweise auch eine eingebaute Shell/Alias/Funktion oder nicht.
In anderen Shells bevorzugen Sie:
- zsh :
echo==echo
oderecho=$commands[echo]
oderecho=${${:-echo}:c}
- ksch , zsch :
echo=$(whence -p echo)
- yash :
echo=$(command -ev echo)
- rc , akanga :
echo=`whatis -p echo`
(Vorsicht bei Pfaden mit Leerzeichen) - Fisch :
set echo (type -fp echo)
Beachten Sie, dass Sie nur laufen möchten dieses echo
Befehl, müssen Sie seinen Pfad nicht abrufen, Sie können einfach Folgendes tun:
env echo this is not echoed by the builtin echo
Zum Beispiel mit tcsh
, um das eingebaute which
zu verhindern nicht verwendet werden:
set Echo = "`env which echo`"
Wenn Sie einen externen Befehl benötigen
Ein weiterer Fall, in dem Sie vielleicht which
verwenden möchten ist, wenn Sie wirklich brauchen ein externer Befehl. POSIX erfordert, dass alle Shell-Einbauten (wie command
) auch als externe Befehle verfügbar sein, aber leider ist das bei command
nicht der Fall auf vielen Systemen. Zum Beispiel ist es selten, einen command
zu finden Befehl auf Linux-basierten Betriebssystemen, während die meisten von ihnen einen which
haben Befehl (obwohl verschiedene mit unterschiedlichen Optionen und Verhaltensweisen).
Fälle, in denen Sie einen externen Befehl benötigen, wären überall dort, wo Sie einen Befehl ausführen würden, ohne eine POSIX-Shell aufzurufen.
Das system("some command line")
, popen()
… Funktionen von C oder verschiedenen Sprachen rufen eine Shell auf, um diese Befehlszeile zu parsen, also system("command -v my-cmd")
arbeite in ihnen. Eine Ausnahme davon wäre perl
wodurch die Shell optimiert wird, wenn sie keine Shell-Sonderzeichen (außer Leerzeichen) sieht. Das gilt auch für seinen Backtick-Operator:
$ 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 dieses :;
oben erzwingt perl
um dort eine Shell aufzurufen. Durch die Verwendung von which
, müssten Sie diesen Trick nicht anwenden.