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, which
ing 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