5 ms·
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
by mzajc 1y ago
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`.
- JohnMakin 1y agoyou just solved a bug I couldnt explain like 6 years ago
- noman-land 1y agoWtf. TIL about hash.
- ndsipa_pomu 1y agoUsing "hash" is arguably the best way to determine if a command is available in a BASH script e.g. hash java 2>/dev/null || printf "java command not available\n"
- amiga386 1y agoIf 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 command -v java >/dev/null || echo 'no java command in path' ...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. java -version || { echo >&2 'no java command in path'; exit 1; } git --version || { echo >&2 'no git command in path'; exit 1; } gcc --version || { echo >&2 'no gcc command in path'; exit 1; }
- ndsipa_pomu 1y agoYeah, 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.
- amiga386 1y agoAs far as I know, its POSIX compliant? https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07_06 https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V... 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
- ndsipa_pomu 1y agoFair enough - I usually just target BASH and don't worry about POSIX compliance.
- bobbylarrybobby 1y agoAh, so that's where sudo texhash -r comes from when installing a latex package!
- vlovich123 1y agoIs this an old behavior? I would think ENOENT would invalidate the cache entry at least.
- mzajc 1y agoRetested with bash 5.2.37(1)-release from Debian testing, it still does this :( Changing $PATH does wipe it at least.
- Aloisius 1y agoIt's still a thing in bash 5.2.37. 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.
- Hackbraten 1y ago> 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.
- edoceo 1y agoIsn't cache invalidation one of the hard problems?
- vlovich123 1y agoSure 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)
- DiggyJohnson 1y agoHoly shit
- ninkendo 1y agoI 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. Edit: wrong shell, zsh has rehash, bash does not.
- codesnik 1y agobut zsh has "rehash"? for as long as I remember.
- ninkendo 1y agoBah, 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.
- macrocosmos 1y agoIf there is something nice that one has and one does not have. zsh is the one that has it.
- semiquaver 1y agoCounterpoint, /dev/udp pseudo-devices in bash.
- NikkiA 1y agothe 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`.
- eternauta3k 1y agoThe other typical cause is when an interpreter or library is compiled with the wrong libc version.
- OptionOfT 1y agoUnsure 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.