5

I am using Ubuntu 20.04, and I usually need to find the final executable path for a given command. Let's take awk as an example:

file $(which awk)
# Output: /usr/bin/awk: symbolic link to /etc/alternatives/awk

file /etc/alternatives/awk
# Output: /etc/alternatives/awk: symbolic link to /usr/bin/gawk

My question is:

Is there a command (or a flag for file command) that will return directly the final path of a given command? in the example above I would like it to return the path of gawk

Thank you

3

4 Answers 4

10

You can use readlink:

$ readlink -f -- "$(which awk)"
/usr/bin/gawk

From man readlink:

-f, --canonicalize canonicalize by following every symlink in every component of the given name recursively; all but the last component must exist

2
  • 1
    I think readlink -e is better, because using -f will return the current path if the given command doesn't exist. Dec 23, 2021 at 16:34
  • 1
    @OK-Validation I don't think readlink -e offers any advantages, since $(which ...) shouldn't print nonexistent paths in the first place. Additionally, readlink -f is slightly more portable; readlink -e is GNU-specific. (I know you specified Ubuntu, but people using e.g. Alpine could stumble across this and be confused when it doesn't work.)
    – anonymoose
    Dec 25, 2021 at 3:33
8

I found another command realpath:

realpath -- "$(which awk)"
#Output: /usr/bin/gawk

2
  • command -v awk is a more portable alternative to which(1).
    – iBug
    Dec 26, 2021 at 18:35
  • @iBug: see the comment of Barmar (under the question post). Dec 26, 2021 at 18:46
5

If you want to tell the file command on Linux to return information about the awk command, and to resolve all symbolic links while doing so, then use file with its -L option:

$ file -L -- "$(command -v awk)"
/bin/awk: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=b863ebf57d3cc33d8e7fed3be0e0b5d235489b46, for GNU/Linux 3.2.0, stripped

To resolve all links to get the final absolute path of a command, use readlink -e:

$ readlink -e -- "$(command -v awk)"
/usr/bin/gawk
5

In zsh, if you have a command name in a variable $cmd, $cmd:c will expand to the path of the command¹ (via a look-up of $PATH / $path), and the :P qualifier can be used to get the corresponding realpath (a symlink-free canonical absolute path to the file as when using the realpath() standard C function):

$ cmd=awk
$ print -r -- $cmd:c
/usr/bin/awk
$ print -r -- $cmd:c:P
/usr/bin/gawk
$ file -- $cmd:c:P
/usr/bin/gawk: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=b863ebf57d3cc33d8e7fed3be0e0b5d235489b46, for GNU/Linux 3.2.0, stripped
$ namei -l -- $cmd:c
f: /usr/bin/awk
drwxr-xr-x root root /
drwxr-xr-x root root usr
drwxr-xr-x root root bin
lrwxrwxrwx root root awk -> /etc/alternatives/awk
drwxr-xr-x root root   /
drwxr-xr-x root root   etc
drwxr-xr-x root root   alternatives
lrwxrwxrwx root root   awk -> /usr/bin/gawk
drwxr-xr-x root root     /
drwxr-xr-x root root     usr
drwxr-xr-x root root     bin
-rwxr-xr-x root root     gawk

To avoid the intermediary variable, you can use an anonymous function:

$ (){print -r -- $1:c:P} awk
/usr/bin/gawk

Or a normal function:

$ canonical-path-of() print -rC1 -- $@:c:P
$ canonical-path-of awk ls
/usr/bin/gawk
/usr/bin/ls

You can also use the =cmd operator which is a form of filename expansion like tilde expansion but that expands to the path of the corresponding command, and still apply the :P modifier by this time as a glob qualifier²:

$ print -r -- =awk
/usr/bin/awk
$ print -r -- =awk(:P)
/usr/bin/gawk
$ print -r -- =blah
zsh: blah not found

¹ beware however that if the command in not found in $PATH, $cmd:c will expand to the same thing as $cmd, and $cmd:c:P would expand to the path of $cmd relative to the current working directory.

² this time when the command can't be found, it triggers a fatal error. That =cmd expansion can be disabled with set +o equals

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.