I am keeping my dotfiles under version control and the script deploying them creates hard links. I also use etckeeper to put my /etc under version control. Recently I have gotten warnings like this:

warning: hard-linked files could cause problems with bzr

A simple copy (cp filename.ext filename.ext) will not work:

cp: `filename.ext' and `filename.ext' are the same file

Renaming/moving a file - except across volumes - also doesn't break the hard-link.

So my question is: is there a way to break a hard-link to a file without actually having to know where the other hard-link/s to that file is/are?

  • 3
    The "rm" command breaks hard links.
    – Johan
    Mar 4, 2013 at 12:53

7 Answers 7

cp -p filename filename.tmp
mv -f filename.tmp filename

Making it scriptable:

dir=$(dirname -- "$filename")
tmp=$(TMPDIR=$dir mktemp)
cp -p -- "$filename" "$tmp"
mv -f -- "$tmp" "$filename"

Doing the copy first, then moving it into place, has the advantage that the file atomically changes from being a hard link to being a separate copy (there is no point in time where filename is partial or missing).


You probably mean that you want to split the hard-link off to a separate, independent file.

mv hardlink tempname && cp tempname hardlink && rm tempname

A hardlink is the connection between an entry in the directory and the inode block on the disk.

inodes store file meta-data, and for small files, some file systems stores data in the inode, otherwise pointers to the data blocks, and for very large files indirect and double-indirect lists of pointers to disk allocation units.

Regardless, the connection between the file name (Which is what the ls command produce) and the inode block which stores this meta-data, is called a hard link.

Having multiple hard links to a single file means the same inode referenced by more than one directory entry, possibly in different directories (on a single file system)

rm deletes the file name entry from the directory. Once an inode is no longer referenced by any files, its space is freed up for use by other files.

  • Indeed. This is what the cp and mv examples implied. So no way around using a temporary file, I see. Mar 4, 2013 at 13:20
  • @0xC0000022L, No, inodes don't contain pointers to other inodes. Just to data blocks (or they can double as data space if the object is small).
    – vonbrand
    Mar 4, 2013 at 13:26
  • 4
    Make sure the permissions and other data is preserved when copying, i .e. use cp -a (at least GNU coreutils).
    – vonbrand
    Mar 4, 2013 at 13:29
  • @0xC0000022L, if you look carefully, there is just a temporary name for the orginal file, a new file is created only in the last step.
    – vonbrand
    Mar 4, 2013 at 13:32
  • @vonbrand They can indeed, depending on what file system is being used.
    – Johan
    Mar 4, 2013 at 13:35

Put this at the end of your ~/.bashrc file.

delink () { tmpfile="$1$(date)"; cp -a "$1" "$tmpfile"; mv "$tmpfile" "$1"; }

Run it like this

delink filename

The best way to do it with a bash script would be something like this:

if [ -f "$1" ] ; then
dir="$(dirname -- "$1")"
tmpfile="$(mktemp --tmpdir="$dir")"
cp --preserve=all -f -- "$1" "$tmpfile"
mv -f -- "$tmpfile" "$1"

points to note:

  • check if the file is a regular file before trying to copy it
  • keep the old file in place until the copy is ready
  • use mktemp to generate a file which is guaranteed not to exist
  • use -f to force overwrite and --preserve=all to keep metadata as similar as possible to the original file
  • use -- and " to quote paths containing spaces and/or beginning with -

Doing the replacement without creating a temporary file isn't possible with current (3.16) linux system calls: while it is possible to overwrite a file atomically (i.e., remove the old file and replace with a new one as a single operation), it is not possible to do so with a file which has no name on the filesystem (i.e. a temporary file created using the O_TMPFILE flag of open function) because the rename function require a filename as input (there is no version of rename which takes as input a file descriptor - see here for details)

  • 1
    Note that you failed to quote the name in your dirname and mktemp calls. Fixed that for you...
    – derobert
    Aug 6, 2014 at 12:10
  • @derobert oh thank you, but this won't work since there are nested double quotes... need another fix! Kinda hate bash
    – pqnet
    Aug 6, 2014 at 12:13
  • 3
    It'll work because of the $( ... )-style command substitution. One reason its nicer than ` ... `-style.
    – derobert
    Aug 6, 2014 at 12:15
  • @derobert nice, didn't know that. Also, how can you use ` inside inline code tags?
    – pqnet
    Aug 6, 2014 at 12:20
  • You can escape them with backslashes. So to get ` you put in: `\`` (of course, I double-escaped that to get it to show you what you type).
    – derobert
    Aug 6, 2014 at 12:22

An empty in-place sed does the trick (tested with GNU sed):

sed -i '' <file>

You can use find and xargs to run that on many files:

find <paths...> -type f -links +1 -print0 | xargs -0 sed -i ''

If your are looking for all the filename who are hardlink to this file then you can use:

find -samefile myknowhardlinkfile

also ls -il myknowhardlinkfile will show you the number of filename hardlinked to the same inode (third field).

101612442 -rw-rw-r--. 2 me me 0 Aug  5 07:07 myknowhardlinkfile
  • 1
    This doesn't actually answer the question, although it could be useful.
    – Flimm
    Sep 27, 2016 at 14:54

The command you're looking for is unlink

  • Perhaps my question was not clear, but by "in-place" and the examples with cp and mv I meant to make clear that I wish the file to exist afterward. Mar 4, 2013 at 13:21
  • Ah, I didn't find that clear, no. You should go with Johan's answer, then.
    – Jenny D
    Mar 4, 2013 at 13:23
  • 1
    The command "unlink" simply removes the file (what the unlink() system call does). Sep 27, 2016 at 11:23

You must log in to answer this question.

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