34

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.

1

1 Answer 1

49

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.

6
  • Regarding that last paragraph, wouldn't it suffice to specify absolute paths to bash and ln?
    – Daniel Beck
    Commented Mar 8, 2013 at 14:42
  • 3
    My problem was that readlink returns a relative path by default, but the -e switch fixes that.
    – Dennis
    Commented Mar 8, 2013 at 14:51
  • What would be the command for OSX? Commented May 30, 2017 at 2:43
  • 2
    On a Mac it is: brew install coreutils and find * -type l -exec bash -c 'ln -f "$(greadlink -m "$0")" "$0"' {} \;. Commented May 31, 2017 at 14:03
  • 1
    Nice! And even works with spaces in names. Commented Nov 4, 2019 at 8:01

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .