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 which
Befehl 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-cmd
Befehle 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
. csh
eingeführt Aliase um 1978 (obwohl csh
wurde zuerst veröffentlicht in 2BSD
, der im Mai 1979), und auch die Verarbeitung einer .cshrc
für die Benutzer die Schale gestalten (jede Schale, wie csh
liest .cshrc
auch 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 rc
Datei (die .profile
dient 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 which
csh - Skript wurde für die hinzugefügten csh
Benutzer eine ausführbare Datei zu identifizieren, und es ist ein kaum anderes Skript Sie finden which
auf vielen kommerziellen Unix - Varianten heute (wie Solaris, HP / UX, AIX oder Tru64).
Dieses Skript liest den Benutzer ~/.cshrc
(wie alle csh
Skripte, sofern sie nicht mit aufgerufen werden csh -f
) und sucht die angegebenen Befehlsnamen in der Liste der Aliase und in $path
(dem Array, csh
das auf der Grundlage verwaltet wird $PATH
).
Los geht's, which
kam als erstes für die damals beliebteste Shell (und csh
war 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 which
csh-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 source
Eingabe einer anderen csh
Datei definiert haben, und (obwohl dies keine gute Idee wäre) PATH
möglicherweise in neu definiert ~/.cshrc
.
Wenn Sie diesen which
Befehl über eine Bourne-Shell ausführen, werden die in Ihrer definierten Aliase weiterhin gesucht. ~/.cshrc
Wenn 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 type
eingebauten 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 type
Befehl hatte ein ähnliches Problem wie das which
Skript, da er keinen Fehlerbeendigungsstatus zurückgab, wenn der Befehl nicht gefunden wurde. Im Gegensatz dazu wird für ausführbare Dateien nicht nur das which
ausgegeben, was die Verwendung in Skripten erschwert.ls is /bin/ls
/bin/ls
Die Bourne-Shell von Unix Version 8 (nicht im type
Umlauf) wurde umbenannt whatis
. Und die Plan9-Shell rc
( der ehemalige Nachfolger von Unix) (und ihre Derivate wie akanga
und es
) haben es whatis
ebenfalls.
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 csh
Funktionen (Zeileneditor, Aliase ...) der Bourne-Shell hinzu . Es wurde whence
(zusätzlich zu type
) ein eigenes eingebautes Programm hinzugefügt , das mehrere Optionen -v
zur Verfügung stellte ( um die type
ähnlich ausführliche Ausgabe bereitzustellen und -p
nur 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), zsh
wurde zwischen 1989 und 1991 veröffentlicht.
Obwohl Ash als Ersatz für die Bourne-Shell type
gedacht war, wurde es erst viel später (in NetBSD 1.3 und FreeBSD 2.3) eingebaut hash -v
. OSF / 1 /bin/sh
hatte eine type
eingebaute Version, die immer 0 bis OSF / 1 v3.x zurückgab. bash
fügte kein hinzu whence
, fügte aber eine -p
Option hinzu type
, um den Pfad auszudrucken ( type -p
würde lauten whence -p
) und alle übereinstimmenden Befehle -a
zu melden . made builtin und fügte einen Befehl hinzu , der sich wie 's verhält . hat sie alle.tcsh
which
where
bash
type -a
zsh
In der fish
Shell (2005) ist ein type
Befehl als Funktion implementiert.
Das which
csh-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
, whereis
verhält es sich so, which
dass es nur ausführbare Dateien in sucht $PATH
). In OpenBSD und FreeBSD which
wurde auch eine in C geschriebene geändert, die $PATH
nur Befehle in sucht .
Implementierungen
Es gibt Dutzende von Implementierungen eines which
Befehls auf verschiedenen Unices mit unterschiedlicher Syntax und unterschiedlichem Verhalten.
Unter Linux (neben den in tcsh
und eingebauten zsh
) gibt es mehrere Implementierungen. Auf neueren Debian-Systemen ist es beispielsweise ein einfaches POSIX-Shell-Skript, das nach Befehlen in sucht $PATH
.
busybox
hat auch einen which
Befehl.
Es gibt eine, GNU
which
die wahrscheinlich die extravaganteste ist. Es versucht, das, was das which
csh-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 bash
zu tun). .
zsh
Es 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
zsh
zsh/parameters
macht im Modul auch die Kommando-Hash-Tabelle zum commands
assoziativen Array:
$ print -r -- $commands[ls]
/bin/ls
Das whatis
Hilfsprogramm (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).
whereis
Wurde auch 3BSD
zur gleichen Zeit hinzugefügt, als which
wäre es in geschrieben C
, nicht csh
und 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 -v
und an -V
(die bis POSIX.2008 optional waren). UNIX gibt den type
Befehl an (keine Option). Das ist alles ( where
, which
, whence
sind in keiner Norm festgelegt)
Bis zu einer bestimmten Version type
und command -v
optional in der Linux Standard Base-Spezifikation, was erklärt, warum zum Beispiel einige alte Versionen von posh
(obwohl auf pdksh
denen beide basierten ) keine hatten. command -v
wurde auch zu einigen Bourne-Shell-Implementierungen hinzugefügt (wie unter Solaris).
Status Heute
Heutzutage ist dies der Status type
und er command -v
ist in allen Bourne-ähnlichen Shells allgegenwärtig (beachten Sie jedoch, wie von @jarno bemerkt, die Einschränkung / den Fehler, bash
wenn Sie sich nicht im POSIX-Modus befinden, oder einige Nachkommen der Almquist-Shell, die in den Kommentaren unten aufgeführt sind). tcsh
ist die einzige Shell, die Sie verwenden möchten which
(da keine type
vorhanden und which
eingebaut ist).
In den Schalen ausgenommen tcsh
und zsh
, which
man kann sagen , den Pfad der gegebenen ausführbaren solange es keine Alias oder eine Funktion von demselben Namen in einem unserer ~/.cshrc
, ~/.bashrc
oder jede Shell - Startdatei und Sie definieren nicht $PATH
in 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 where
in tcsh
oder zsh
, type -a
in bash
oder zsh
, whence -a
in ksh93 und in anderen Shells verwenden, die Sie type
in Kombination mit which -a
denen 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 $PATH
Komponenten 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, $PATH
bis der execve
Systemaufruf nicht mehr mit einem Fehler zurückkehrt . Zum Beispiel, wenn $PATH
enthält /foo:/bar
und Sie ausführen möchten ls
, werden sie zuerst versuchen, auszuführen, /foo/ls
oder wenn dies 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, zum Beispiel, weil es keine gültige ausführbare Datei ist. command -v ls
würde melden, /foo/ls
ob Sie über die Ausführungsberechtigung für verfügen /foo/ls
, aber ls
möglicherweise tatsächlich ausgeführt werden, /bar/ls
wenn /foo/ls
keine gültige ausführbare Datei vorhanden ist.
- Wenn
foo
es sich um eine integrierte Funktion oder einen Alias handelt, wird command -v foo
zurückgegeben foo
. Mit einigen Muscheln mögen ash
, pdksh
oder zsh
kann es auch zurückkehren , foo
wenn $PATH
die leere Zeichenfolge enthält und es gibt eine ausführbare foo
Datei 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 mount
wird sie manchmal für busybox integriert sh
) und bash
Funktionen aus der Umgebung abrufen kann.
- Wenn
$PATH
relative Pfadkomponenten enthalten sind (normalerweise .
oder die leere Zeichenfolge, die sich beide auf das aktuelle Verzeichnis beziehen, aber alles command -v cmd
Mö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 cd
anderen 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 chmod
wird zurückkehren , /opt/ast/bin/chmod
auch 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 csh
und tcsh
hast du nicht viel Auswahl. In tcsh
, das ist in Ordnung, wie which
es eingebaut ist. Dies csh
ist 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
), dash
oder Bourne
Shell - Scripts, also Schalen, die nicht über whence -p
(wie ksh
oder zsh
) , command -ev
(like yash
), whatis -p
( rc
, akanga
) oder ein eingebautes which
(like tcsh
oder zsh
) auf Systemen, wo which
verfügbar ist und nicht das csh
Skript.
Wenn diese Bedingungen erfüllt sind, dann:
echo=$(which echo)
würde dir den Pfad des Ersten echo
in $PATH
(außer in echo
Eckfällen ) geben, unabhängig davon, ob es sich auch um eine eingebaute / alias / Funktion handelt oder nicht.
In anderen Shells bevorzugen Sie:
- zsh :
echo==echo
oder 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 echo
Befehl 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 which
verwendet wird:
set Echo = "`env which echo`"
wenn Sie einen externen Befehl benötigen
Ein weiterer Fall, den Sie möglicherweise verwenden möchten, which
ist, 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 command
auf vielen Systemen leider nicht der Fall ist. Beispielsweise ist es selten, command
unter Linux-basierten Betriebssystemen einen Befehl zu finden , während die meisten von ihnen einen which
Befehl 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, perl
die 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 perl
Gesagten zwingt dazu, dort eine Shell aufzurufen. Wenn Sie verwenden which
, müssten Sie diesen Trick nicht anwenden.
which
gehen 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, anstattwhich
die erste ausführbare Datei eines bestimmten Namens in der zu finden$PATH
". Die meisten Antworten und Gründe gegen denwhich
Umgang 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.
).