A Weird Imagination

Which command will be run?

Posted in

The problem

While trying to develop a modification to the Pelican source, I was unexpectedly having my installed version of Pelican get run instead of the local version:

$ which pelican
$ command -v pelican

For some reason, which was pointing to the executable I was expecting bash to run, but the Bash builtin command was telling me that bash was running the installed version instead.

The solution

Use the hash builtin to clear bash's cache of the location of pelican:

$ hash -d pelican
$ command -v pelican

The details

Programs vs. builtins

command is a builtin, which uses the same code path bash uses when running a command, so it always gives the right answer, while which is an external program that will use its own logic to decide which program should be executed. We can use type to determine if something is a program or a builtin:

$ type command
command is a shell builtin
$ type which
which is /usr/bin/which

Path hashing

The reason for the discrepancy is that to save time, bash will memoize the paths for commonly used commands so it does not have to repeatedly look up where they are. The hash builtin displays the table of cached command paths:

::: bash
$ hash
hits    command
  14    /usr/bin/git
   2    /usr/bin/wajig
   2    /usr/bin/man
   6    /usr/bin/gvim
 118    /usr/bin/make
   8    /bin/ls
   2    /usr/bin/find

And type on a program in that table will give a slightly different output:

$ type make
make is hashed (/usr/bin/make)

Caching paths is usually fine because the locations of available commands tend to not change often. But when I added a local version of an existing command to /usr/local/bin/, I violated that assumption.

The fix is to use the -d flag of hash to remove it from the table or hash pelican to recompute its location and put the new location in the table.


Have something to add? Post a comment by sending an email to comments@aweirdimagination.net. You may use Markdown for formatting.

There are no comments yet.