For use in a shell-script, I'm looking for a commandline-way to get the destination of a symbolic link. The closest I've come so far is stat -N src
, which outputs src -> dst
. Of course I could parse the output and get dst
, but I wonder if there is some direct way of getting the destination.

- 3,487
8 Answers
Another option would be to use the specifically designed command readlink
if available.
E.g.
$ readlink -f `command -v php`
/usr/bin/php7.1

- 19,173

- 1,309
-
25Use
readlink -f
if you want to know the last symlink target and not only the next one. – scai Sep 10 '12 at 14:25 -
-
13
-
2
-
2@tommy.carstensen readlink(1) was added to GNU coreutils in 2003, so these days you can likely depend on it on practically all systems using coreutils. (Cf. http://git.savannah.gnu.org/cgit/coreutils.git/commit/src/readlink.c?id=2ae02ab5b91daa8dc94ac42496a3ee701c25d1ac) – Josip Rodin Oct 05 '15 at 12:55
-
You can probably rely on readlink these days, but you can't rely on -f on all *nix platforms...readlink is available on OSX but the -f flag is for specifying output format, and I don't see an equivalent for following all symlinks. – Theodore Murdock Jul 27 '17 at 21:41
-
2
readlink
can give trouble in scripts, it return the destination of the link, relative to the link.readlink -f
only gives the final destination. If you where looking to copy the symlink and its destination in the script, the symlink will break if it was pointing to another symlink. The intermediate will be missing. – Tim May 24 '18 at 12:28
On Mac OS X and FreeBSD/NetBSD/etc. it's:
stat -f %Y <filename>
More generically I guess the solution is (stat --printf=%N uses weird quotes):
ls -l b | sed -e 's/.* -> //'
Example:
# ln -s a b
# stat -f %Y b
a
Another method is:
# find b -maxdepth 0 -printf %l
a#
The last line is mangled because it has no newline, but that is fine if you need the result in a variable, like so
# f=$(find b -maxdepth 0 -printf %l)
# echo $f
a
The -maxdepth
is needed to prevent find
from descending into directories if b
happens to be a directory.

- 1,138

- 370
-
I totally skimmed over,
stat --printf='%N\n'
is exactly what I want, weird quotes don't bother me, their the same quotes rm and ln --interactive use – ThorSummoner Sep 30 '15 at 17:56 -
This apparently isn't portable because on Linux, the GNU coreutils' stat(1) has different parameters and prints out
link -> dest
in the output. The find(1) solution should be checked if it's with GNU findutils or otherwise... – Josip Rodin Oct 05 '15 at 11:06 -
Please don't forget to quote your variable expansions,
echo $f
certainly does not produce what you expect when the symlink points to/*
(yes it's possible) – Camusensei Mar 17 '17 at 10:19
This can be done using GNU find
: find src -prune -printf "%l\n"
.

- 544,893

- 3,487
realpath
command of coreutils
package,
as linked in readlink
command's manual page.
for example:
realpath /bin/python
outputs
/usr/bin/python2.7
on my machine.
On a system where I have no readlink
or stat
commands but I do have Python 2.x, I'm using a short script:
#!/usr/bin/env python
import os, sys
if __name__ == "__main__":
src = sys.argv[1]
target = os.readlink(src)
if not os.path.isabs(target):
target = os.path.abspath(os.path.join(os.path.dirname(src), target))
print target
Note that unlike readlink -f
this may only follow one level of symlink.

- 1,177
Portable pure Bash realpath
bash_realpath() {
# print the resolved path
# @params
# 1: the path to resolve
# @return
# &1: the resolved link path
local path="${1}"
while [[ -L ${path} && "$(ls -l "${path}")" =~ -\>\ (.*) ]]
do
path="${BASH_REMATCH[1]}"
done
echo "${path}"
}

- 477
Portably: no luck except using heuristics to parse ls -l output, or use perl -le 'print readlink("some-file")'
some systems have a readlink
command, some with a -f
option to obtain the absolute path.
There are various implementations of a stat
command as a wrapper for the stat
/lstat
system calls. The GNU one is not useful in that regard, but zsh's builtin one is more so:
zmodload zsh/stat
stat +link the-link
Still with zsh, you can get the absolute path of a file (removes every symlink component) with the :A
modifier (applies to variable expansion, history expansion and globbing:
~$ gstat -c %N b
`b' -> `a'
~$ var=b
~$ echo $var:A
/home/me/a
~$ echo b(:A)
/home/me/a
~$ echo ?(@:A)
/home/me/a

- 544,893
Added a pure sh version, hoping it to be more portable if bash
is not installed :
$ cat ./realpath.sh
#!/usr/bin/env sh
bash_realpath() {
# print the resolved path
# @params
# 1: the path to resolve
# @return
# &1: the resolved link path
for path;do
while [ -L "${path}" ]
do
path="$(\ls -l "${path}" | awk '{print $NF}')"
done
which "${path}"
done
}
bash_realpath "$@"
Use case :
$ ./realpath.sh $(which gcc python)
/usr/bin/x86_64-linux-gnu-gcc-6
/usr/bin/python2.7

- 2,149
src -> dst
as the output, hence finding this question, I found thatstat -N src
didn't work, but thatstat -c"%N" src
came close (RHEL7). – Mark Booth Dec 14 '22 at 16:28