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`.
It's just how bash works. If there's an entry in the session cache, it uses it. Since executable paths only get cached when you run a command successfully, this only happens when it gets moved from one directory in your PATH to another after you run it once, which isn't that common.
Setting PATH or calling hash -r will clear the session cache or one could run set +h which will disable it altogether.
> this only happens when it gets moved from one directory in your PATH to another after you run it once
It also happens when you have two executables in different directories and then you delete the one with the higher priority. Happens regularly for me after I uninstall a Linux Homebrew package.
Sure but not doing it on ENOENT suggests they’re just being completely lazy. Not to mention that they do have the tools (eg inotify watches) to proactively remove stale entries based on HD changes. Of course I’d be careful about the proactive one as it’s really easy to screw things up more (eg 100 bash instances all watching the same PATH directories might get expensive or forgetting to only do this for interactive mode connected to a TTY)
I think bash has an alias “rehash” that does the same as hash -r too. But zsh doesn’t have it, so “hash -r” has entered my muscle memory, as it works in both shells.
Bah, you’re right! I got it backwards, it’s zsh that has rehash, bash does not. And hash -r works in both.
I guess I’ve been using zsh longer than I thought, because I learned about rehash first, then made the switch to hash -r later. I started using zsh 14 years ago, and bash 20+ years ago, so my brain assumed “I learned about rehash first” must have been back when I was using bash. zsh is still “that new thing” in my head.
the odd thing is, at some point I ended up with `hash -R` as muscle memory that I always type before I correct it to a lower case r, and I'm not sure why, I can't remember any shell that uses `-R`.
Unsure of in which situation, but I've had situations where a script didn't have the right shebang, and as such I had to resort to `alias --save hash='#'` to make sure the script worked.
If you want to be compatible across all shells, use command -v. POSIX mandates it exists and has that returncode behaviour, whereas it doesn't mandate the hash, which or where command
...and of course, if you're going to run the command anyway, and you know an invocation that does nothing and always exits with success, you can do that too. I like doing running "--version" or equivalent in CI systems, because it has the side effect of printing what actual versions were in use during the run.
Yeah, if you're targetting POSIX shells, then "command -v" may be more reliable.
If you're targetting BASH, then "hash" is a builtin so maybe slightly quicker (not that it's likely to be an issue) and it caches the location of "java" or whatever you're looking for, so possibly marginally quicker when you do want to run the command.
Whilst running "java -version" may be useful in some scripts (my scripts often put the output into a debug function so it only runs it when I set LOG_LEVEL to a suitable value, but it writes output to a file and STDERR), you run into an issue of "polluting" STDOUT which then means that you're not going to be using your script in a pipeline without some tinkering (ironically you're putting the failure message into STDERR when you probably don't care as the script is exiting and hopefully breaking the pipeline). Also, it can take some research to figure out what invocation to use for a specific command, whereas the "hash" version can just be used with little thought.
By the way, I don't believe that ">&2" is POSIX compliant, but that's trivial to fix.
The redirection operator:
[n]>&word
shall duplicate one output file descriptor from another, or shall close one. If word evaluates to one or more digits, the file descriptor denoted by n, or standard output if n is not specified, shall be made to be a copy of the file descriptor denoted by word