I've been trying to use the -ls and find commands to total the number of hard link occurrences within a directory.

Is there a simple way to accomplish this beyond physically counting all of the link counts >2 using the ls -li command?

  • Within the top level directory, or recursively? – Jeff Schaller Nov 6 '18 at 1:11
  • Within the top level. – lostInCode Nov 6 '18 at 1:14
  • No, it is not a duplicate. I'm seeking to count ALL occurrences of hard links, not the count of a single inode occurances. – lostInCode Nov 6 '18 at 1:18
  • 1
    So if a file in the current directory is hard-linked to a file in a parent or child directory, does it "count"? – Jeff Schaller Nov 6 '18 at 1:33
  • Should . and .. be counted (on file systems where they are implemented as hard links )? – Stéphane Chazelas Nov 6 '18 at 7:28

With zsh:

(){echo $#} *(NDl+1)

Note that it doesn't count . nor ...

If you don't want to count directories (which on most file systems are always hard links), change to *(NDl+1^/).

  • (){echo $#} anonymous function which outputs its number of arguments
  • *(...) glob with glob qualifiers
  • N: enable nullglob for that glob (expand to nothing when there's no match)
  • D: enable dotglob for that glob (includes hidden files, but never . nor ..)
  • l+1: select files with a number of links strictly greater than 1.
  • ^/: exclude files of type directory.

POSIX equivalent:

find .//. ! -name . -prune -links +1 | grep -c //

(add a ! -type d after -prune to exclude directories).

Or with ls:

(export LC_ALL=C; ls -Aqn | awk 'NR > 1 && $2 > 1 {n++}; END {print n}')

To exclude directories, add && $1 !~ /^d/ after $2 > 1.

| improve this answer | |
ls -qAi | awk '{print $1}' | sort | uniq -d | wc -l

That counts inodes that are duplicated in the current directory (one of each, replace -d with -D to count all occurrences), so not counting hard links where all the other links live outside the current directory. . and .. are excluded. Replace -A with -a to add them back (though it's very unlikely they would have hard links in the current directory anyway).

There may be a shorter way but this is very illustrative.

Mr. Pipe | is your friend.

(man too)

Have fun!

| improve this answer | |
  • just want to point out that telling ls to reverse sort on time is pointless, given the later sorting. Also parsing ls is tricky; notice what happens if you touch $'foo\nbar and then pipe ls ...|sort – Jeff Schaller Nov 6 '18 at 1:36
  • 1
    @JeffSchaller, my edit should address those concerns. Ronnie, please feel free to revert if you don't agree with the change. – Stéphane Chazelas Nov 6 '18 at 7:55

Given the Linux environment, you could use the stat command on each file and ask for the link count. You can decide if you want to include or exclude dot-files from the count with shopt -s dotglob. In the loop below, I intentionally skip directories.

shopt -s dotglob
for f in ./*
  [ -d "$f" ] && continue
  if [ $(stat -c %h "$f") -gt 1 ]

Alternatively, with GNU find:

find . -maxdepth 1 \! -type d -links +1 -printf . | wc -c

This asks find to look, starting in the current directory (.):

  • only in the current directory (-maxdepth 1)
  • skipping directories (! -type d, where the ! is escaped from the shell)
  • where the number of links is greater than 1
  • in which case, print a period (.)
  • ... and then count the number of periods emitted
| improve this answer | |
  • 1
    Note that [ -d "$f" ] && continue also excludes symlinks to directories, while the stat call (which here assumes GNU stat) retrieves information on the symlink, not there target. – Stéphane Chazelas Nov 6 '18 at 7:42

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.