Disambiguating Ambiguous Unix Links

For many years I've been irritated by Linux's symbolic links - particularly the useful but incredibly annoying /etc/alternatives/ arrangement. This is meant to allow different packages to install the same binaries - not usually at the same time, but /etc/alternatives/java is meant to be a link to whichever version of Java you have installed. A reasonably good idea, but the problem is this:

$ which java
$ ls -l /usr/bin/java
lrwxrwxrwx. 1 root root 22 Dec  6  2020 /usr/bin/java -> /etc/alternatives/java

The problem is ... you still don't know which java you have. You have to go 'round again:

$ ls -l /etc/alternatives/java
lrwxrwxrwx. 1 root root 63 May 17 09:33 /etc/alternatives/java -> /usr/lib/jvm/java-11-openjdk-

As it turns out, this is the actual binary - but in this context you can't actually tell (it could be another link). There are better ways to check, in the form of a couple utilities: namei and readlink. The former provides more information, but most of the time I just want the final binary target, and the latter is better for that.

$ namei $(which java)
f: /usr/bin/java
 d /
 d usr
 d bin
 l java -> /etc/alternatives/java
   d /
   d etc
   d alternatives
   l java -> /usr/lib/jvm/java-11-openjdk-
     d /
     d usr
     d lib
     d jvm
     d java-11-openjdk-
     d bin
     - java

I'm not trying to give you a full description of these things: read the (short) man pages if you want the full details. But namei traces the links back to their origin, telling you when it finds a f file, d directory, or l link. readlink defaults to only disambiguating the first link: to get it to follow all further links, use the -f option. For Mac users, Darwin has readlink but it is itself a link to stat and doesn't support -f. If you want that, brew install coreutils and use the greadlink command ("g" for "GNU").

$ readlink -f $(which java)

I use this command a LOT, including assigning its output to a variable to capture the target binary.