Was ist der Zweck des Hash-Befehls?


118

Wenn Sie ausführen hash, wird der Pfad aller Befehle angezeigt, die seit dem letzten Zurücksetzen des Hash ausgeführt wurden ( hash -r).

[root@c04c ~]# hash
hash: hash table empty

[root@c04c ~]# whoami
root

[root@c04c ~]# hash
hits    command
   1    /usr/bin/whoami

[root@c04c ~]# whoami
root

[root@c04c ~]# hash
hits    command
   2    /usr/bin/whoami

Nach den Manpages ist der Zweck von Hash:

Das Dienstprogramm / usr / bin / hash wirkt sich darauf aus, wie sich die aktuelle Shell-Umgebung die Speicherorte der gefundenen Dienstprogramme merkt. Abhängig von den angegebenen Argumenten werden der Liste der gespeicherten Speicherorte Speicherorte für Hilfsprogramme hinzugefügt oder der Inhalt der Liste gelöscht. Wenn keine Argumente angegeben sind, wird über den Inhalt der Liste berichtet. Die -rOption bewirkt, dass die Shell alle gespeicherten Positionen vergisst.

Dienstprogramme, die als integrierte Komponenten für die Shell bereitgestellt werden, werden von Hash nicht gemeldet.

Abgesehen davon, wie oft ich einen Befehl eingegeben habe, kann ich das Dienstprogramm von nicht sehen hash.

Es war sogar in den 15 nützlichsten Befehlen von thegeekstuff.com enthalten

Inwiefern ist das hashsinnvoll?

Antworten:


97

hashist ein in Bash integrierter Befehl. Die Hash-Tabelle ist eine Funktion bash , die verhindert, dass bei $PATHjeder Eingabe eines Befehls eine Suche durchgeführt werden muss, indem die Ergebnisse im Speicher zwischengespeichert werden. Die Tabelle wird bei Ereignissen gelöscht, die die Ergebnisse offensichtlich ungültig machen (z. B. Ändern $PATH).

Der hashBefehl ist nur, wie Sie mit diesem System interagieren (aus welchem ​​Grund Sie das Gefühl haben, dass Sie es brauchen).

Einige Anwendungsfälle:

  • Wie Sie gesehen haben, gibt es aus, wie oft Sie welche Befehle gedrückt haben, wenn Sie es ohne Argumente eingeben. Hier erfahren Sie möglicherweise, welche Befehle Sie am häufigsten verwenden.

  • Sie können es auch verwenden, um ausführbare Dateien an nicht standardmäßigen Speicherorten zu speichern.

Beispiel:

[root@policyServer ~]# hash -p /lol-wut/whoami whoami
[root@policyServer ~]# whoami
Not what you're thinking
[root@policyServer ~]# which whoami
/usr/bin/whoami
[root@policyServer ~]# /usr/bin/whoami
root
[root@policyServer ~]#

Dies kann nützlich sein, wenn Sie nur eine einzige ausführbare Datei in einem Verzeichnis außerhalb des $PATHgewünschten Verzeichnisses haben, indem Sie einfach den Namen eingeben, anstatt alles in dieses Verzeichnis aufzunehmen (was der Effekt wäre, wenn Sie es hinzufügen $PATH).

Ein Alias ​​kann dies normalerweise auch, und da Sie das Verhalten der aktuellen Shell ändern, wird es nicht in von Ihnen gestarteten Programmen abgebildet. Ein Symlink zur einzelnen ausführbaren Datei ist hier wahrscheinlich die bevorzugte Option. hashist ein Weg, es zu tun.

  • Sie können es verwenden, um Dateipfade nicht mehr zu merken. Dies ist nützlich, wenn eine neue ausführbare Datei in einem früheren PATHVerzeichnis auftaucht oder mvan eine andere Stelle gelangt und Sie möchten, dass Bash herauskommt und sie wieder findet, anstatt an der Stelle, an der sie sich zuletzt daran erinnert, sie zu finden.

Beispiel:

[root@policyServer ~]# hash
hits    command
   1    /bin/ls
[root@policyServer ~]# cp /bin/ls /lol-wut
[root@policyServer ~]# hash
hits    command
   1    /bin/cp
   1    /bin/ls
[root@policyServer ~]# hash -d ls
[root@policyServer ~]# ls
default.ldif  newDIT.ldif  notes.txt  users.ldif
[root@policyServer ~]# hash
hits    command
   1    /bin/cp
   1    /lol-wut/ls
[root@policyServer ~]#

Der cpBefehl führte dazu, dass eine neue Version der lsausführbaren Datei früher in my $PATHangezeigt wurde, löschte jedoch nicht die Hash-Tabelle. Früher habe ich hash -dden Eintrag für selektiv lsaus der Hash-Tabelle entfernt. Bash wurde dann gezwungen, $PATHerneut zu suchen , und als es dies tat, fand es es an der neueren Position (früher in $ PATH als zuvor ausgeführt).

Sie können dieses $PATHVerhalten "Neuen Speicherort der ausführbaren Datei finden von " jedoch selektiv aufrufen :

[root@policyServer ~]# hash
hits    command
   1    /bin/ls
[root@policyServer ~]# hash ls
[root@policyServer ~]# hash
hits    command
   0    /lol-wut/ls
[root@policyServer ~]#

Meistens möchten Sie dies nur tun, wenn Sie möchten, dass sich etwas aus der Hash-Tabelle entfernt und nicht zu 100% abgemeldet und dann wieder erfolgreich angemeldet wird, oder wenn Sie einige Änderungen beibehalten möchten, die Sie an Ihrer Shell vorgenommen haben.

Um veraltete Zuordnungen zu entfernen, können Sie auch hash -r(oder export PATH=$PATH) die gesamte Hash-Tabelle von bash löschen.

Es gibt viele kleine Situationen wie diese. Ich weiß nicht, ob ich es als einen der "nützlichsten" Befehle bezeichnen würde, aber es gibt einige Anwendungsfälle.


Ich frage mich, woher der Name "Hash" kommt. Was ist dann zB mit "Cache"? :).
Michael

2
@Michael Da der hashBefehl intern eine Hash-Tabelle zum Speichern der Zuordnungen verwendet. en.wikipedia.org/wiki/Hash_table
jlliagre

10
Es sollte beachtet werden, dass dies hashnicht bashspezifisch ist. Der Befehl hat seinen Ursprung in der Bourne-Shell in SVR2 (obwohl die Funktion der Hashing-Pfade von Befehlen cshdavor stammt ) und ist in allen Bourne-ähnlichen und POSIX-Shells zu finden.
Stéphane Chazelas

1
Anstatt export PATH=$PATHden Tisch hash -rabzuräumen , sollte das ausreichen.
Ravron

1
Ein weiterer Anwendungsfall ist die Installation einer zweiten Kopie eines Programms in einem früheren Teil Ihres $ PATH. Sie müssen hash -roder Sie werden die alte Version bekommen, weil sich $ PATH nicht geändert hat und Bash nicht merkt, dass es dasselbe Programm aus einem früheren Verzeichnis (mit höherer Priorität) laden kann. Weitere Informationen finden Sie unter conda.pydata.org/docs/… .
John Zwinck

37

Hier ist die klassische Verwendung, vereinfacht:

# My PATH contains /home/rici/bin as well as the Usual Suspects:
# (the real one has lots more)
$ echo $PATH
/home/rici/bin:/usr/local/bin:/usr/bin:/bin

# I've installed a program called hello in /usr/local/bin
$ $ cat /usr/local/bin/hello
#!/bin/bash

echo Hello, world. I live at $0

# The program works.
$ hello
Hello, world. I live at /usr/local/bin/hello

# Now I want to create a better hello, just for me. I put it in
# my own bin directory, and according to my PATH, it should come first.
$ cp /usr/local/bin/hello ~/bin/hello

# So now I will try running it
$ hello
Hello, world. I live at /usr/local/bin/hello

# WTF? Oh, forgot to run hash.
# Tell bash to update where to look for hello
$ hash hello
$ hello
Hello, world. I live at /home/rici/bin/hello

# Ah, all is well.

Wie hier erwähnt, kann die selektive Aktualisierung der Hash-Tabelle mit einem einzigen Befehl aufgerufen werden hash hello.
Ioannis Filippidis

@johntex: ok, geändert.
rici

Stellen Sie sich das Potenzial für seltsame Fehler vor, bevor Sie wissen, dass die Hash-Tabelle existiert! Gibt es eine Liste von Umständen, unter denen die Hash-Tabelle automatisch aktualisiert wird?
28.

@benji: Es wird nie automatisch aktualisiert (als Ganzes). Wenn Sie bash im posix-Modus ausführen oder setopt -s checkhashdie ausführbare Hash-Datei für einen Befehl nicht mehr vorhanden ist, wird der Hash-Eintrag für diesen Befehl aktualisiert. Beachten Sie jedoch, dass jede Bash-Sitzung eine eigene Hash-Tabelle hatte. Wenn Sie also die Sitzung schließen und eine neue starten, wird der Hash effektiv geleert. ( hash -rist ein einfacher Weg, das zu tun.)
rici

Durch das Aktualisieren $PATHoder Starten eines neuen Bash-Terminals wird die Tabelle anscheinend gelöscht.
Benjamin

17

Hier ist eine nützliche Verwendung von hash:

hash php 2> /dev/null || hash -p /usr/local/foobar/php/bin/php php 2> /dev/null

Es bedeutet: Wenn PHP nicht im PATH ist, dann benutze

/usr/local/foobar/php/bin/

8

Ja, im Bash-Referenzhandbuch heißt es:

Eine vollständige Suche in den Verzeichnissen in $ PATH wird nur durchgeführt, wenn der Befehl nicht in der Hash-Tabelle gefunden wird.

Sie können das Hashing jedoch deaktivieren mit set +h:

-h - Suchen und merken Sie sich (Hash-) Befehle, während sie zur Ausführung gesucht werden. Diese Option ist standardmäßig aktiviert.

Versuchen:

set +h
hash # prints bash: hash: hashing disabled
echo $? # prints 1

Das gleiche gilt für hash -r, hash NAMEetc

Eine "Befehlserkennung" ( so oder so ) funktioniert nicht:

set -h
hash ls >/dev/null 2>&1 || echo "Please install ls" >&2 # prints nothing

set +h
hash ls >/dev/null 2>&1 || echo "Please install ls" >&2 # prints Please install ls

Sie können so etwas schreiben:

old_options="$-"
set -h
hash ls >/dev/null 2>&1 || echo "Please install ls" >&2
[[ "$old_options" =~ "h" ]] || set +h

oder (danke an @mikeserv) ohne neue Variablen zuweisen oder Tests durchführen zu müssen:

set -h -- "-${-:--}" "$@"
hash ls >/dev/null 2>&1 || echo "Please install ls" >&2
set +h "$@"

1
Für Ihr old_optionsDing - ich mache normalerweise so etwas wie das: set -h -- "-${-:--}" "$@"; hash ...; set +h "$@"Es passt einfach alles automatisch, ohne dass Sie neue Variablen zuweisen oder Tests durchführen müssen oder was auch immer.
mikeserv

5

Einfache Erkennung, ob ein Befehl verfügbar ist:

CMD=gzip
if hash bzip2; then
    CMD=$_
fi
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.