144
votes

Say that I setup a symbolic link:

ln -s /root/Public/mytextfile.txt /root/Public/myothertextfile.txt

is there a way to see what the target of myothertextfile.txt is using the command line?

187
votes

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
  • 8
    As suggested in another question, you may want to use readlink -f. – Denilson Sá Maia Jun 7 '14 at 0:14
  • 5
    To get this working on Mac OS X, brew install coreutils. This installs basic gnu versions of commands prefixed with the letter g, i.e. greadlink -f somefile – Mike D Feb 21 '16 at 20:47
  • with -f switch, command returns nothing in my case.. – sotn Jul 19 '18 at 8:08
  • Using -f gave me the information that I wanted, i.e. resolving multiple symlinks and showing the end target. – isapir Oct 15 '18 at 19:52
  • on Ubuntu 18.04.1 LTS (Bionic Beaver) the manpages of readlink have a note saying "Note realpath(1) is the preferred command to use for canonicalization functionality." – Shmuel Kamensky Jul 1 '19 at 15:32
25
votes

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
  • 1
    Nothing wrong with the first answer, but this one is more complete and helpful. – Mike S May 11 '18 at 15:30
6
votes

This will also work:

ls -l /root/Public/myothertextfile.txt

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

5
votes

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”.

3
votes

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

If you can't use readlink, then parsing the result of ls -l could be done like this.

The normal result would be:

ls -l /root/Public/myothertextfile.txt
lrwxrwxrwx 1 root root 30 Jan  1 12:00 /root/Public/myothertextfile.txt -> /root/Public/mytextfile.txt

So we want to replace everything before " -> " and the arrow included. We could use sed for this:

ls -l /root/Public/myothertextfile.txt | sed 's/^.* -> //'
/root/Public/mytextfile.txt
1
vote

The question is not accurate enough to give a simple answer as the one of brian-brazil:

readlink -f some_path

will indeed dereference every symlink involved in the path construct to the final target behind some_path.

But one level of cascading symlinks is just a particular case among others in a system, the general case being N levels of cascading symlinks. Look at the following on my system:

$ rwhich emacs
/usr/bin/emacs
 /etc/alternatives/emacs
  /usr/bin/emacs24-x

rwhich is my own recursive implementation of which that prints all of the intermediate cascading symlinks (to stderr) down to the final target (to stdout).

Then if I want to know what is:

  • the target of symlink /usr/bin/emacs**, the obvious answer to me is /etc/alternatives/emacs as returned by:

    readlink $(which emacs)
    readlink /usr/bin/emacs
    
  • the final target behind cascading symlinks /usr/bin/emacs, the answer shall be /usr/bin/emacs24-x as returned by:

    readlink -f $(which emacs)
    readlink -f /usr/bin/emacs
    rwhich emacs 2>/dev/null
    
0
votes

ll or ls -l should list the directory items, including your symbolic link and it's target

cd -P /root/Public/myothertextfile.txt (in your case) should point to the original path

0
votes

If you find the target of all links within a folder and its subfolder use the find . -type l -ls command with link type as follows:

me@local.localdomain:~/test$ find . -type l -ls
8601855888        0 lrwxr-xr-x    1 me           staff                   6 Jan 24 19:53 ./link -> target

If you want the target of a single, then ls -l should work:

me@local.localdomain:~/test$ ls -l 
total 0
lrwxr-xr-x  1 me  staff  6 Jan 24 19:53 link -> target
-rw-r--r--  1 me  staff  0 Jan 24 19:53 target

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