Find out symbolic link target via command line

Solution 1:

Use the -f flag to print the canonicalized version. For example:

readlink -f /root/Public/myothertextfile.txt

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

Solution 2:

readlink is the command you want. You should look at the man page for the command. Because if you want to follow a chain of symbolic links to the actual file, then you need the -e or -f switch:

$ ln -s foooooo zipzip   # fooooo doesn't actually exist
$ ln -s zipzip zapzap

$ # Follows it, but doesn't let you know the file doesn't actually exist
$ readlink -f zapzap
/home/kbrandt/scrap/foooooo

$ # Follows it, but file not there
$ readlink -e zapzap

$ # Follows it, but just to the next symlink
$ readlink zapzap
zipzip

Solution 3:

This will also work:

ls -l /root/Public/myothertextfile.txt

but readlink would be preferred for use in a script rather than parsing ls.


Solution 4:

If you want to show the source and the destination of the link, try stat -c%N files*. E.g.

$ stat -c%N /dev/fd/*
‘/dev/fd/0’ -> ‘/dev/pts/4’
‘/dev/fd/1’ -> ‘/dev/pts/4’

It’s not good for parsing (use readlink for that), but it shows link name and destination, without the clutter of ls -l

-c can be written --format and %N means “quoted file name with dereference if symbolic link”.


Solution 5:

The readlink is a good thing, but GNU-specific and non cross platform. I used to write cross platform scripts for /bin/sh, therefore I'd use something like:

 ls -l /root/Public/myothertextfile.txt | awk '{print $NF}'

or:

 ls -l /root/Public/myothertextfile.txt | awk -F"-> " '{print $2}'

but these needs to be tested on different platforms. I think they'll work, but don't 100% sure for ls output format.

The result of ls can also be parsed within bash without depending on an external command like awk, sed or perl.

This bash_realpath function, resolves the final destination of a link (link→link→link→final):

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}"
}