¿Cuál es el propósito del comando hash?

118

Si lo ejecuta hash, muestra la ruta de todos los comandos ejecutados desde el último restablecimiento del 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

Según las páginas del manual, el propósito del hash es:

La utilidad / usr / bin / hash afecta la forma en que el entorno actual del shell recuerda las ubicaciones de las utilidades encontradas. Dependiendo de los argumentos especificados, agrega ubicaciones de utilidad a su lista de ubicaciones recordadas o purga el contenido de la lista. Cuando no se especifican argumentos, informa sobre el contenido de la lista. La -ropción hace que el shell olvide todas las ubicaciones recordadas.

El hash no informa de las utilidades proporcionadas como integradas al shell.

Aparte de ver cuántas veces he ingresado un comando, no puedo ver la utilidad de hash.

Incluso apareció en los 15 comandos útiles principales de thegeekstuff.com

¿De qué maneras es hashútil?

Spuder
fuente

Respuestas:

97

hashes un comando incorporado bash. La tabla hash es una característica bash que evita que tenga que buscar $PATHcada vez que escribe un comando almacenando en caché los resultados en la memoria. La tabla se borra en eventos que obviamente invalidan los resultados (como modificar $PATH)

El hashcomando es solo cómo interactúa con ese sistema (por cualquier razón que sienta que necesita).

Algunos casos de uso:

  • Como viste, imprime cuántas veces tocas qué comandos si lo escribes sin argumentos. Esto podría decirle qué comandos usa con más frecuencia.

  • También puede usarlo para recordar ejecutables en ubicaciones no estándar.

Ejemplo:

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

Lo que podría ser útil si solo tiene un único ejecutable en un directorio fuera del $PATHque desea ejecutar simplemente escriba el nombre en lugar de incluir todo en ese directorio (lo que sería el efecto si lo agregara $PATH).

Sin embargo, un alias generalmente también puede hacer esto, y dado que está modificando el comportamiento del shell actual, no está mapeado en los programas que inicia. Un enlace simbólico al ejecutable solitario es probablemente la opción preferible aquí. hashEs una forma de hacerlo.

  • Puede usarlo para no recordar rutas de archivos. Esto es útil si un nuevo ejecutable aparece en un PATHdirectorio anterior o llega mva otro lugar y desea forzar a bash a salir y encontrarlo nuevamente en lugar del último lugar donde recuerda haberlo encontrado.

Ejemplo:

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

El cpcomando provocó que una nueva versión del lsejecutable apareciera antes en mi $PATHpero no activó una purga de la tabla hash. Solía hash -dpurgar selectivamente la entrada lsde la tabla hash. Bash se vio obligado a mirar de $PATHnuevo y, cuando lo hizo, lo encontró en la ubicación más nueva (antes en $ PATH que antes).

Sin $PATHembargo, puede invocar selectivamente este comportamiento "buscar nueva ubicación del ejecutable desde ":

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

En su mayoría, solo querría hacer esto si deseaba algo fuera de la tabla hash y no era 100% posible que pudiera cerrar sesión y luego volver a ingresar con éxito, o si desea conservar algunas modificaciones que ha realizado en su shell.

Para deshacerse de las asignaciones obsoletas, también puede hacer hash -r(o export PATH=$PATH) lo que efectivamente purga toda la tabla hash de bash.

Hay muchas pequeñas situaciones como esa. No sé si lo llamaría uno de los comandos "más útiles", pero tiene algunos casos de uso.

Bratchley
fuente
Me pregunto de dónde viene el nombre 'hash'. ¿Qué pasa por ejemplo, "caché", entonces? :).
Michael
2
@Michael Porque el hashcomando usa internamente una tabla hash para almacenar las asignaciones. es.wikipedia.org/wiki/Hash_table
jlliagre
10
Cabe señalar que hashno es bashespecífico, el comando se originó en el shell Bourne en SVR2 (aunque la característica de rutas de hashing de comandos proviene de csheso antes) y se encuentra en todos los shells similares a Bourne y POSIX.
Stéphane Chazelas
1
En lugar de export PATH=$PATHlimpiar la mesa, hash -rdebería ser suficiente.
ravron
1
Otro caso de uso es cuando instala una segunda copia de un programa en una parte anterior de su $ PATH. Necesita hash -ro obtendrá la versión anterior porque $ PATH no cambió y, por lo tanto, Bash no se da cuenta de que podría cargar el mismo programa desde un directorio anterior (de mayor prioridad). Ver conda.pydata.org/docs/… para más detalles.
John Zwinck
37

Aquí está el uso clásico, simplificado:

# 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.
rici
fuente
Como se señaló aquí, la actualización selectiva de la tabla hash se puede invocar con un solo comando hash hello.
Ioannis Filippidis
@johntex: ok, cambiado.
rici
¡Imagine el potencial de errores extraños antes de saber que existe la tabla hash! ¿Hay una lista de circunstancias cuando la tabla hash se actualiza automáticamente?
benjimin
@benji: nunca se actualiza automáticamente (en general). Si ejecuta bash en modo posix o setopt -s checkhashy el ejecutable hash para un comando ya no existe, la entrada hash para ese comando se actualizará. Pero tenga en cuenta que cada sesión bash tenía su propia tabla hash, por lo que cerrar la sesión y comenzar una nueva efectivamente vacía el hash. ( hash -res una manera más fácil de hacer eso).
Rici
Al actualizar $PATHo iniciar un nuevo terminal de bash, ambos parecen limpiar la tabla.
benjimin
17

Aquí hay un uso útil de hash:

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

Significa: si php no está en la RUTA, entonces use

/usr/local/foobar/php/bin/
Gilles Quenot
fuente
8

Sí, el Manual de referencia de Bash dice:

Se realiza una búsqueda completa de los directorios en $ PATH solo si el comando no se encuentra en la tabla hash.

Pero puede deshabilitar el hash con set +h:

-h - Localiza y recuerda comandos (hash) a medida que se buscan para su ejecución. Esta opción está activada de forma predeterminada.

Tratar:

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

Lo mismo es para hash -r, hash NAMEetc.

Una "detección de comando" (como esto o aquello ) no funciona:

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

Puedes escribir algo como esto:

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

o (gracias a @mikeserv) sin tener que asignar ninguna variable nueva ni hacer ninguna prueba:

set -h -- "-${-:--}" "$@"
hash ls >/dev/null 2>&1 || echo "Please install ls" >&2
set +h "$@"
Evgeny Vereshchagin
fuente
1
Para su old_optionscosa, generalmente hago algo como esto: set -h -- "-${-:--}" "$@"; hash ...; set +h "$@"por lo que todo encaja automáticamente sin tener que asignar ninguna variable nueva o hacer ninguna prueba o lo que sea.
mikeserv
5

Detección fácil si un comando está disponible:

CMD=gzip
if hash bzip2; then
    CMD=$_
fi
brablc
fuente