GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Warum nicht „welche“ verwenden? Was dann verwenden?

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…)).

Verwandte:Debian – Wie verwende ich proprietäre drahtlose Treiber während der Debian-USB-Installation?

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 zum execve Systemaufruf kommt nicht mit einem Fehler zurück. Wenn beispielsweise $PATH enthält /foo:/bar und Sie ls 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 , aber ls 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 gibt foo zurück . Mit einigen Shells wie ash , pdksh oder zsh , es kann auch foo zurückgeben wenn $PATH enthält den leeren String und es gibt ein ausführbares foo 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 Beispiel mount ist manchmal für busybox sh eingebaut ) und zum Beispiel bash 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 Sie command -v ausführen ist nach dem cd 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 …), sondern command -v chmod gibt /opt/ast/bin/chmod zurück auch wenn dieser Pfad nicht existiert.
Verwandte:Dd vs. Katze – ist dd heutzutage noch relevant?

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 oder echo=$commands[echo] oder echo=${${:-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.


Linux
  1. Howto:Was ist Git und Github? Wie verwende ich es und warum sollte es mich interessieren?

  2. Warum ist Cd kein Programm?

  3. Was sollte man nicht auf eine SSD setzen?

  4. Warum ist Bash überall (in den meisten, wenn nicht allen Linux-Distributionen)?

  5. Warum ist SUID für Shell-Skripte deaktiviert, aber nicht für Binärdateien?

Was ist die Shell unter Linux?

Was ist eine virtuelle Maschine und warum sollte man sie verwenden?

Was sind Firefox Multi-Account-Container? Warum und wie wird es verwendet?

Was ist die Login-Shell in Linux?

Was ist die ONLYOFFICE Community-Funktion und warum sollten Sie sie verwenden?

Was ist eine .sh-Datei?