I want to scp a file to a server. The file is a symbolic link, and actually what I want to do is copy the source file.

I don't want to track the source file's path manually, because it can be replaced.

How do I get the source file's absolute path so that I can then scp with it?

  • 2
    Most scp versions follow symlinks by default, don't they? You should check yours and maybe save yourself some work.
    – jw013
    Commented Oct 6, 2011 at 3:26
  • @jw103 Thanks for letting me know it. I didn't know. :)
    – Eonil
    Commented Oct 6, 2011 at 4:42
  • 2
    Note: scp -r forces following symlinks.
    – Eonil
    Commented Oct 6, 2011 at 4:43

10 Answers 10


Try this line:

readlink -f `which command`

If command is in your $PATH variable , otherwise you need to specify the path you know.

  • 14
    -f will return a path to a non-existent final target, so long as the intermediate link targets exist... Use -e to avoid this, ie. -e will return null if the final target does not exist.
    – Peter.O
    Commented Oct 6, 2011 at 4:31
  • 14
    -f errored with readlink: illegal option -- f in OSX. Removing it worked fine. Commented Nov 13, 2013 at 16:44
  • 2
    @HeathBorders For OSX if you have homebrew coreutils installed you can also use greadlink instead.
    – Adam Gent
    Commented Aug 22, 2016 at 14:47
  • On my systems, which command outputs which: no command in (…) or nothing.  How does it make sense to use which here?  What am I missing? Commented Jul 28, 2020 at 21:00
  • ls -ln /your/path/without/slash/at/the/end Commented Dec 2, 2022 at 19:30

Under Linux, readlink reads the contents of a symlink, and readlink -f follows symlinks to symlinks to symlinks, etc., until it finds something that isn't a symlink.

This isn't necessary for scp though: scp always follows symlinks (it always copies file content, ignoring metadata except that -p preserves file times and modes when possible).

If you find yourself disappointed by what metadata scp can and can't preserve, I suggest using rsync. With no option, rsync copies file contents ignoring metadata. The commonly used option -a preserves all garden-variety metadata (times, symbolic links, permissions and ownership), and there are options to preserve exotic metadata like ACLs and hard links.

  • readlink do only single step for read linking. for example in ubuntu we have /bin/zsh , that is symlinked to /etc/alternatives/zsh , but this is second symlink. Finally we will not get real file or directory path. The same is with stat command. Only readlink with -f option do all resolves.
    – Znik
    Commented Mar 23, 2018 at 9:52

Comparing the use of commands ls, stat, readlink, taking operations on file /etc/localtime as an example:

  • Using ls:
    [flying@lempstacker ~]$ ls /etc/localtime 
    [flying@lempstacker ~]$ ls -l /etc/localtime
    lrwxrwxrwx. 1 root root 35 Aug  2 22:41 /etc/localtime -> ../usr/share/zoneinfo/Asia/Shanghai
  • Using stat
    [flying@lempstacker ~]$ stat /etc/localtime
      File: ‘/etc/localtime’ -> ‘../usr/share/zoneinfo/Asia/Shanghai’
      Size: 35          Blocks: 0          IO Block: 4096   symbolic link
    Device: fd01h/64769d    Inode: 272202388   Links: 1
    Access: (0777/lrwxrwxrwx)  Uid: (    0/    root)   Gid: (    0/    root)
    Access: 2016-11-23 09:00:59.999887800 +0800
    Modify: 2016-08-02 22:41:26.090389904 +0800
    Change: 2016-08-02 22:41:26.090389904 +0800
     Birth: -
    [flying@lempstacker ~]$ stat -c "%N" /etc/localtime
    ‘/etc/localtime’ -> ‘../usr/share/zoneinfo/Asia/Shanghai’
  • Using readlink
    [flying@lempstacker ~]$ readlink /etc/localtime
    [flying@lempstacker ~]$ readlink -f /etc/localtime

It seems like that command readlink -f is the best fit.

Explanation on the -f parameter

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

  • duplicates this answer but is more elaborate.
    – Cadoiz
    Commented Jan 11, 2022 at 15:51

Stat will give you this information:

$ stat current
  File: `current' -> `/home/user/releases/build/'
  • readlink do only single step for read linking. for example in ubuntu we have /bin/zsh , that is symlinked to /etc/alternatives/zsh , but this is second symlink. Finally we will not get real file or directory path. The same is with stat command. Only readlink with -f option do all resolves.
    – Znik
    Commented Mar 23, 2018 at 9:53

You can either do

readlink -f `which command`

or you can get something similar in Linux with

stat `which command` | grep File

If you are adding this to a script, inspect the error. If you don't want to see the error message in the case when the sym link is not there, go with something like

readlink -f "`which command`" 
  • stat and realink without -f do only single step. then better call readlink -f .
    – Znik
    Commented Mar 23, 2018 at 9:56

I would use realpath <symlink>.

Probably what you call 'source' is the 'target' file according to the usage of ln: Usage: ln [OPTION]... [-T] TARGET LINK_NAME

  • Thanks This worked like a charm! with Mac OSX! where readlink didn't cut it...stupid mac. lol
    – JayRizzo
    Commented Aug 20, 2018 at 6:48

The following would give the full path resolving the symlink.

which [symlink] | xargs realpath

readlink -f 'which java' didn't work for me.

But this did:

readlink -f $(which java)
  • 3
    Didn't work because it is ` not '.. but $() is better practice anyways. Commented Jun 27, 2017 at 18:15
  • Why downvote? My answer is an acceptable alternative, and, as the comment says, a better practice. Commented Nov 10, 2017 at 16:32
  • 1
    Most people who down vote do not also comment, so asking for an explanation of a down vote will rarely get you an explanation, because the down voter has already left and isn't likely to come back. I would suggest you remove the typoed code that does not work from your answer. I would be willing to up vote if you took the time to clean it up and explain why $() is better practice. Commented Nov 10, 2017 at 16:38
  • As Stephen explained to you, ' is not ` . You should encapsulate by command , but this is deprecated. using $() is better, because encapsulation $( $(command) ) is much easier and don't need special escaping. Then it is preferred. In your first example you used usual apostrophe, then readlink is trying to find file named 'which java' with space in its name. But you intention is call command which with argument java, for finding java binary executable path.
    – Znik
    Commented Mar 23, 2018 at 10:02

On MAC OX, "-f" parameter doesn't work, 'readlink' works without any parameter.

KZs-MacBook-Pro:bin kz$ readlink -f pip3
readlink: illegal option -- f
usage: readlink [-n] [file ...]

KZs-MacBook-Pro:bin kz$ readlink pip3

For macOS Catalina try -

readlink -n `which command`

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .