Convert symlinks to hard links

I'd like to recursively convert soft links to hard links in a directory. I've tried something like this:

for f in *; do (mv $f{,~} && ln $(readlink $f~) && rm $f~) done

…but it has two major problems:

  • not recursive
  • picks up files that are not symbolic links

It would be nice to somehow feed the above line to find -type l, but i'm not sure how to do that.


Solution 1:

This command should work:

find -type l -exec bash -c 'ln -f "$(readlink -m "$0")" "$0"' {} \;

How it works:

  • find -type l finds all links in the current directory.

  • -exec bash -c '...' {} \; invokes bash to execute ....

    It passes {} – the name of the link that's currently being processed ‐ as an argument, which bash can access as $0.

  • readlink -m "$0" returns the absolute path of the symbolic link's destination.

  • ln -f "$(readlink -m "$0")" "$0" overwrites (-f) the symbolic link $0 with a hard link to its target.

If the link cannot be converted for some reason, it will remain untouched and ln will print an error message.