97

I can do this:

$ pwd
/home/beau
$ ln -s /home/beau/foo/bar.txt /home/beau/bar.txt
$ readlink -f bar.txt
/home/beau/foo/bar.txt

But I'd like to be able to do this:

$ pwd
/home/beau
$ cd foo
$ ln -s bar.txt /home/beau/bar.txt
$ readlink -f /home/beau/bar.txt
/home/beau/foo/bar.txt

Is this possible? Can I somehow resolve the relative pathname and pass it to ln?

  • But what is the problem that it would be solved with the described behavior? – forcefsck Mar 31 '11 at 14:46
  • Well, say you offer a compressed folder for download, and would like to give copy-pastable instructions on how to symlink some of the folder's files. – Humphrey Bogart Mar 31 '11 at 18:14
  • 2
    ln -s $(pwd)/bar.txt ~/ or include an install script. – forcefsck Mar 31 '11 at 19:15
  • 2
    the link stores actually the name you use on command line. The resolution of that name to a file is done relative to the link location. You have to use ln -s foo/bar.txt /home/beau/bar.txt – pqnet Aug 25 '14 at 15:41

8 Answers8

118

If you create a symbolic link to a relative path, it will store it as a relative symbolic link, not absolute like your example shows. This is generally a good thing. Absolute symbolic links don't work when the filesystem is mounted elsewhere.

The reason your example doesn't work is that it's relative to the parent directory of the symbolic link and not where ln is run.

You can do:

$ pwd
/home/beau
$ ln -s foo/bar.txt bar.txt
$ readlink -f /home/beau/bar.txt
/home/beau/foo/bar.txt

Or for that matters:

$ cd foo
$ ln -s foo/bar.txt ../bar.txt

It's important to realise that the first argument after ln -s is stored as the target of the symlink. It can be any arbitrary string (with the only restrictions that it can't be empty and can't contain the NUL character), but at the time the symlink is being used and resolved, that string is understood as a relative path to the parent directory of the symlink (when it doesn't start with /).

penguin359
  • 12,077
  • 2
    Nicely said. You answered this while I was typing my own answer. :) – Shadur-don't-feed-the-AI Mar 31 '11 at 12:30
  • I just had a slightly different question - and this answers it perfectly. I wanted to know how to reference the home directory in symlink so it would work when I transferred it from one user to another. I wasn't sure if something like ~ would work. – Joe Feb 25 '17 at 07:05
  • 3
    "that string is understood as a relative path to the parent directory of the symlink" - this explains everything! – Bojan Komazec Sep 26 '19 at 15:25
  • @Joe I think to be perfect on that point it'd have to mention that ~ is expanded by the shell itself, while . isn't. It's a mistake I make constantly at least, when using ln. – Andreas Jan 27 '23 at 02:04
58

If you use newer coreutils (I'm using coreutils-8.22-11), it has an option to do that:

ln --help | grep relative
can hold arbitrary text; if later resolved, a relative link is
  -r, --relative              create symbolic links relative to link location

For example:

$ mkdir /tmp/test
$ touch /tmp/test/to
$ ln -rs /tmp/test/to /tmp/test/from
$ ls -l /tmp/test/from
    lrwxrwxrwx 1 ptr ptr 4 aug   25 17.02 /tmp/test/to -> from
detly
  • 5,160
ptr
  • 741
16

Sorry, no. Symbolic links are relative to the location the link is in, not the location you were when you created the link. There are several good reasons for this behavior, most of which involve mounting remote filesystems.

9

There's a convenient little utility called symlinks (originally by Mark Lord, now maintained by J. Brandt Buckley), present in many Linux distributions.

To convert all the absolute symbolic links in a directory to be relative:

symlinks -c /path/to/directory

Unfortunately there's no option to convert a single link, you'd have to move it to its own directory first.

mkdir ../tmp
mv link-to-relativize ../tmp
symlinks ../tmp
mv ../tmp/* .
rmdir ../tmp
Toby Speight
  • 8,678
  • thanks. symlinks available on github. Maintained by J. Brandt Buckley. v1.4.3. – Evgeny Aug 08 '15 at 12:20
  • This tool is of limited utility. It only repairs absolute symlinks that are valid, relative to the absolute root. For instance, say I have a file system tree tree/ which contains tree/bin/foo -> /bin/bar. There is no way to tell symlinks that tree is the "sysroot", such that /bin/bar is supposed to refer to tree/bin/bar (from where I'm sitting) and therefore the correct way to relativize it is to rewrite it to just bar. To the symlinks utility, this looks like a dangling link; all it can do is delete it. – Kaz Apr 19 '21 at 19:36
5

As the most common shells expands paths, to create a relative symlink you should quote the "source path".

Also, the -s parameter means --symbolic. ln creates hard links by default.

$ ln -s '../../relative_path' path
$ ls -al path

lrwxrwxrwx 1 empjustine empjustine 19 Aug  6 01:38 path -> ../../relative/path
Ouki
  • 5,962
4

You could try:

ln -s `pwd`/bar.txt /home/beau/bar.txt

But, it makes a symbolic link to the absolute pathname. Your textual question asks mentions relative pathname...

Normally, a relative pathname is what you want, and what ln -s gives you. I think what you want is:

cd /home/beau
ln -s foo/bar.txt bar.txt
MattBianco
  • 3,704
0

If you are certain there is no chance of your mount points changing you can achieve what you are trying to do in a bash shell like this:

ln -s `pwd`/bar.txt ~/bar.txt

This assumes that you are in the target directory when making the symbolic link.

mreff555
  • 113
0

When I want to create a relative path I follow the pattern given below

# CD into the directory where you want the symlink to be (ie the target)
$ cd projX/subdir1
$ pwd
/Users/USERX/repoX/projX/subdir1

relative path to the source of the file you want for the symlink, put it in the cwd

$ ln -s ../subdir2/file_foo.txt .

Verify the symlink is as expected

$ readlink /Users/USERX/repoX/projX/subdir2/file_foo.txt ../subdir1/file_foo.txt

jxramos
  • 193