Qual è lo scopo del comando hash?


118

Se lo esegui hashmostra il percorso di tutti i comandi eseguiti dall'ultimo reset dell'hash ( 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

Secondo le pagine man, lo scopo dell'hash è:

L'utility / usr / bin / hash influenza il modo in cui l'ambiente shell corrente ricorda le posizioni delle utility trovate. A seconda degli argomenti specificati, aggiunge le posizioni delle utilità al suo elenco di posizioni memorizzate oppure elimina i contenuti dell'elenco. Quando non viene specificato alcun argomento, viene riportato il contenuto dell'elenco. L' -ropzione fa sì che la shell dimentichi tutte le posizioni memorizzate.

Le utilità fornite come incorporate nella shell non vengono riportate dall'hash.

Oltre a vedere quante volte ho inserito un comando, non riesco a vedere l'utilità di hash.

È stato anche descritto nei primi 15 utili comandi di thegeekstuff.com

In che modo è hashutile?

Risposte:


97

hashè un comando incorporato bash. La tabella hash è una funzionalità bash che gli impedisce di cercare $PATHogni volta che si digita un comando memorizzando nella cache i risultati. La tabella viene cancellata dagli eventi che ovviamente invalidano i risultati (come la modifica $PATH)

Il hashcomando è proprio il modo in cui interagisci con quel sistema (per qualsiasi motivo ti senti necessario).

Alcuni casi d'uso:

  • Come hai visto, stampa quante volte premi quali comandi se lo digiti senza argomenti. Questo potrebbe dirti quali comandi usi più spesso.

  • Puoi anche usarlo per ricordare gli eseguibili in posizioni non standard.

Esempio:

[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 ~]#

Il che potrebbe essere utile se hai solo un singolo eseguibile in una directory esterna a $PATHquella che vuoi eseguire semplicemente digitando il nome invece di includere tutto in quella directory (che sarebbe l'effetto se lo aggiungessi a $PATH).

Un alias di solito può fare anche questo, anche se e poiché stai modificando il comportamento della shell corrente, non è mappato nei programmi che avvii. Un collegamento simbolico all'eseguibile solitario è probabilmente l'opzione preferibile qui. hashè un modo per farlo.

  • Puoi usarlo per non ricordare i percorsi dei file. Questo è utile se un nuovo eseguibile appare in una PATHdirectory precedente o arriva mvda qualche altra parte e vuoi forzare l'uscita di bash e trovarlo di nuovo invece dell'ultimo posto in cui ricorda di averlo trovato.

Esempio:

[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 ~]#

Il cpcomando ha causato la visualizzazione di una nuova versione lsdell'eseguibile in precedenza $PATHma non ha attivato l'eliminazione della tabella hash. Ho usato hash -dper eliminare selettivamente la voce lsdalla tabella hash. Bash è stato quindi costretto a controllare di $PATHnuovo e quando lo ha fatto, lo ha trovato nella posizione più recente (prima in $ PATH di quanto non fosse in esecuzione prima).

È possibile richiamare selettivamente questo $PATHcomportamento "trova nuova posizione dell'eseguibile da ", sebbene:

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

Avresti principalmente voluto farlo se volevi qualcosa fuori dalla tabella hash e non eri al 100% che potresti disconnetterti e poi rientrare con successo, o se volevi conservare alcune modifiche che hai apportato alla tua shell.

Per sbarazzarsi delle mappature obsolete, puoi anche fare hash -r(o export PATH=$PATH) che effettivamente elimina l'intera tabella hash di bash.

Ci sono molte piccole situazioni del genere. Non so se lo definirei uno dei comandi "più utili" ma ha alcuni casi d'uso.


Mi chiedo da dove venga il nome "hash". Che dire di "cache", quindi? :).
Michael,

2
@Michael Perché il hashcomando utilizza internamente una tabella hash per memorizzare i mapping. en.wikipedia.org/wiki/Hash_table
jlliagre

10
Va notato che hashnon è bashspecifico, il comando ha avuto origine nella shell Bourne in SVR2 (anche se la funzionalità dei percorsi di hashing dei comandi proviene da cshquella precedente) e si trova in tutte le shell tipo Bourne e POSIX.
Stéphane Chazelas,

1
Invece di export PATH=$PATHcancellare la tabella, hash -rdovrebbe essere sufficiente.
Ravron,

1
Un altro caso d'uso è quando si installa una seconda copia di un programma in una parte precedente di $ PATH. Devi hash -ro otterrai la vecchia versione perché $ PATH non è cambiato e quindi Bash non si rende conto che potrebbe caricare lo stesso programma da una directory precedente (priorità più alta). Vedi conda.pydata.org/docs/… per i dettagli.
John Zwinck,

37

Ecco l'uso classico, semplificato:

# 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.

Come notato qui, l'aggiornamento selettivo della tabella hash può essere richiamato con un singolo comando hash hello.
Ioannis Filippidis,

@johntex: ok, cambiato.
rici,

Immagina il potenziale per strani bug prima di sapere che esiste la tabella hash! Esiste un elenco di circostanze in cui la tabella hash viene aggiornata automaticamente?
benjimin,

@benji: non viene mai aggiornato automaticamente (nel suo insieme). Se si esegue bash in modalità posix o se setopt -s checkhashl'eseguibile con hash per un comando non esiste più, la voce di hash per quel comando verrà aggiornata. Ma nota che ogni sessione bash aveva la sua tabella hash, quindi chiudendo la sessione e iniziando una nuova si svuota efficacemente l'hash. ( hash -rè un modo più semplice per farlo.)
rici

L'aggiornamento $PATHo l'avvio di un nuovo terminale bash sembrano entrambi ripulire la tabella.
benjimin,

17

Ecco un utile utilizzo di hash:

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

Significa: se php non è nel PERCORSO, usa

/usr/local/foobar/php/bin/

8

Sì, il Manuale di riferimento di Bash dice:

Una ricerca completa delle directory in $ PATH viene eseguita solo se il comando non viene trovato nella tabella hash.

Ma puoi disabilitare l'hash con set +h:

-h - Individua e ricorda i comandi (hash) mentre vengono cercati per l'esecuzione. Questa opzione è abilitata per impostazione predefinita.

Provare:

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

Lo stesso vale per hash -r, hash NAMEecc

Un "rilevamento comandi" (come questo o quello ) non funziona:

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

Puoi scrivere qualcosa del genere:

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

o (grazie a @mikeserv) senza dover assegnare nuove variabili o fare test:

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

1
Per quanto old_optionsti riguarda, di solito faccio qualcosa del genere: set -h -- "-${-:--}" "$@"; hash ...; set +h "$@"quindi tutto si adatta automaticamente senza dover assegnare nuove variabili o fare test o altro.
Mikeserv,

5

Rilevazione semplice della disponibilità di un comando:

CMD=gzip
if hash bzip2; then
    CMD=$_
fi
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.