Hard Links Help

Ksh newbie here, so please bear with me.

I'm currently writing a script that searches through a directory and displays files with multiple hard links. The way I have it set up, is that it displays the i-node number and then each of the link names. In addition to this, I need to know if there are any hard links outside of the searched directory and if so, how many. Which I'm not completely sure how to do.

find /etc ! -type d -links +1 -ls
718093    8 -rw-r--r--   2 root     root          223 Aug 22  2009 /etc/hosts
718091    8 -rw-r--r--   3 root     root          204 Aug 22  2009 /etc/sysconfig/network-scripts/ifcfg-eth0
718093    8 -rw-r--r--   2 root     root          223 Aug 22  2009 /etc/sysconfig/networking/profiles/default/hosts
718094    8 -rw-r--r--   2 root     root           68 Aug 22  2009 /etc/sysconfig/networking/profiles/default/resolv.conf
718091    8 -rw-r--r--   3 root     root          204 Aug 22  2009 /etc/sysconfig/networking/profiles/default/ifcfg-eth0
718091    8 -rw-r--r--   3 root     root          204 Aug 22  2009 /etc/sysconfig/networking/devices/ifcfg-eth0
718094    8 -rw-r--r--   2 root     root           68 Aug 22  2009 /etc/resolv.conf

I understand the $4 is the number of hard links, however does that include any links that are found outside of the current directory? If not, then what exactly do I need to do to get a total number of hard links in and out of the current directory?

Also, on a side note what is $2?

The second column is the number of disk blocks required to house the file. Generally the value is 512 byte blocks, though some versions of find might default to 1024 byte blocks.

The fourth column is the number of links, but only if the file is a regular file. If it is a directory it is the number of files contained/referenced in the directory.

Because links (old timers call them links as symbolic links were introduced later) cannot span filesystems, I'd approach the problem by executing a find on the whole file system and matching inodes for files that are listed within the target directory. The following is a way to do this and assumes that the target directory is the current working directory.

#!/usr/bin/env ksh

p=$(df -h . )
find ${p##* } -type file -ls 2>/dev/null | awk -v cwd="$(pwd)/" '
        {
                if( $4 > 1  )                           # only care about files with multiple hardlinks
                {
                        dir = $NF;
                        sub( "[^/]+$", "", dir );       # lop off the filename
                        if( dir == cwd )                # collect for printing only files in the target directory
                                listit[$1]++;

                        paths[$1] = paths[$1] $NF " ";  # collect paths associated with the inode (regardless of directory)
                }
        }

        END {
                for( f in listit )
                {
                        x = split( paths[f], a, " " );  # split paths for a "vertical" listing
                        printf( "%s\n", f );            # print the inode number
                        for( i = 1; i <= x; i++ )       # list each path
                                printf( "\t%s\n", a );
                        printf( "\n" );
                }
        }
'

[/size]

Hope this gets you started.

Nice work agama,

Can I suggest 1 slight change: add a -mount flag on the find, to keep it to the 1 filesystem.

1 Like

Thanks for the replies. Just one last question though. In the fourth column above, is that the total number of hard links for that file or just the number of links in that directory?

Total number of links to the real file.

@Chubler_XL -- yes, good call. Thanks.

can you please tell wot does ##* mean in the below piece of your code.

find ${p##* } -type file -ls 2>/dev/null | awk -v cwd="$(pwd)/" '

An excerpt from here

After reading that put it to the test.

p=$(df -h .) # free hard disk space in the current directory in a human readable format
echo ${p##* } # there's a space after the *

Thank you.. It was really helpful:b: