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 byhash. If the file is moved elsewhere on$PATHand bash has the old path cached, you will get an ENOENT. The entire cache can be invalidated withhash -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