-1

So my task is to find the file with the most hardlinks in a directory. So far i have :

find . -name "file*" | xargs -I{} -n 1 find . -samefile {} 

which gives me:

./hardlinkFIle245
./hardlinkFIle23
./hardlinkFIle2
./file2.txt
./hardlinkFIle1234
./hardlinkFIle123
./hardlinkFIle12
./hardlinkFIle1
./file1.txt

Now when I pipe it in with |wc -l, I get the total number of lines 9:

find . -name "file*" | xargs -I{} -n 1 find . -samefile {} | wc -l

What I want is for each xargs batch -n 1 to give me the count : so i want:

4
5
  • Doesn't your find command's -printf have a %n specifier for the number of hardlinks? You could simply sort and tail that – steeldriver Mar 20 at 22:52
  • Thanks man that is the right answer, but the sort is not working as expected after print f: find . -printf '%n' | sort -n How to to sort the answer and also print the file name and sort them by num,ber of hardlinks – Goking Mar 21 at 11:19
  • Something like find . -printf '%n\t%f\n' | sort -n | tail -n 1 should work, I think? if there is more than one file with the same number of links, it will return the one whose name is "numerically first" – steeldriver Mar 21 at 12:15
  • The way they are sorted are in order 5 5 5 5 5 5 4 4 4 4 and so on and not: just 5 and 4 so tail -n 3 gives me basically 3 5's, since they all point to the same file. But what i want is the unique files ordered by number by hardlinks sorted so tail - n 2 would give me 5 4 not 5 5. – Goking Mar 21 at 12:48
  • Just use uniq command: find . -printf '%n\n' | uniq | sort -n -r – Goking Mar 21 at 12:58
2

If using GNU find, you can have it report the number of links with -printf %n. So you can get the maximum value with:

find . -name 'file*' -printf '%n\n' | sort -rn | head -n1

Note however that that number may include links that are not found under . or for entries that don't match the file* pattern.

If you only want to count hardlinks named file* found under ., and see the paths for those, you could do:

find . -name 'file*' -printf '%i\0%p\0' | gawk -v RS='\0' '
   {
     inode = $0
     getline file
   }
   ++count[inode] >= max {
     files[inode] = files[inode] " - " file ORS
     max = count[inode]
     max_inode = inode
   }
   END {
     printf "%s", "File with most links ("max"):\n" files[max_inode]
   }'

Which would still run just one find invocation instead of one per file.

2

You can just spawn a new shell on each xargs:

find . -name "file*" | xargs -n 1 sh -c 'echo "$1"; find . -samefile "$1" | wc -l' xargs-sh

Though using xargs is a bad idea here as it would break if file paths contain whitespace of quoting characters.

Here, using wc -l is also brittle as it breaks if file paths contain newline characters.

You could use the standard find -exec cmd {} + syntax and save having to run one sh per file by using a loop:

find . -name "file*" -exec sh -c '
  for file do
    printf "%s\n" "$file"
    find .//. -samefile "$file" | grep -c //
  done' find-sh {} +

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.