Quel est le but de la commande hash?

118

Si vous exécutez, hashil affiche le chemin de toutes les commandes exécutées depuis la dernière réinitialisation du hachage ( 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

Selon les pages de manuel, le hachage a pour but:

L'utilitaire / usr / bin / hash affecte la manière dont l'environnement shell actuel se souvient des emplacements des utilitaires trouvés. Selon les arguments spécifiés, il ajoute les emplacements des utilitaires à la liste des emplacements mémorisés ou purge le contenu de la liste. Lorsqu'aucun argument n'est spécifié, le contenu de la liste est signalé. Cette -roption fait que le shell oublie tous les emplacements mémorisés.

Les utilitaires fournis en tant que fonctions intégrées au shell ne sont pas signalés par hash.

Outre le nombre de fois où j'ai entré une commande, je ne vois pas l'utilité de hash.

Il figurait même parmi les 15 commandes les plus utiles de thegeekstuff.com

De quelle manière est hashutile?

spuder
la source

Réponses:

97

hashest une commande intégrée de bash. La table de hachage est une fonctionnalité bash qui l’empêche de chercher $PATHchaque fois que vous tapez une commande en mettant en cache les résultats en mémoire. La table est effacée lorsque des événements invalident manifestement les résultats (tels que la modification $PATH)

La hashcommande est simplement la façon dont vous interagissez avec ce système (pour la raison qui vous semble nécessaire).

Quelques cas d'utilisation:

  • Comme vous l'avez vu, il affiche le nombre de fois où vous appuyez sur quelle commande si vous le tapez sans argument. Cela peut vous indiquer les commandes que vous utilisez le plus souvent.

  • Vous pouvez également l'utiliser pour mémoriser des exécutables dans des emplacements non standard.

Exemple:

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

Ce qui pourrait être utile si vous avez juste un seul exécutable dans un répertoire en dehors de $PATHcelui que vous voulez exécuter en tapant simplement le nom au lieu de tout inclure dans ce répertoire (ce qui serait l'effet si vous l'ajoutiez à $PATH).

Cela dit, un alias peut généralement aussi le faire, et puisque vous modifiez le comportement du shell actuel, il n'est pas mappé dans les programmes que vous lancez. Un lien symbolique vers l'exécutable isolé est probablement l'option préférable ici. hashest une façon de le faire.

  • Vous pouvez l'utiliser pour ne pas mémoriser les chemins de fichiers. Ceci est utile si un nouvel exécutable apparaît dans un PATHrépertoire précédent ou va mvquelque part et que vous voulez forcer bash à sortir et à le retrouver au lieu du dernier endroit mémorisé.

Exemple:

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

La cpcommande a fait lsapparaître une nouvelle version de l' exécutable plus tôt dans my, $PATHmais n'a pas déclenché de purge de la table de hachage. J'avais l'habitude hash -dde purger sélectivement l'entrée lsde la table de hachage. Bash a ensuite été obligé de regarder à $PATHnouveau et quand il l'a fait, il l'a trouvé à l'emplacement le plus récent (plus tôt dans $ PATH qu'il était en cours d'exécution auparavant).

Vous pouvez invoquer sélectivement ce $PATHcomportement "trouver le nouvel emplacement de l'exécutable ", cependant:

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

Vous voudriez surtout faire cela si vous vouliez quelque chose en dehors de la table de hachage et que vous ne pouviez pas vous déconnecter à 100%, puis revenir avec succès, ou si vous vouliez conserver certaines modifications que vous avez apportées à votre shell.

Pour vous débarrasser des mappages obsolètes, vous pouvez également faire hash -r(ou export PATH=$PATH) simplement purger la table de hachage entière de bash.

Il y a beaucoup de petites situations comme celle-là. Je ne sais pas si je l'appellerais l'une des commandes "les plus utiles", mais elle a quelques cas d'utilisation.

Bratchley
la source
Je me demande d'où vient le nom 'hash'. Qu'en est-il par exemple "cache", alors? :)
Michael
2
@Michael Parce que la hashcommande utilise en interne une table de hachage pour stocker les mappages. en.wikipedia.org/wiki/Hash_table
jlliagre
10
Il convient de noter qu’il hashn’est pas bashspécifique, la commande a été créée dans le shell Bourne dans SVR2 (bien que la fonctionnalité de hachage des chemins de commandes vienne d’ cshavant) et qu’elle se retrouve dans tous les shells de type Bourne et POSIX.
Stéphane Chazelas
1
Au lieu de export PATH=$PATHvider la table, cela hash -rdevrait suffire.
Ravron
1
Un autre cas d'utilisation concerne l'installation d'une deuxième copie d'un programme dans une partie antérieure de votre $ PATH. Vous aurez besoin de hash -rou vous obtiendrez l'ancienne version car $ PATH n'a pas changé et donc Bash ne se rend pas compte qu'il pourrait charger le même programme à partir d'un répertoire plus ancien (priorité plus élevée). Voir conda.pydata.org/docs/… pour plus de détails.
John Zwinck
37

Voici l'utilisation classique, simplifiée:

# 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
la source
Comme indiqué ici, la mise à jour sélective de la table de hachage peut être appelée avec une seule commande hash hello.
Ioannis Filippidis
@johntex: ok, changé.
rici
Imaginez le potentiel de bugs étranges avant de savoir que la table de hachage existe! Existe-t-il une liste de circonstances dans lesquelles la table de hachage est automatiquement actualisée?
benjimin
@benji: il n'est jamais automatiquement actualisé (dans son ensemble). Si vous exécutez bash en mode posix ou si setopt -s checkhashl'exécutable haché d'une commande n'existe plus, l'entrée de hachage de cette commande sera mise à jour. Mais notez que chaque session bash a sa propre table de hachage, donc fermer la session et en démarrer une nouvelle videra le hachage. ( hash -rest un moyen plus facile de le faire.)
rici
La mise à jour $PATHou le lancement d'un nouveau terminal bash semblent nettoyer la table.
benjimin
17

Voici une utilisation utile de hash:

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

Cela signifie: si php n'est pas dans le PATH, utilisez

/usr/local/foobar/php/bin/
Gilles Quenot
la source
8

Oui, le manuel de référence de Bash dit:

Une recherche complète des répertoires dans $ PATH est effectuée uniquement si la commande est introuvable dans la table de hachage.

Mais vous pouvez désactiver le hachage avec set +h:

-h - Localiser et mémoriser (hachage) des commandes au fur et à mesure de leur exécution. Cette option est activée par défaut.

Essayer:

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

La même chose est pour hash -r, hash NAMEetc.

Une "détection de commande" (comme ceci ou cela ) ne fonctionne pas:

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

Vous pouvez écrire quelque chose comme ceci:

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

ou (grâce à @mikeserv) sans avoir à affecter de nouvelles variables ni à effectuer de tests:

set -h -- "-${-:--}" "$@"
hash ls >/dev/null 2>&1 || echo "Please install ls" >&2
set +h "$@"
Evgeny Vereshchagin
la source
1
Pour votre old_optionstruc, je fais habituellement quelque chose comme ceci: set -h -- "-${-:--}" "$@"; hash ...; set +h "$@"ainsi tout se met automatiquement en place sans avoir à assigner de nouvelles variables ou à faire des tests ou quoi que ce soit.
mikeserv
5

Détection facile si une commande est disponible:

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