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.
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 The -f flag has this functionality: "canonicalize by following every symlink in every component of the given name recursively; all but the last component must exist". This makes the output of this command potentially different from stat -c'%N' src, which doesn't follow every symlink.
- 25Use
readlink -fif you want to know the last symlink target and not only the next one.scai– scai2012-09-10 14:25:03 +00:00Commented Sep 10, 2012 at 14:25 - 13Note that
-fis a non-portable extension to GNUreadlink.bahamat– bahamat2012-09-10 21:38:51 +00:00Commented Sep 10, 2012 at 21:38 - 2When would readlink not be available? Thanks.tommy.carstensen– tommy.carstensen2015-07-29 12:55:18 +00:00Commented Jul 29, 2015 at 12:55
- 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. git.savannah.gnu.org/cgit/coreutils.git/commit/src/…)Josip Rodin– Josip Rodin2015-10-05 12:55:13 +00:00Commented Oct 5, 2015 at 12:55
- 3
readlinkcan give trouble in scripts, it return the destination of the link, relative to the link.readlink -fonly 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– Tim2018-05-24 12:28:32 +00:00Commented May 24, 2018 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.
- 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 useThorSummoner– ThorSummoner2015-09-30 17:56:42 +00:00Commented Sep 30, 2015 at 17:56 - This apparently isn't portable because on Linux, the GNU coreutils' stat(1) has different parameters and prints out
link -> destin the output. The find(1) solution should be checked if it's with GNU findutils or otherwise...Josip Rodin– Josip Rodin2015-10-05 11:06:43 +00:00Commented Oct 5, 2015 at 11:06 - Please don't forget to quote your variable expansions,
echo $fcertainly does not produce what you expect when the symlink points to/*(yes it's possible)Camusensei– Camusensei2017-03-17 10:19:23 +00:00Commented Mar 17, 2017 at 10:19
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.
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}" } 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 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
src -> dstas the output, hence finding this question, I found thatstat -N srcdidn't work, but thatstat -c"%N" srccame close (RHEL7).