144

If you run hash it shows the path of all commands run since the hash was last reset (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

According to the man pages, the purpose of hash is:

The /usr/bin/hash utility affects the way the current shell environment remembers the locations of utilities found. Depending on the arguments specified, it adds utility locations to its list of remembered locations or it purges the contents of the list. When no arguments are specified, it reports on the contents of the list. The -r option causes the shell to forget all remembered locations.

Utilities provided as built-ins to the shell are not reported by hash.

Other than seeing how many times I've entered a command, I can't see the utility of hash.

It was even featured in thegeekstuff.com's top 15 useful commands

In what ways is hash useful?

spuder
  • 18,053

6 Answers6

118

hash is a bash built-in command. The hash table is a feature of bash that prevents it from having to search $PATH every time you type a command by caching the results in memory. The table gets cleared on events that obviously invalidate the results (such as modifying $PATH)

The hash command is just how you interact with that system (for whichever reason you feel you need to).

Some use cases:

  • Like you saw it prints out how many times you hit which commands if you type it with no arguments. This might tell you which commands you use most often.

  • You can also use it to remember executables in non-standard locations.

Example:

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

Which might be useful if you just have a single executable in a directory outside of $PATH that you want to run by just type the name instead of including everything in that directory (which would be the effect if you added it to $PATH).

An alias can usually do this as well, though and since you're modifying the current shell's behavior, it isn't mapped in programs you kick off. A symlink to the lone executable is probably the preferable option here. hash is one way of doing it.

  • You can use it to un-remember file paths. This is useful if a new executable pops up in an earlier PATH directory or gets mv'd to somewhere else and you want to force bash to go out and find it again instead of the last place it remembers finding it.

Example:

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

The cp command caused a new version of the ls executable to show up earlier in my $PATH but didn't trigger a purge of the hash table. I used hash -d to selectively purge the entry for ls from the hash table. Bash was then forced to look through $PATH again and when it did, it found it in the newer location (earlier in $PATH than it was running before).

You can selectively invoke this "find new location of executable from $PATH" behavior, though:

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

You'd mostly just want to do this if you wanted something out of the hash table and weren't 100% that you could logout and then back in successfully, or you wanted to preserve some modifications you've made to your shell.

To get rid of stale mappings, you can also do hash -r (or export PATH=$PATH) which effectively just purges bash's entire hash table.

There are lots of little situations like that. I don't know if I'd call it one of the "most useful" commands but it does have some use cases.

Stephen Kitt
  • 434,908
Bratchley
  • 16,824
  • 14
  • 67
  • 103
  • I wonder where the name 'hash' does came from. What about e.g. "cache", then? :). – Michael Aug 08 '13 at 20:11
  • 5
    @Michael Because the hash command internally uses a hash table to store the mappings. http://en.wikipedia.org/wiki/Hash_table – jlliagre Aug 08 '13 at 20:39
  • 20
    It should be noted that hash is not bash specific, the command originated in the Bourne shell in SVR2 (though the feature of hashing paths of commands comes from csh before that) and is found in all Bourne-like and POSIX shells. – Stéphane Chazelas Aug 08 '13 at 21:34
  • 3
    Instead of export PATH=$PATH to clear the table, hash -r should suffice. – ravron Jan 04 '16 at 18:52
  • 1
    Another use case is when installing a second copy of a program into an earlier part of your $PATH. You need to hash -r or you'll get the old version because $PATH didn't change and so Bash doesn't realize it could load the same program from an earlier (higher priority) directory. See http://conda.pydata.org/docs/troubleshooting.html#resolution-reactivate-the-environment-or-run-hash-r-in-bash-or-rehash-in-zsh for details. – John Zwinck Jan 19 '17 at 04:26
  • I seriously dislike the hash command and bash's behavior when a program is no longer in the cache. For example if I delete a program that previously got found in the PATH (I deleted it because I wanted a different program with the same name to be used), bash unhelpfully says "bash: \path\to\deleted-program: No such file or directory" instead of just searching the PATH for it again. Then I have to remember/lookup the stupid command to reset the hash (and man hash further complicates things by bringing up docs for some database programming API). – Michael Burr Jan 25 '18 at 23:04
  • And why should I know or have to remember that a command named hash has anything to do with searching a PATH? What do I care that bash uses a hash table (or a sorted list or just the file system) to store these things? In fact, I'd like bash to behave as much as possible as if it uses the file system to store this information since PATH is a description of where on the file system bash should look for programs. If bash needs to optimize that, fine, but it should be resilient to breaks and fixing problems that I do notice shouldn't require tedious research. Sorry for the rant. – Michael Burr Jan 25 '18 at 23:13
  • The point of the hash table is to not search $PATH on every invocation. Imagine a scenario where you have multiple slow NFS mounts in $PATH then storing the location of executables in memory prevents you from seeing a hug lag when you type a simple ls -l out. – Bratchley Jan 29 '18 at 14:21
  • @Bratchley: I understand the reason for the hash table - I still don't think the hash command is a good one. The discoverability is terrible - man hash gives you no good info on it, hash --help is not very helpful, and why would I think that a command named hash has anything to do with PATH problems in the first place? The behavior of bash when a hash-match no longer works is terrible - why not just search the PATH again, or at least mention that the hash command might be of use? Anyway, I'm sorry for the rant - especially since it isn't really directed at you or your answer. – Michael Burr Jan 30 '18 at 17:11
  • There's not really a compelling case to be made for discoverability. Like I was saying in my answer hash isn't really something you use that often. In the ~15 years I've been using Unix/Linux, I've literally never used it outside of when I first learned about it in college and then again when I wrote this answer. You can't possibly document every single part of the system and the hash table really is just some random part of the system. – Bratchley Jan 31 '18 at 15:05
  • Also as far as discoverablity goes, that's probably part of the reason this is one of my most highly upvoted answers. I was legitimately surprised when it hit the double digits. So if someone really does have a question about what hash does this answer actually comes up in a google search (when I searched for hash command it was #1) which is the most common MO nowadays anyways. – Bratchley Jan 31 '18 at 15:09
46

Here's the classic usage, simplified:

# 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
  • 9,770
  • As noted here selective update of the hash table can be invoked with a single command hash hello. – 0 _ Aug 09 '14 at 00:42
  • @johntex: ok, changed. – rici Aug 09 '14 at 06:10
  • Imagine the potential for weird bugs before knowing that the hash table exists! Is there a list of circumstances when the hash table automatically gets refreshed? – benjimin Jun 28 '19 at 01:00
  • @benji: it never gets automatically refreshed (as a whole). If you run bash in posix mode or setopt -s checkhash and the hashed executable for a command no longer exists, the hash entry for that command will be updated. But note that every bash session had its own hash table, so closing the session and starting a new one effectively empties the hash. (hash -r is an easier way to do that.) – rici Jun 28 '19 at 01:47
  • Updating $PATH, or launching a new bash terminal, both appear to clean out the table. – benjimin Jun 28 '19 at 06:52
  • @benjimin: "launching a new bash terminal" creates a different bash session. As I said, every bash session has its own hash table. The hash table in the old bash session is not affected by the creation of the new bash session. Of course, if you close the old bash session, it's gone. – rici Jun 28 '19 at 12:27
  • @benjimin: however, you're right about changing the value of $PATH. That does flush the hash table. – rici Jun 28 '19 at 12:34
20

Here's a useful use of hash:

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

It means: if php isn't in the PATH, then use

/usr/local/foobar/php/bin/
Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
9

Yes, Bash Reference Manual says:

A full search of the directories in $PATH is performed only if the command is not found in the hash table.

But you can disable hashing with set +h:

-h - Locate and remember (hash) commands as they are looked up for execution. This option is enabled by default.

Try:

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

The same is for hash -r, hash NAME etc

A "command detection" (like this or that) doesn't work:

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

You can write something like this:

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

or (thanks to @mikeserv) without having to assign any new variables or do any tests:

set -h -- "-${-:--}" "$@"
hash ls >/dev/null 2>&1 || echo "Please install ls" >&2
set +h "$@"
Evgeny
  • 5,476
  • 1
    For your old_options thing - I usually do something like this: set -h -- "-${-:--}" "$@"; hash ...; set +h "$@" so it just all falls into place automatically without having to assign any new variables or do any tests or whatever. – mikeserv Jul 29 '15 at 11:28
  • @mikeserv Can you please expound on the syntax of options passed to the set command – xquilt Dec 28 '22 at 10:54
6

Easy detection whether a command is available:

CMD=gzip
if hash bzip2; then
    CMD=$_
fi
brablc
  • 241
0

Another use case for hash is when you're testing a script that uses a specific binary and you'd like to simulate the binary doesn't exist in the system.

For example, writing a script that uses git:

hash -p /usr/nope/git git

git status bash: /usr/nope/git: No such file or directory

Ko Ga
  • 101