macOS Cannot Create Valid Symlink From Terminal Using Relative Paths

Using the following snippet it creates an invalid symlink. But according to other answers it should work fine.

# make a temp dir and save path to var.
# set dir (mktemp -d)  # fish shell only!
dir=$(mktemp -d)  # bash/zsh
cd $dir
mkdir a b
echo 'text file' > a/file.txt
ln -s a/file.txt b/
open .

When you see the symlink in Finder it doesn't recognise the file type in Quick Preview and when you open it Finder complains 'the original item cannot be found'.

If you use absolute paths everything works fine. See below:

rm b/file.txt
ln -s $dir/a/file.txt $dir/b/
open .

Then you can see symlink is correctly make and you can even read the file content in Quick Preview.

How is this happening? I've checked that ln is /bin/ln. I'm on macOS Catalina 10.15.7 (19H2).

correct symlink and quick preview


Note that this is about the filesystem and does not matter which shell you are using.

The issue is that the symbolic link just contains the text you passed to it and then the OS tries to resolve it from the actual path of the link. The link works as if you cd to where you store the link

So what happens is when you want to see b/file.txt the OS tries to open the file a/file.txt relative to directory b (ie $dir/b/a/file.txt) What you want the link to contain is ../a/file.txt

To create it from $dir

 ln -s ../a/file.txt b/

Or from anywhere

 ln -s ../a/file.txt $dir/b/

This is not related to fish nor macOS in general, the resulting symlink is just broken

$ mkdir a b
$ touch a/foo.txt
$ ln -s a/foo.txt b/
$ cd b
$ cat foo.txt 
cat: foo.txt: No such file or directory
$ ll foo.txt 
lrwxr-xr-x  1 pse  staff  9 Feb  1 13:14 foo.txt@ -> a/foo.txt

Basically you need to think ahead when creating symlinks in a directory which is different from the current one. I find it easier to run

$ cd b
$ ln -s ../a/foo.txt .

or just avoid relative symlinks in general.