blog.chay.dev

TIL: Bash caches the paths of commands

.. using hash.

Thanks to https://news.ycombinator.com/item?id=43840285:

Fun fact: if you've ever had bash (or another shell) complain that a file doesn't exist, even though it's on $PATH, check if it's been cached by hash. If the file is moved elsewhere on $PATH and bash has the old path cached, you will get an ENOENT. The entire cache can be invalidated with hash -r.

Try it yourself

Let's try to reproduce the "file doesn't exist" error. Run a shell in a docker container, and echo the PATH:

> docker run --rm -it ubuntu
root@47d19e31a63a:/# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

Let's look at what programs are in /usr/local/bin and /usr/bin`:

root@47d19e31a63a:/# ls /usr/local/bin
root@47d19e31a63a:/# ls /usr/bin
...

Nothing in /usr/local/bin, but loads of stuff in /usr/bin. Let's try running uptime:

root@47d19e31a63a:/# uptime
 14:26:10 up 1 min,  0 user,  load average: 1.13, 0.55, 0.36

Now let's try moving uptime to /usr/local/bin, and run it again. Since it's in the PATH, we should be able to discover it, but we expect this to fail because we know the entry is cached.

root@47d19e31a63a:/# mv /usr/bin/uptime /usr/local/bin/uptime
root@47d19e31a63a:/# uptime
bash: /usr/bin/uptime: No such file or directory
root@47d19e31a63a:/# /usr/local/bin/uptime
 14:27:16 up 2 min,  0 user,  load average: 0.66, 0.60, 0.41

We managed to reproduce the error! Let's look at the hash table now:

root@47d19e31a63a:/# hash
hits	command
   1	/usr/bin/mv
   1	/usr/bin/uptime
   2	/usr/bin/ls

Interestingly, echo is not cached, even though echo is also a program.

Let's try clearing the cache, and running uptime again.

root@47d19e31a63a:/# hash -r
root@47d19e31a63a:/# uptime
 14:32:38 up 25 min,  0 user,  load average: 0.24, 0.41, 0.37
root@47d19e31a63a:/# which uptime
/usr/local/bin/uptime