74

Let's say when I do ls -li inside a directory, I get this:

12353538 -rw-r--r-- 6 me me 1650 2013-01-10 16:33 fun.txt

As the output shows, the file fun.txt has 6 hard links; and the inode number is 12353538.

How do I find all the hard links for the file i.e. files with the same inode number?

2
  • 1
    See also the answers to this question. Commented Feb 7, 2021 at 11:14
  • Are you looking for all directory entries, or an inode? (there is at most one file per file-system with each i-node number). Commented Dec 19, 2022 at 22:31

4 Answers 4

73

The basic premise is to use:

find /mount/point -mount -samefile /mount/point/your/file

On systems with findmnt you can derive the mount point like this:

file=/path/to/your/file
find "$(findmnt -o TARGET -cenT "$file")" -mount -samefile "$file"

It's important not to search from / - unless the target file is on that filesystem - because inode numbers are reused in each mounted filesystem.

6
  • 4
    @TheoneManis I just noticed that the other parts of the find call are not necessary. find is clever enough to use them implicitely. Usually you have to give find a search path and have to tell it that is shall not leave these file systems (via symlinks or mount points). But when looking for hard links it's clear on which file system to search. Commented Apr 24, 2013 at 13:51
  • 7
    Hauke, that depends on the version of find you're using. The GNU version might do that, but the BSD one does not, and this will not work as-is on Mac. Commented Apr 24, 2013 at 19:36
  • 4
    You may want to add -xdev to avoid descending into directories in other filesystems, otherwise you might find another file with the same inode number located in another filesystem.
    – mmoya
    Commented Nov 17, 2013 at 17:46
  • The near-equivalent to -samefile on HP-UX is -linkedto (though it is slightly different: a search path must still be specified, for example). Commented Aug 27, 2015 at 19:37
  • 3
    Note that if you are not in the root of your mount point, find will explore only subfolders of the current folder. So you should really say something like find /mount/point -samefile /mount/point/your/file
    – Calimo
    Commented Apr 6, 2017 at 13:30
62

If you already have the inode number you can use find's -inum option:

find /mount/point -xdev -inum 12353538

(some find implementations also support -mount as an equivalent of -xdev though only -xdev is standard).

0
3

ffind from The Sleuth Kit can find all the file names for an inode, including deleted file names.

For example:

sudo ffind -a /dev/sda3 $(stat --format=%i ~/just_a_test)

yields

* /home/me/empty_1
* /home/me/hard_link_to_empty1
/home/me/just_a_test
/home/me/hard_link_to_just_a_test

The entries with a preceding star are previous file names that don't exist anymore (because the file was renamed or deleted).


I use $(stat --format=%i ~/just_a_test) to get the inode of the file.

To get the partition of the file name programmatically (/dev/sda3 in the previous example), you can use df:

file=~/just_a_test; sudo ffind -a $(df -P "$file" | awk 'END{print $1}') $(stat --format=%i "$file")
0

With thanks to the previous answers.

Note that the stat binary can not only provide one with the inode, but its corresponding device's mount point too. The snippet below uses this to solve the 'usual' use case:

fn_hardlinks() {
  declare target; target="$1" && shift
  [ ! -e "$target" ] && \
    { echo "[error] invalid target: '$target'" 1>&2; exit 1; }
  stat '/' 2>/dev/null 1>&2 || \
    { echo "[error] no functioning 'stat' binary found'" 1>&2; exit 1; }
  declare mount; mount="$(stat -c '%m' "$target")"
  declare inode; inode="$(stat -c '%i' "$target")"
  [ "x${mount[-1]}" != "x/" ] && mount+="/"
  find "$mount" -xdev -inum "$inode" 2>/dev/null
}

alias hardlinks=fn_hardlinks

and running:

> hardlinks ./resources/sphinx/gitinfo.py
/home/user/build/z-documentation/resources/sphinx/gitinfo.py
/home/user/build/sphinx-gitinfo/git/sphinx-gitinfo/gitinfo.py

PS: watch out for exit vs return depending on how you use this!

2
  • Why fn_hardlinks with an alias rather than a function named hardlinks?
    – Sinjai
    Commented Apr 20, 2023 at 17:36
  • no reason here - it's a no-op as you rightly allude to. but given the context that this was ripped from a larger pool of utils - because to those unseasoned in shell, function calls don't stand out (hence the prefix), and the alias is for consistency and anticipation of parameterisation Commented Apr 27, 2023 at 19:42

You must log in to answer this question.

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