2 min read

hash: You’ve Probably Never Heard Of This Thing That Affects Every Command You’ve Ever Typed In A Terminal

When you type a command into the terminal, how does the computer know where to find the program you asked it to run?

Dumb question, I know. It's which, right?

$ which ls
/bin/ls

Actually, no. Here's the problem with which. which finds the program you ask it for, but it does so by walking through your entire $PATH looking for matches. Now if your $PATH looks something like this...

/Users/andrew/.rbenv/shims:/Users/andrew/.nvm/versions/node/v0.12.7/
bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:/
usr/local:/opt/X11/bin:/usr/local/git/bin:/usr/local/bin:/usr/bin:/
bin:/usr/sbin:/sbin:/opt/X11/bin:/usr/local/git/bin:/Applications/
Wireshark.app/Contents/MacOS:/usr/local/opt/fzf/bin

...then looking through the whole thing is going to be sloooooowwww.

Imagine if that process had to repeat every time you ran a command. Say you fire off 5 tar commands in a row because you forgot the damn flags again. Every time you hit enter, you're sitting there like an idiot waiting for milliseconds before you program even begins to execute, while your computer is out there somewhere, whiching merrily along, looking for the same copy of tar that it just found a second ago.

Those are milliseconds that you will never get back.

Fortunately for us, our UNIX forbearers decreed "This sucks". Like most problems in Computer Science, it was solved with a hash table.

Enter hash

The first time you use a program in a new shell session, the computer does a which-style lookup to find where that program lives. It does this exactly once. After that it stores the location of that program in a hash table, so the next time you ask it to run that same program it knows exactly where to find it.

We can perform operations on this table and look at its state using the appropriately-named hash command. It's appropriately named for two reasons: 1) because it's a hash table, and 2) because it's dank.

Let's open a new shell session, run a command, and then look at the state of our table with hash -l:

$ ls /tmp/
afile.go  somefile.txt
$ hash -l
builtin hash -p /bin/ls ls

As you can see the computer found a copy of ls at /bin/ls. From now on, as long as you keep this shell session alive, that's the ls it's going to use.

So what happens if we get rid of /bin/ls?

$ which -a ls
/bin/ls
/usr/sbin/ls
$ mv /bin/ls /bin/totally-not-ls-nothing-to-see-here-this-is-a-whole-different-thing

As you can see, there are multiple copies of ls on our $PATH, so obviously the computer should be smart enough to detect that the one in its hash table is no longer there, and go find another one.

$ ls
-bash: /bin/ls: No such file or directory
$ which ls
/usr/sbin/ls

Just kidding. The moment a program's location gets stored in the hash table, the computer assumes it will never need to find another location for the program, which can lead to frustrating errors like this.

If you get in this situation and don't know about hash, this is infuriating. What do you mean there's no such file or directory. You just TOLD me where it is!!!!

But now that you know about hash, you need not be afraid. The solution is actually quite simple: just open a new shell session and start over with a clean hash table, or use hash -r to refresh your hash table.

$ ls
-bash: /bin/ls: No such file or directory
$ hash -r 
$ hash -l
hash: hash table empty
$ ls /tmp/
afile.go  somefile.txt
$ which ls
/usr/sbin/ls