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
which
, usetype
. – Barmar Dec 24 '21 at 14:48