Wie funktioniert die Suche in $ PATH unter der Haube?

8

Im moment gibt es viel zu viele Artikel / Ressourcen im Internet , die Menschen lehrt , WIE auf die Umgebungsvariable , PATHso dass sie die kurze Hand verwenden können , javaoder pythonusw. statt des absoluten Pfad in Kommandozeilen - Schnittstelle.

Ich bin daran interessiert zu wissen, was sich hinter den Kulissen befindet, wenn wir den Befehl eingeben und die Eingabetaste drücken (ähnlich wie beim Eingeben einer URL im Browser ).

Hier ist meine Vermutung:

  1. Lesen Sie den Befehl (pard / preprocess stdin, um die richtigen Argumente zu erhalten $@)
  2. Befehlssuche
  3. Befehlsausführung (Programm gestartet, Speicher verbrauchen, stdout / stderr zur Shell)
  4. wieder machen den Emulator durch relevante Umgebungsvariablen (z $PS#, $PROMPTusw.)

Der Teil, den ich am meisten herausfinden möchte, ist die Befehlssuche. Offensichtlich $PATHwird das von einer Hintergrundfunktion verbraucht und durch :/ ;als Begrenzer getrennt. Was ist dann passiert? Verwenden wir eine Hash-Tabelle (Schlüssel: Basisname der Datei, Wert: absoluter Verzeichnisname der Datei), um die Binärdateien unter diesen PATHs oder anderen Hooks zu speichern?

HINWEIS: Ich dachte ursprünglich, es sei eine Hash-Tabelle, mit der ich [ -z hash [command] ]überprüfen kann , ob ein Befehl in der aktuellen Umgebung verfügbar ist, aber wenn ich ihn verwende, hash | grep pythonerhalte ich nichts von der Ausgabe, während ich which pythonwie erwartet arbeite. (Ich denke, der Mechanismus ist möglicherweise Shell-spezifisch, aber ich möchte mehr Einblicke in ihn erhalten.)

Xlee
quelle

Antworten:

11

Wie Sie vermuten, hängt das genaue Verhalten von der Shell ab, POSIX legt jedoch eine grundlegende Funktionalität fest.

Die Befehlssuche und -ausführung für die Standard-Shell-Befehlssprache (von der die meisten Shells eine Obermenge implementieren) hat viele Fälle, aber wir sind im Moment nur an dem Fall interessiert, in dem sie PATHverwendet wird. In diesem Fall:

Der Befehl muss mithilfe der Umgebungsvariablen PATH durchsucht werden, wie unter XBD-Umgebungsvariablen beschrieben

und

Wenn die Suche erfolgreich ist:

[...]

die Schale führt die Anwendung in einer unterschiedlichen Gebrauchsumgebung mit Aktionen , die äquivalent zu nennen execl()Funktion [...] mit dem Pfad Argument Satz zu dem Pfadnamen aus der Suche ergibt.

Im erfolglosen Fall schlägt die Ausführung fehl und ein Exit-Code von 127 wird mit einer Fehlermeldung zurückgegeben.

Dieses Verhalten stimmt insbesondere mit der execvpFunktion überein . Alle exec*Funktionen akzeptieren den Dateinamen eines auszuführenden Programms, eine Folge von Argumenten (die argvdas Programm sein werden) und möglicherweise eine Reihe von Umgebungsvariablen. Für die Versionen, die PATHLookup verwenden, definiert POSIX Folgendes :

Die Argumentdatei wird verwendet, um einen Pfadnamen zu erstellen , der die neue Prozessabbilddatei [...] identifiziert. Das Pfadpräfix für diese Datei wird durch Durchsuchen der als Umgebungsvariable PATH übergebenen Verzeichnisse ermittelt


Das Verhalten von PATH wird an anderer Stelle definiert als:

Diese Variable muss die Folge von Pfadpräfixen darstellen, die bestimmte Funktionen und Dienstprogramme bei der Suche nach einer ausführbaren Datei anwenden, die nur unter einem Dateinamen bekannt ist. Die Präfixe werden durch ein <colon> (':') getrennt. Wenn auf diesen Dateinamen ein Präfix ungleich Null angewendet wird, wird zwischen dem Präfix und dem Dateinamen ein <Slash> eingefügt, wenn das Präfix nicht mit endet. Ein Präfix mit der Länge Null ist eine Legacy-Funktion, die das aktuelle Arbeitsverzeichnis angibt. Es wird als zwei benachbarte Zeichen ("::") angezeigt, als erstes <colon> vor dem Rest der Liste oder als nachfolgendes <colon> nach dem Rest der Liste. Eine streng konforme Anwendung muss einen tatsächlichen Pfadnamen (z. B..) Verwenden, um das aktuelle Arbeitsverzeichnis in PATH darzustellen.Die Liste wird von Anfang bis Ende durchsucht, wobei der Dateiname auf jedes Präfix angewendet wird, bis eine ausführbare Datei mit dem angegebenen Namen und den entsprechenden Ausführungsberechtigungen gefunden wird . Wenn der gesuchte Pfadname einen <Slash> enthält, wird die Suche durch die Pfadpräfixe nicht durchgeführt. Wenn der Pfadname mit einem <Slash> beginnt, wird der angegebene Pfad aufgelöst (siehe Pfadnamenauflösung ). Wenn PATH nicht gesetzt oder auf null gesetzt ist, ist die Pfadsuche implementierungsdefiniert.

Das ist ein bisschen dicht, also eine Zusammenfassung:

  1. Wenn der Programmname einen /(Schrägstrich, U + 002F SOLIDUS) enthält, behandeln Sie ihn wie gewohnt als Pfad und überspringen Sie den Rest dieses Vorgangs. Für die Shell tritt dieser Fall technisch nicht auf (da sich die Shell-Regeln bereits damit befasst haben).
  2. Der Wert von PATHwird an jedem Doppelpunkt in Stücke aufgeteilt und dann jede Komponente von links nach rechts verarbeitet. Als spezieller (historischer) Fall wird eine leere Komponente einer nicht leeren Variablen als .(das aktuelle Verzeichnis) behandelt.
  3. Für jede Komponente wird der Programmname mit einer Verknüpfung an das Ende angehängt, /und das Vorhandensein einer Datei mit diesem Namen wird überprüft. Wenn eine vorhanden ist, werden auch gültige Ausführungsberechtigungen (+ x) überprüft. Wenn eine dieser Prüfungen fehlschlägt, fährt der Prozess mit der nächsten Komponente fort. Andernfalls wird der Befehl in diesen Pfad aufgelöst und die Suche abgeschlossen.
  4. Wenn Ihnen die Komponenten ausgehen, schlägt die Suche fehl.
  5. Wenn nichts PATHdrin ist oder es nicht existiert, mach was du willst.

Echte Shells verfügen über integrierte Befehle, die vor dieser Suche gefunden werden, und häufig auch über Aliase und Funktionen. Die interagieren nicht mit PATH. POSIX definiert einige Verhaltensweisen in Bezug auf diese , und Ihre Shell kann viel mehr haben.


Während es möglich ist, sich auf exec*für Sie die meisten , dies zu tun, kann die Schale in die Praxis umzusetzen diese Lookup selbst, insbesondere Zwecke für das Caching, aber die leeren Cache - Verhalten ähnlich sein sollte. Muscheln haben hier einen ziemlich weiten Spielraum und in den Eckfällen ein subtil unterschiedliches Verhalten.

Wie Sie festgestellt haben, verwendet Bash eine Hash-Tabelle , um die vollständigen Pfade der zuvor angezeigten Befehle zu speichern, und auf diese Tabelle kann mit der hashFunktion zugegriffen werden. Wenn Sie zum ersten Mal einen Befehl ausführen, der durchsucht wird, und wenn ein Ergebnis gefunden wird, wird es der Tabelle hinzugefügt, sodass Sie beim nächsten Versuch nicht mehr nachsehen müssen.

In zsh hingegen wird im PATHAllgemeinen nach dem vollständigen Wert gesucht, wenn die Shell gestartet wird. Eine Nachschlagetabelle wird mit allen erkannten Befehlsnamen vorab ausgefüllt, sodass Laufzeitsuchen normalerweise nicht erforderlich sind (es sei denn, ein neuer Befehl wird hinzugefügt). Sie können dies feststellen, wenn Sie versuchen, einen Befehl zu vervollständigen, der zuvor nicht vorhanden war.

Sehr leichte Shells dashdelegieren in der Regel so viel Verhalten wie möglich an die Systembibliothek und müssen sich nicht an frühere Befehlspfade erinnern.

Michael Homer
quelle
Vielen Dank für diese detaillierte Erklärung, die wirklich tiefe Einblicke gibt. Ihr Vergleich PATHzwischen bashund zshhilft mir, meine Verwirrung zu lösen!
Xlee