Shell – What idempotent command can I use to make a symlink pointing to a directory

shell-scriptsymlink

I want to put a command into a shell script which will create a symlink to directory, but this script could be run over and over, so on subsequent invocations, the command should not change anything.

Here is the directory structure:

% tree /tmp/test_symlink 
/tmp/test_symlink
├── foo
└── repo
    └── resources
        └── snippets
            ├── php.snippets
            ├── sh.snippets
            ├── snippets.snippets
            ├── sql.snippets
            └── vim.snippets

I want to create a symlink in foo/ called snippets which points to
the directory /tmp/test_symlink/repo/resources/snippets.
So I run:

% ln -sfv /tmp/test_symlink/repo/resources/snippets /tmp/test_symlink/foo/snippets
'/tmp/test_symlink/foo/snippets' -> '/tmp/test_symlink/repo/resources/snippets'

which gives the desired result.

% tree /tmp/test_symlink                                                          
/tmp/test_symlink
├── foo
│   └── snippets -> /tmp/test_symlink/repo/resources/snippets
└── repo
    └── resources
        └── snippets
            ├── php.snippets
            ├── sh.snippets
            ├── snippets.snippets
            ├── sql.snippets
            └── vim.snippets

5 directories, 5 files

However, when the command is run again,

% ln -sfv /tmp/test_symlink/repo/resources/snippets /tmp/test_symlink/foo/snippets
'/tmp/test_symlink/foo/snippets/snippets' -> '/tmp/test_symlink/repo/resources/snippets'

it creates a symlink to a directory, where the symlink aleady exists puts symlink inside the real directory

% tree /tmp/test_symlink                                                          
/tmp/test_symlink
├── foo
│   └── snippets -> /tmp/test_symlink/repo/resources/snippets
└── repo
    └── resources
        └── snippets
            ├── php.snippets
            ├── sh.snippets
            ├── snippets -> /tmp/test_symlink/repo/resources/snippets
            ├── snippets.snippets
            ├── sql.snippets
            └── vim.snippets

why is this happening and how can I modify the command so that subsequent invocations won't create this weird effect?

Best Answer

You should use the -T option for this, it tells ln to always treat the link name as the desired link name, never as a directory.

Without this option, if you give ln a link name which exists as a directory, it creates a new link to the target inside that directory.

Note that -T is a GNU-ism (at least, it’s not in POSIX), but you’re already using -v which is a GNU-ism too so I imagine that’s not an issue.

Alternatively, you can just specify the parent directory as the link name, and the link will always be (re-)created there:

ln -sfv /tmp/test_symlink/repo/resources/snippets /tmp/test_symlink/foo/

This works because your symlink has the same name as the target.

Related Question