137

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?

Eonil
  • 4,657

10 Answers10

183

Try this line:

readlink -f `which command`

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

daisy
  • 54,555
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 Mar 23 '18 at 09:52
15

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

  • Using ls:
    [flying@lempstacker ~]$ ls /etc/localtime 
    /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
    ../usr/share/zoneinfo/Asia/Shanghai
    [flying@lempstacker ~]$ readlink -f /etc/localtime
    /usr/share/zoneinfo/Asia/Shanghai
    

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

AdminBee
  • 22,803
Gorgon
  • 301
5

Stat will give you this information:

$ stat current
  File: `current' -> `/home/user/releases/build/'
  ...
polynomial
  • 2,461
  • 20
  • 15
  • 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 Mar 23 '18 at 09:53
5

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`" 
5

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

XavierStuvw
  • 1,119
1

The following would give the full path resolving the symlink.

which [symlink] | xargs realpath
KSC
  • 111
0

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. – Stephen Rauch Jun 27 '17 at 18:15
  • Why downvote? My answer is an acceptable alternative, and, as the comment says, a better practice. – NonCreature0714 Nov 10 '17 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. – Stephen Rauch Nov 10 '17 at 16:38
  • As Stephen explained to you, ' is not . You should encapsulate bycommand` , 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 Mar 23 '18 at 10:02
0

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
../../../Library/Frameworks/Python.framework/Versions/3.7/bin/pip3
Xb74Dkjb
  • 101
  • 3
0

For macOS Catalina try -

readlink -n `which command`