Using rdfind I found a lot of hard-linked files (~60-90) and I want to unlink all of them. One possible solution is to just find all files -link +1 and just copy them, delete them and move it back to link name.

Is there a better solution? Is there an easier way to do this?

  • How big is the linked file? – agc Jun 7 '16 at 21:22
  • All of those filesizes vary from a few byte to 6GB, but all of them together arround 400GB (thats what im guessing, just before i used rdfind) now i think its arround 200GB – zanglerecke Jun 7 '16 at 21:56
  • Why do you want to do this? – James Youngman Aug 3 '16 at 8:50

You can do:

find . -type f -links +1 -exec sh -c '
  { rm -f "$1" && cat > "$1"; } < "$1"' sh {} \;

But that won't preserve file metadata. On Linux, you can do instead:

find . -type f -links +1 -exec sh -c '
  { rm -f "$1" && cp -p /proc/self/fd/0 "$1"; } < "$1"' sh {} \;

Note that if cat or cp fails (like on disk full), you'll loose the file's content that cat/cp didn't manage to copy.

We use -exec {} \; instead of -exec {} + so that the link count of the file is updated after the file has been found so that find will not find the last link if the link count has dropped to 1 (which means for a file with initial link count of 4, only 3 instances will be overwritten).

With GNU tar, you can also do:

find . -type f -links +1 -print0 |
  tar --hard-dereference --null -T - -cf - | tar xpf -

That will run fewer commands but overwrite more files.

You may see some:

tar: ./file: file changed as we read it

for large files, but that's OK as the second tar will unlink the file before overwriting it so in effect, that will be very similar to the first approach. It's just the first tar noticing the link count decreasing (and ctime changing as a result) when the second tar unlinks the file.


I believe copy, delete, and move back is the way to do it.

For a large number of files, you don't want to run three processes for each file. You could use cpio to reduce the number of processes.

Inside your tree:

find_the_hardlinks > hardlinks #find your hardlinks
< hardlinks cpio -pdv ../the_copy
< hardlinks | rm -d '\n'  #delete the links
pwd=$PWD; cd ../the_copy; 
< "$pwd/hardlinks" cpio -pdlv "$pwd" #hardlink back from copy
cd "$pwd"; rm -rf ../the_copy #remove the copy so it's like a move

It's not exactly easy, but it is quite efficient and wrappable into a generic script.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.