Warum nicht “ verwenden, welches ”? Was ist dann zu verwenden?

Wenn Sie nach dem Pfad zu einer ausführbaren Datei suchen oder prüfen, was passieren würde, wenn Sie einen Befehlsnamen in eine Unix-Shell eingeben, gibt es eine Vielzahl verschiedener Dienstprogramme ( which, type, command, whence, where, whereis, whatis, hash usw.)

Wir hören oft, dass which vermieden werden sollte. Warum? Was sollten wir stattdessen verwenden?

Kommentare

  • Ich denke, die meisten Argumente gegen die Verwendung von which gehen von einem interaktiven Shell-Kontext aus. Diese Frage ist mit Tags / Portabilität versehen Die Frage in diesem Zusammenhang lautet „, was anstelle von which verwendet werden soll, um die erste ausführbare Datei eines bestimmten Namens in der $PATH „. Die meisten Antworten und Gründe dagegen which befasst sich mit Aliasen, integrierten Funktionen und Funktionen, die in den meisten tragbaren Shell-Skripten der realen Welt nur von akademischem Interesse sind. Lokal definierte Aliase werden beim Ausführen eines Shell-Skripts nicht ‚ vererbt (es sei denn, Sie beziehen es mit .).
  • @MattBianco, ja, csh (und which ist in den meisten Werbespots immer noch ein csh -Skript Unices) liest ~/.cshrc, wenn es nicht interaktiv ist. Aus diesem Grund ‚ werden Sie ‚ feststellen, dass csh-Skripte normalerweise mit #! /bin/csh -f beginnen. which funktioniert nicht, weil darauf abzielt, Ihnen die Aliase zu geben, da ‚ als Werkzeug für gedacht ist (interaktive) Benutzer von csh. Benutzer von POSIX-Shells haben command -v.
  • @rudimeier, dann lautet die Antwort immer, es sei denn, Ihre Shell lautet (t)csh (oder es macht Ihnen nichts aus, ‚, wenn es nicht ‚ nicht das richtige Ergebnis liefert), verwenden Sie type oder command -v stattdessen . Siehe die Antworten für warum .
  • @rudimeier, (stat $(which ls) ist aus mehreren Gründen falsch (, fehlende Anführungszeichen), nicht nur die Verwendung von which). Sie ‚ würden stat -- "$(command -v ls)" verwenden. Dies setzt voraus, dass ls tatsächlich ein Befehl ist, der im Dateisystem gefunden wird (kein integrierter Bestandteil Ihrer Shell oder Funktion eines Alias). which gibt Ihnen möglicherweise den falschen Pfad (nicht den Pfad, den Ihre Shell ausführen würde, wenn Sie ls eingeben) oder gibt Ihnen einen definierten Alias In der Konfiguration einiger anderer Shells …
  • @rudimeier gibt es wieder eine Reihe von Bedingungen, unter denen viele which -Implementierungen Ihnen nicht einmal die geben würden ls, das durch Nachschlagen von $PATH gefunden würde (unabhängig davon, was ls ist kann in Ihrer Shell aufrufen). sh -c 'command -v ls' oder zsh -c 'rpm -q --whatprovides =ls' geben Ihnen eher die richtige Antwort. Der Punkt hier ist, dass which ein gebrochenes Erbe von csh ist.

Antwort

Hier ist alles, was Sie nie gedacht hätten, dass Sie jemals nichts darüber wissen wollen:

Zusammenfassung

Um das zu erhalten Pfadname einer ausführbaren Datei in einem Bourne-ähnlichen Shell-Skript (es gibt einige Einschränkungen; 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 

Auf Eingabeaufforderung einer interaktiven Bourne-ähnlichen Shell:

type ls 

Die Der Befehl which ist ein zerbrochenes Erbe der C-Shell und wird in Bourne-ähnlichen Shells besser in Ruhe gelassen.

Anwendungsfälle

Dort „Eine Unterscheidung zwischen der Suche nach diesen Informationen als Teil eines Skripts oder interaktiv an der Shell-Eingabeaufforderung.

An der Shell-Eingabeaufforderung lautet der typische Anwendungsfall: Dieser Befehl verhält sich seltsam Was genau ist passiert, als ich 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 den Befehl tatsächlich aufzurufen.

In Shell-Skripten ist dies in der Regel ganz anders. 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 möchten Sie möglicherweise über alle my-cmd -Befehle Bescheid wissen, die auf dem System in Skripten selten verfügbar sind.

Die meisten verfügbaren Tools (wie es häufig der Fall ist) wurden für die interaktive Verwendung entwickelt.

Verlauf

Zuerst ein wenig Verlauf.

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 führte um 1978 Aliase ein (obwohl csh zum ersten Mal in 2BSD, im Mai 1979) und auch die Verarbeitung eines .cshrc, damit Benutzer die Shell anpassen können (jede Shell als csh , liest .cshrc, auch wenn es nicht wie in Skripten interaktiv ist.

Während die Bourne-Shell 1979 erstmals in Unix V7 veröffentlicht wurde, wurde die Funktionsunterstützung nur stark erweitert später (1984 in SVR2) hatte es sowieso nie eine rc -Datei (die .profile dient zum Konfigurieren Ihrer Umgebung, nicht der Shell per se ).

csh wurde viel beliebter als die Bourne-Shell als (obwohl sie eine schrecklich schlechtere Syntax als die hatte Bourne-Shell) wurden viele bequemere und schönere Funktionen für die interaktive Verwendung hinzugefügt.

In 3BSD (1980) wurde eine which csh-Skript wurde für die Benutzer csh hinzugefügt, um die Identifizierung einer ausführbaren Datei zu erleichtern. Es handelt sich um ein kaum anderes Skript, das Sie als auf vielen kommerziellen Unices heutzutage (wie Solaris, HP / UX, AIX oder Tru64).

Dieses Skript liest die ~/.cshrc des Benutzers (wie alle csh -Skripte, sofern sie nicht mit csh -f aufgerufen werden) und sucht die angegebenen Befehlsnamen in der Liste der Aliase und in $path (das Array, das csh basierend auf $PATH verwaltet).

Los gehts: which war der erste für die damals beliebteste Shell (und csh war bis Mitte des Jahres noch beliebt. 90er Jahre), was der Hauptgrund ist, warum es in Büchern dokumentiert wurde und immer noch weit verbreitet ist.

Beachten Sie, dass selbst für einen csh Benutzer which csh-Skript gibt Ihnen nicht unbedingt die richtigen Informationen. Es werden die in ~/.cshrc definierten Aliase abgerufen, nicht diejenigen, die Sie möglicherweise später an der Eingabeaufforderung definiert haben, oder beispielsweise durch source eines anderen csh -Datei, und (obwohl dies keine gute Idee wäre) PATH wird möglicherweise in ~/.cshrc.

Wenn Sie den Befehl which in einer Bourne-Shell ausführen, werden weiterhin Aliase gesucht, die in Ihrem ~/.cshrc definiert sind Wenn Sie keine haben, weil Sie csh nicht verwenden, erhalten Sie wahrscheinlich immer noch die richtige Antwort.

Eine ähnliche Funktionalität wurde nicht hinzugefügt die Bourne-Shell bis 1984 in SVR2 mit dem eingebauten Befehl type. Die Tatsache, dass es eingebaut ist (im Gegensatz zu einem externen Skript), bedeutet, dass es Ihnen (bis zu einem gewissen Grad) die richtigen Informationen geben kann, da es Zugriff auf die Interna der Shell hat.

Der anfängliche Befehl type hatte ein ähnliches Problem wie das Skript which, da er keinen Fehler-Exit-Status zurückgab, wenn Der Befehl wurde nicht gefunden. Außerdem gab er für ausführbare Dateien im Gegensatz zu which etwas wie ls is /bin/ls statt nur , was die Verwendung in Skripten weniger einfach machte.

Die Bourne-Shell der Unix-Version 8 (nicht in freier Wildbahn veröffentlicht) hatte die type wurde in whatis umbenannt. Und die Plan9-Shell (der künftige Nachfolger von Unix) rc (und seine Derivate wie akanga und es) haben ebenfalls whatis.

Die Korn-Muschel (eine Teilmenge davon die Die POSIX sh -Definition basiert auf), die Mitte der 80er Jahre entwickelt wurde, aber vor 1988 nicht allgemein verfügbar war, fügte viele der csh -Funktionen hinzu ( Zeileneditor, Aliase …) über der Bourne-Shell. Es wurde eine eigene whence eingebaute Version (zusätzlich zu type) hinzugefügt, die mehrere Optionen (-v um die type -ähnliche ausführliche Ausgabe bereitzustellen, und -p, um nur nach ausführbaren Dateien zu suchen (keine Aliase / Funktionen …))

Zufällig zu den Turbulenzen in Bezug auf die Urheberrechtsprobleme zwischen AT & T und Berkeley kamen einige freie Software Shell-Implementierungen in den späten 80ern, frühen 90ern.Die gesamte Almquist-Shell (ash, um die Bourne-Shell in BSDs zu ersetzen), die gemeinfreie Implementierung von ksh (pdksh), bash (gesponsert von der FSF), zsh erschien zwischen 1989 und 1989 1991.

Ash, obwohl als Ersatz für die Bourne-Shell gedacht, hatte erst viel später (in NetBSD 1.3 und FreeBSD 2.3) eine type eingebaut ), obwohl hash -v. OSF / 1 /bin/sh hatte eine type eingebaut, die immer 0 bis OSF / 1 v3.x zurückgegeben. bash hat kein whence hinzugefügt, sondern ein Option auf type zum Drucken des Pfads (type -p entspricht whence -p) und -a, um alle passenden Befehle zu melden. tcsh hat which eingebaut und einen where -Befehl hinzugefügt, der sich wie “ s type -a. zsh hat sie alle.

Die fish Shell (2005) hat einen type Befehl als Funktion implementiert.

Die which Das csh-Skript wurde inzwischen aus NetBSD entfernt (da es in tcsh integriert und in anderen Shells nicht sehr nützlich ist), und die Funktionalität wurde zu whereis hinzugefügt (wenn es als which, whereis verhält sich wie which, außer dass nur ausführbare Dateien in ). In OpenBSD und FreeBSD wurde which ebenfalls in einen in C geschriebenen Befehl geändert, der Befehle nur in $PATH nachschlägt .

Implementierungen

Es gibt Dutzende von Implementierungen einer which co mmand auf verschiedenen Unices mit unterschiedlicher Syntax und unterschiedlichem Verhalten.

Unter Linux (neben den integrierten 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 einen GNU which ist wahrscheinlich das extravaganteste. Es versucht zu erweitern, was das Skript which csh mit anderen Shells gemacht hat: Sie können ihm sagen, was Ihre Aliase und Funktionen sind, damit es geben kann Sie haben eine bessere Antwort (und ich glaube, einige Linux-Distributionen haben einige globale Aliase festgelegt, damit bash dies tut).

zsh verfügt über mehrere Operatoren , die auf den Pfad der ausführbaren Dateien erweitert werden können: den Operator = Dateinamenerweiterung und den Operator :c Verlaufserweiterungsmodifikator (hier angewendet auf Parametererweiterung ):

$ print -r -- =ls /bin/ls $ cmd=ls; print -r -- $cmd:c /bin/ls 

zsh, in der erstellt die Befehls-Hash-Tabelle auch als assoziatives Array commands:

$ print -r -- $commands[ls] /bin/ls 

Das Dienstprogramm whatis (mit Ausnahme des Dienstprogramms in Unix V8 Bourne Shell oder Plan 9 rc / es) ist nicht wirklich verwandt, da es nur zur Dokumentation dient (erfasst die whatis-Datenbank, das ist die Manpage-Zusammenfassung „).

whereis war es auch hinzugefügt in 3BSD zur gleichen Zeit wie which, obwohl es in C geschrieben wurde, nicht csh und wird verwendet, um gleichzeitig die ausführbare Datei, die Manpage und die Quelle zu suchen, jedoch nicht basierend auf der aktuellen Umgebung. Dies entspricht wiederum einem anderen Bedarf.

Auf der Standardseite gibt POSIX nun die command -v und -V Befehle (die bis POSIX.2008 optional waren). UNIX gibt den Befehl type an (keine Option). Das ist alles (where, which, whence sind in keinem Standard angegeben)

Bis zu einer bestimmten Version waren type und command -v in der Linux Standard Base-Spezifikation optional, was erklärt, warum für Einige alte Versionen von posh (obwohl sie auf pdksh basieren, die beide hatten) hatten beide nicht. command -v wurde auch einigen Bourne-Shell-Implementierungen hinzugefügt (wie unter Solaris).

Status heute

Der heutige Status lautet, dass type und command -v insgesamt allgegenwärtig sind die Bourne-ähnlichen Shells (beachten Sie jedoch, wie von @jarno bemerkt, die Einschränkung / den Fehler in bash, wenn Sie sich nicht im POSIX-Modus befinden, oder einige Nachkommen der Almquist-Shell unten in den Kommentaren). tcsh ist die einzige Shell, in der Sie which verwenden möchten (da es kein type dort und which ist eingebaut).

In anderen Shells als tcsh und zsh, which kann Ihnen den Pfad der angegebenen ausführbaren Datei mitteilen, solange in keiner unserer ~/.cshrc, ~/.bashrc oder eine beliebige Shell-Startdatei, und Sie definieren $PATH nicht in Ihrer ~/.cshrc. Wenn Sie einen Alias oder eine Funktion dafür definiert haben, kann er Ihnen davon erzählen oder nicht oder Ihnen das Falsche sagen.

Wenn Sie es wissen möchten Über alle Befehle mit einem bestimmten Namen gibt es nichts Portables. Sie würden where in tcsh oder zsh, in bash oder zsh, whence -a in ksh93 und in anderen Shells können Sie type in Kombination mit which -a verwenden, was möglicherweise funktioniert.

Empfehlungen

Abrufen des Pfadnamens zu einer ausführbaren Datei

Um den Pfadnamen einer ausführbaren Datei in einem Skript abzurufen, gibt es einige Einschränkungen:

ls=$(command -v ls) 

wäre die Standardmethode.

Es gibt jedoch einige Probleme:

  • Es ist nicht möglich, den Pfad der ausführbaren Datei zu kennen, ohne sie auszuführen. Alle Die type, which, command -v … alle verwenden Heuristiken, um den Pfad herauszufinden Sie durchlaufen die $PATH -Komponenten und suchen die erste Nicht-Verzeichnisdatei, für die Sie die Ausführungsberechtigung haben Wenn Sie den Befehl ausführen, werden viele von ihnen (Bourne, AT & T ksh, zsh, ash …) nur in der Reihenfolge von ausgeführt $PATH bis der Systemaufruf execve nicht mit einem Fehler zurückkehrt. Wenn beispielsweise $PATH /foo:/bar enthält und Sie ls ausführen möchten, versuchen sie es zuerst /foo/ls ausführen oder wenn dies fehlschlägt /bar/ls. Jetzt kann die Ausführung von /foo/ls fehlschlagen, da Sie keine Ausführungsberechtigung haben, aber auch aus vielen anderen Gründen, da es sich nicht um eine gültige ausführbare Datei handelt. command -v ls meldet /foo/ls, wenn Sie über die Ausführungsberechtigung für /foo/ls verfügen, aber ls führt möglicherweise tatsächlich /bar/ls aus, wenn /foo/ls keine gültige ausführbare Datei ist.
  • Wenn foo eine integrierte Funktion oder ein Alias ist, gibt command -v foo foo zurück. Bei einigen Shells wie ash, pdksh oder zsh kann auch foo wenn $PATH die leere Zeichenfolge enthält und sich im aktuellen Verzeichnis eine ausführbare foo -Datei befindet. Unter bestimmten Umständen müssen Sie dies möglicherweise berücksichtigen. Beachten Sie beispielsweise, dass die Liste der integrierten Funktionen von der Shell-Implementierung abhängt (z. B. ist mount manchmal für die Busybox integriert sh) und beispielsweise bash können Funktionen aus der Umgebung abrufen.
  • wenn $PATH enthält relative Pfadkomponenten (normalerweise . oder die leere Zeichenfolge, die beide auf das aktuelle Verzeichnis verweisen, aber alles sein können), abhängig von der Shell, command -v cmd gibt möglicherweise keinen absoluten Pfad aus. Der Pfad, den Sie zum Zeitpunkt der Ausführung von ist nicht mehr gültig, nachdem Sie cd an einer anderen Stelle.
  • 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 integrierte Funktionen zur Verfügung (, cmp, cat …), aber command -v chmod gibt /opt/ast/bin/chmod zurück, auch wenn dieser Pfad nicht existiert.

Feststellen, ob ein Befehl vorhanden ist

Um herauszufinden, ob ein bestimmter Befehl standardmäßig vorhanden ist, haben Sie folgende Möglichkeiten:

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 which

(t)csh

verwenden möchte In csh und tcsh haben Sie nicht viel Auswahl. In tcsh ist dies nicht der Fall „ist in Ordnung, da which eingebaut ist. In csh ist dies der Systembefehl which, der in einigen Fällen möglicherweise nicht das tut, was Sie möchten.

Befehle nur in einigen Shells suchen

Ein Fall, in dem die Verwendung von which sinnvoll sein kann, besteht darin, den Pfad eines Befehls zu kennen und das Potenzial zu ignorieren Shell-Einbauten oder -Funktionen in bash, csh (nicht tcsh), dash oder Bourne Shell-Skripte, dh Shells, die keine 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 die csh script.

Wenn diese Bedingungen erfüllt sind, geben Sie:

echo=$(which echo) 

den Pfad der ersten echo in $PATH (außer in Eckfällen), unabhängig davon, ob echo zufällig auch eine Shell ist Builtin / Alias / Function oder nicht.

In anderen Shells würden Sie Folgendes bevorzugen:

  • zsh : echo==echo oder echo=$commands[echo] oder echo=${${:-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 alles, was Sie tun möchten, run Mit dem Befehl echo müssen Sie seinen Pfad nicht ermitteln. Sie können einfach Folgendes tun:

env echo this is not echoed by the builtin echo 

Zum Beispiel mit tcsh, um zu verhindern, dass das eingebaute which verwendet wird:

set Echo = "`env which echo`" 

Wenn Sie einen externen Befehl benötigen

Ein weiterer Fall, in dem Sie which verwenden möchten, ist, wenn Sie tatsächlich benötigen ein externer Befehl. POSIX erfordert, dass alle Shell-Buildins (wie command) auch als externe Befehle verfügbar sind. Leider ist dies bei command nicht der Fall Auf vielen Systemen. Beispielsweise ist es unter Linux-basierten Betriebssystemen selten, einen command -Befehl zu finden, während die meisten von ihnen einen which haben Befehl (obwohl verschiedene mit unterschiedlichen Optionen und Verhaltensweisen).

Fälle, in denen Sie möglicherweise einen externen Befehl wünschen, sind überall dort, wo Sie einen Befehl ausführen würden, 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, also system("command -v my-cmd") arbeiten in ihnen. Eine Ausnahme wäre perl, das die Shell optimiert, wenn kein Shell-Sonderzeichen (außer Leerzeichen) angezeigt wird. Dies gilt auch für den 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 

Das Hinzufügen dieses :; oben zwingt perl, eine Shell aufzurufen Wenn Sie which verwenden, müssen Sie diesen Trick nicht anwenden.

Kommentare

  • @ Joe, which ist ein csh -Skript auf vielen kommerziellen Unices. Der Grund ist historisch, dass ‚ der Grund ist, warum ich die Geschichte gegeben habe, damit die Leute verstehen, woher sie stammt, warum sich die Leute daran gewöhnt haben und warum sie tatsächlich ist kein Grund, warum Sie es verwenden sollten. Und ja, manche Leute benutzen (t) csh. Noch verwendet nicht jeder Linux
  • Nachdem ich diesen Beitrag gelesen habe, habe ich viel Kontext für die Antwort gefunden, aber nicht die Antwort selbst.Wo in diesem Beitrag steht tatsächlich, warum nicht which verwendet wird, im Gegensatz zu Dingen, die Sie möglicherweise versuchen, which zu erledigen, der Verlauf von which, Implementierungen von which, andere Befehle zum Ausführen verwandter Aufgaben oder Gründe für die tatsächliche Verwendung which? Warum sind die anderen Befehle besser ? Was machen sie anders als which? Wie vermeiden sie seine Fallstricke? Diese Antwort verwendet tatsächlich mehr Wörter für die Probleme mit den Alternativen als für die Probleme mit which.
  • command wird beschrieben von POSIX.
  • @St é phaneChazelas Wenn ich eine neue Datei mit touch /usr/bin/mytestfile erstelle und danach command -v mytestfile gibt den Pfad an (während which mytestfile dies nicht tut).
  • @jarno, oh ja, Sie ‚ ist richtig. bash entscheidet sich für eine nicht ausführbare Datei, wenn ‚ keine ausführbare Datei finden kann, sodass ‚ s “ OK “ (obwohl man in der Praxis lieber command -v wäre / type gibt einen Fehler zurück), da ‚ der Befehl ist, den es ausführen würde, wenn Sie mytestfile, aber das Verhalten dash ist fehlerhaft, als ob ‚ eine nicht ausführbare cmd vor einer ausführbaren Datei gibt command -v die nicht ausführbare Datei zurück, während die Ausführung von cmd die ausführbare Datei ausführt (die falsche man ist auch gehasht). FreeBSD sh (ebenfalls basierend auf ash) hat den gleichen Fehler. zsh, yash, ksh, mksh, bash as sh sind in Ordnung.

Antwort

Die Gründe, warum man kann which wurde nicht verwendet. Dies wurde bereits erläutert. Hier einige Beispiele für einige Systeme, bei denen which tatsächlich fehlschlägt.

Bei Bourne-ähnlichen Shells vergleichen wir die Ausgabe von which mit der Ausgabe von type (type Da es sich um eine eingebaute Shell handelt, soll dies die Grundwahrheit sein, da die Shell uns sagt, wie sie einen Befehl aufrufen würde.

Viele Fälle sind Eckfälle , aber denken Sie daran, dass which / type häufig in Eckfällen verwendet werden (um die Antwort zu finden zu einem unerwarteten Verhalten wie: Warum um alles in der Welt verhält sich dieser Befehl so, welches nenne ich? ).

Die meisten Systeme, die meisten Bourne-ähnlichen Shells: Funktionen

Der offensichtlichste Fall betrifft Funktionen:

$ type ls ls is a function ls () { [ -t 1 ] && set -- -F "$@"; command ls "$@" } $ which ls /bin/ls 

Der Grund dafür ist, dass which nur über ausführbare Dateien und manchmal über Aliase (obwohl nicht immer die von Ihrer Shell) berichtet und nicht funktioniert.

Die GNU, deren Manpage ein fehlerhaftes Beispiel hat (da sie vergessen haben, $@ zu zitieren), wie sie auch zum Melden von Funktionen verwendet wird, aber genau wie für Aliase, da es keinen Shell-Syntax-Parser implementiert, ist es leicht zu täuschen:

$ which() { (alias; declare -f) | /usr/bin/which --tty-only --read-alias --read-functions --show-tilde --show-dot "$@";} $ f() { echo $"\n}\ng ()\n{ echo bar;\n}\n" >> ~/foo; } $ type f f is a function f () { echo " } g () { echo bar; } " >> ~/foo } $ type g bash: type: g: not found $ which f f () { echo " } $ which g g () { echo bar; } 

Die meisten Systeme, die meisten Bourne-ähnlichen Shells: Builtins

Ein weiterer offensichtlicher Fall sind Buildins oder Schlüsselwörter, da which als externer Befehl nicht wissen kann, welche Builds Ihre Shell hat (und einige Shells wie zsh, bash oder ksh können eingebaute Elemente dynamisch laden):

$ type echo . time echo is a shell builtin . is a shell builtin time is a shell keyword $ which echo . time /bin/echo which: no . in (/bin:/usr/bin) /usr/bin/time 

(gilt nicht für zsh, in dem which eingebaut ist)

Solaris 10, AIX 7.1, HP / UX 11i, Tru64 5. 1 und viele andere:

$ csh % which ls ls: aliased to ls -F % unalias ls % which ls ls: aliased to ls -F % ksh $ which ls ls: aliased to ls -F $ type ls ls is a tracked alias for /usr/bin/ls 

Dies liegt daran, dass auf den meisten kommerziellen Unices which (wie in der ursprünglichen Implementierung) on 3BSD) ist ein csh -Skript, das ~/.cshrc liest. Die Aliase, die gemeldet werden, sind die dort definierten, unabhängig von den aktuell definierten Aliasen und unabhängig von der Shell, die Sie tatsächlich verwenden.

In HP / UX oder Tru64:

% echo "setenv PATH /bin:/usr/bin" >> ~/.cshrc % setenv PATH ~/bin:/bin:/usr/bin % ln -s /bin/ls ~/bin/ % which ls /bin/ls 

(Die Solaris- und AIX-Versionen haben dieses Problem behoben, indem sie $path gespeichert haben, bevor sie ~/.cshrc und wiederherstellen, bevor die Befehle nachgeschlagen werden)

$ type "a b" a b is /home/stephane/bin/a b $ which "a b" no a in /usr/sbin /usr/bin no b in /usr/sbin /usr/bin 

Oder:

$ d="$HOME/my bin" $ mkdir "$d"; PATH=$PATH:$d $ ln -s /bin/ls "$d/myls" $ type myls myls is /home/stephane/my bin/myls $ which myls no myls in /usr/sbin /usr/bin /home/stephane/my bin 

(Als csh -Skript können Sie natürlich nicht erwarten, dass es mit Argumenten funktioniert, die Leerzeichen enthalten …)

CentOS 6.4, bash

$ type which which is aliased to `alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde" $ alias foo=": "|test|"" $ which foo alias foo=": "|test|"" /usr/bin/test $ alias $"foo=\nalias bar=" $ unalias bar -bash: unalias: bar: not found $ which bar alias bar=" 

Auf diesem System ist systemweit ein Alias definiert, der den Befehl GNU which umschließt.

Die falsche Ausgabe liegt daran, dass which die Ausgabe von bash „s alias, weiß aber nicht, wie man es richtig analysiert, und verwendet Heuristiken (ein Alias pro Zeile sucht nach dem ersten gefundenen Befehl nach einem |, ;, & …)

Das Schlimmste unter CentOS ist, dass zsh hat Ein perfekter which eingebauter Befehl, aber CentOS hat es geschafft, ihn zu brechen, indem er durch einen nicht funktionierenden Alias für GNU which ersetzt wurde.

Debian 7.0, ksh93:

(gilt jedoch für die meisten Systeme mit vielen Shells)

$ unset PATH $ which which /usr/local/bin/which $ type which which is a tracked alias for /bin/which 

Unter Debian /bin/which ist ein /bin/sh -Skript. In meinem Fall ist sh dash, aber es ist dasselbe, wenn es bash.

Ein nicht gesetztes PATH soll die PATH Suche nicht deaktivieren, sondern die Verwendung des Systems „s Standardpfad , dem leider unter Debian niemand zustimmt (dash und bash haben /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin, zsh hat /bin:/usr/bin:/usr/ucb:/usr/local/bin, ksh93 hat /bin:/usr/bin, mksh hat /usr/bin:/bin ($(getconf PATH)), execvp() (wie in env) hat :/bin:/usr/bin (ja, schaut zuerst im aktuellen Verzeichnis!))

Aus diesem Grund macht which oben einen Fehler, da dash die Standard PATH unterscheidet sich von ksh93 „s

Es ist Nr t besser mit GNU which, das meldet:

which: no which in ((null)) 

(interessanterweise gibt es tatsächlich eine /usr/local/bin/which auf meinem System, bei dem es sich tatsächlich um ein akanga -Skript handelt, das mit akanga (ein Shell-Ableitung, wobei die Standardeinstellung PATH /usr/ucb:/usr/bin:/bin:.))

bash ist, jedes System:

Der eine , auf den sich Chris in seiner Antwort bezieht :

$ PATH=$HOME/bin:/bin $ ls /dev/null /dev/null $ cp /bin/ls bin $ type ls ls is hashed (/bin/ls) $ command -v ls /bin/ls $ which ls /home/chazelas/bin/ls 

Auch nach dem manuellen Aufruf von hash:

$ type -a which which is /usr/local/bin/which which is /usr/bin/which which is /bin/which $ hash -p /bin/which which $ which which /usr/local/bin/which $ type which which is hashed (/bin/which) 

Nun ein Fall, in dem which und manchmal type schlagen fehl:

$ mkdir a b $ echo "#!/bin/echo" > a/foo $ echo "#!/" > b/foo $ chmod +x a/foo b/foo $ PATH=b:a:$PATH $ which foo b/foo $ type foo foo is b/foo 

Nun mit einigen Shells:

$ foo bash: ./b/foo: /: bad interpreter: Permission denied 

Mit anderen:

$ foo a/foo 

Weder which noch type kann im Voraus wissen, dass b/foo nicht b e ausgeführt. Einige Shells wie bash, ksh oder yash beim Aufrufen von foo wird in der Tat versuchen, b/foo auszuführen und einen Fehler zu melden, während andere (wie zsh, ash, csh, Bourne, tcsh) wird ausgeführt a/foo beim Ausfall des Systemaufrufs execve() beim b/foo.

Kommentare

  • mksh verwendet tatsächlich etwas anderes für die Standardeinstellung $PATH: first wird die Kompilierungszeitkonstante des Betriebssystems _PATH_DEFPATH verwendet (am häufigsten auf den BSDs), dann wird confstr(_CS_PATH, …) (POSIX) verwendet. und wenn beide nicht existieren oder fehlschlagen, wird /bin:/usr/bin:/sbin:/usr/sbin verwendet.
  • In Ihrem ersten Beispiel, auch wenn ls ist eine Funktion, die verwendet wird g ls von PATH. Und which kann Ihnen gut sagen, welches verwendet wird /usr/bin/ls oder /usr/local/bin/ls. Ich sehe ‚ nicht “ Warum nicht welche “ ….
  • @rudimeier, dass which ls mir /bin/ls gibt, unabhängig davon, ob die ls ruft /bin/ls oder /opt/gnu/bin/ls oder dir oder gar nichts auf. IOW, which (das, was implementiert, IMMV) gibt etwas irrelevantes
  • @St é phaneChazelas. Nein nein Nein. Ich weiß bereits, dass meine ls eine Funktion ist. Ich weiß , dass meine Funktion ls ls von PATH. Jetzt teilt mir which mit, wo sich die Datei befindet. Sie sehen nur einen einzigen Anwendungsfall: “ Was würde meine Shell mit diesem Befehl tun. “ Für diesen Anwendungsfall which ist falsch, richtig.Es gibt jedoch auch andere Anwendungsfälle, in denen (GNU) which genau das Richtige ist.
  • @rudimeter, hängt von der which Implementierung. Einige werden Ihnen sagen, dass ‚ ein Alias ist (wenn Sie einen Alias konfiguriert haben oder wenn in Ihrem Haus ein ~/.cshrc vorhanden ist ein solcher Alias), einige geben Ihnen einen Weg, aber unter bestimmten Bedingungen den falschen. sh -c 'command -v ls', obwohl nicht perfekt, gibt Ihnen mit größerer Wahrscheinlichkeit die richtige Antwort auf diese unterschiedliche Anforderung (und ist auch Standard).

Antwort

Eine Sache, die Stephane (aus meiner kurzen Sicht) anscheinend nicht erwähnt hat, ist die, die which hat Keine Ahnung von der Pfad-Hash-Tabelle Ihrer Shell. Dies hat zur Folge, dass möglicherweise ein Ergebnis zurückgegeben wird, das nicht repräsentativ für das ist, was tatsächlich ausgeführt wird, was das Debuggen unwirksam macht.

Antwort

Ich erschrecke normalerweise, wenn diese Frage ahnungslosen Benutzern empfohlen wird, da unbegründetes Bashing auf which für niemanden nützlich ist.

Wenn which funktioniert gut und bietet die richtige Antwort auf eine Aufgabe, die dem Unix-Moto folgt: eine Sache tun, es gut machen , warum sollte which gesperrt werden?

Die Frage sollte dann sein, welche gut funktioniert und einen bestimmten Job gut erledigt?

Zum einen das externe Dienstprogramm unter / bin / which in Debian ist ein Shell-Skript, dessen Ziel es ist, nur ausführbare Dateien mit dem angegebenen Namen im Pfad aufzulisten. Ich glaube, dass which sein beabsichtigtes Ziel korrekt erreicht. Es lädt keine Aliase, keine Funktionen, nichts aus der Shell, sondern listet nur die ersten (oder alle) ausführbaren Dateien des angegebenen Namens im PATH auf. Die bedeutet , eine Datei mit demselben Namen wie angegeben gefunden zu haben, sollte der Benutzer von ihr (ihm) herausfinden. Selbst.

Ja, andere which -Implementierungen können (und normalerweise) bestimmte Probleme haben.

Antwort

Wir hören oft das, was vermieden werden sollte. Warum? Was sollen wir stattdessen verwenden?

Das habe ich noch nie gehört. Bitte geben Sie konkrete Beispiele an. Ich würde mir Sorgen um Ihre Linux-Distribution und die installierten Softwarepakete machen, da hier which herkommt!

SLES 11.4 x86-64

in tcsh Version 6.18.01:

> which which which: shell built-in command. 

in Bash-Version 3.2-147:

> which which /usr/bin/which > which -v GNU which v2.19, Copyright (C) 1999 - 2008 Carlo Wood. GNU which comes with ABSOLUTELY NO WARRANTY; This program is free software; your freedom to use, change and distribute this program is protected by the GPL. 

which ist Teil von util-linux Ein Standardpaket, das von der Linux Kernel Organization zur Verwendung als Teil des Linux-Betriebssystems verteilt wird. Es bietet auch diese anderen Dateien

/bin/dmesg /bin/findmnt /bin/logger /bin/lsblk /bin/more /bin/mount /bin/umount /sbin/adjtimex /sbin/agetty /sbin/blkid /sbin/blockdev /sbin/cfdisk /sbin/chcpu /sbin/ctrlaltdel /sbin/elvtune /sbin/fdisk /sbin/findfs /sbin/fsck /sbin/fsck.cramfs /sbin/fsck.minix /sbin/fsfreeze /sbin/fstrim /sbin/hwclock /sbin/losetup /sbin/mkfs /sbin/mkfs.bfs /sbin/mkfs.cramfs /sbin/mkfs.minix /sbin/mkswap /sbin/nologin /sbin/pivot_root /sbin/raw /sbin/sfdisk /sbin/swaplabel /sbin/swapoff /sbin/swapon /sbin/switch_root /sbin/wipefs /usr/bin/cal /usr/bin/chrp-addnote /usr/bin/chrt /usr/bin/col /usr/bin/colcrt /usr/bin/colrm /usr/bin/column /usr/bin/cytune /usr/bin/ddate /usr/bin/fallocate /usr/bin/flock /usr/bin/getopt /usr/bin/hexdump /usr/bin/i386 /usr/bin/ionice /usr/bin/ipcmk /usr/bin/ipcrm /usr/bin/ipcs /usr/bin/isosize /usr/bin/line /usr/bin/linux32 /usr/bin/linux64 /usr/bin/look /usr/bin/lscpu /usr/bin/mcookie /usr/bin/mesg /usr/bin/mkzimage_cmdline /usr/bin/namei /usr/bin/rename /usr/bin/renice /usr/bin/rev /usr/bin/script /usr/bin/scriptreplay /usr/bin/setarch /usr/bin/setsid /usr/bin/setterm /usr/bin/tailf /usr/bin/taskset /usr/bin/time /usr/bin/ul /usr/bin/uname26 /usr/bin/unshare /usr/bin/uuidgen /usr/bin/wall /usr/bin/whereis /usr/bin/which /usr/bin/write /usr/bin/x86_64 /usr/sbin/addpart /usr/sbin/delpart /usr/sbin/fdformat /usr/sbin/flushb /usr/sbin/freeramdisk /usr/sbin/klogconsole /usr/sbin/ldattach /usr/sbin/partx /usr/sbin/rcraw /usr/sbin/readprofile /usr/sbin/rtcwake /usr/sbin/setctsid /usr/sbin/tunelp 

meine util-linux ist Version 2.19. Versionshinweise können leicht auf Version 2.13 vom 28. August 2007 zurückgeführt werden. Ich bin mir nicht sicher, was der Sinn oder das Ziel davon war, aber es wurde sicherlich nicht in dieser langen Sache beantwortet, die 331-mal positiv bewertet wurde.

Kommentare

  • Beachten Sie, wie die Die Frage erwähnt nicht, auf welches Unix sie sich bezieht. Linux ist nur eines von wenigen.
  • Wie Ihre which -v zeigt, ist die ‚ die GNU, die (die Extravagante) eine, die in der anderen Antwort erwähnt wurde und in keiner Weise spezifisch für Linux ist), nicht util-linux, zu dem AFAIK niemals ein which -Dienstprogramm enthielt. util-linux 2.19 stammt aus dem Jahr 2011, GNU 2.19 aus dem Jahr 2008.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.