I have written a Perl script that identifies all regular files that are hard links to the same data. The script works fine on UNIX and Cygwin. I haven’t tested it with Strawberry Perl or any other Windows port of Perl, but I thought I’d share it anyhow. On Windows (Cygwin) I would open a terminal and do ./list-dup-hard-links /cygdrive/c/.
#!/usr/bin/perl
#
# NAME
#
# list-dup-hard-links - list regular file names pointing to the same inode
#
# SYNOPSIS
#
# list-dup-hard-links DIRECTORY
#
# DESCRIPTION
#
# For each inode that is referred to by more than one regular file, print
# the inode number and the list of corresponding files.
#
# AUTHOR
#
# Peter John Acklam <pjacklam@online.no>
use strict; # restrict unsafe constructs
use warnings; # control optional warnings
use File::Find; # traverse a file tree
if (@ARGV != 1) {
die "Usage: $0 DIRECTORY\n";
}
my $start_dir = shift; # starting directory
my $start_dev = (stat $start_dir)[0]; # device number of where we start
my %inum2files; # map each inode number to file(s)
sub wanted {
return if -l; # skip symlinks
my @fileinfo = stat(); # get file info
if (-d _) { # if a directory
my $this_dev = $fileinfo[0]; # get device number
if ($this_dev != $start_dev) { # if we crossed a device boundary
$File::Find::prune = 1; # mark directory for pruning
return; # and return
}
}
return unless -f _; # continue only if a regular file
my $inum = $fileinfo[1]; # get inode number
push @{ $inum2files{$inum} }, # append this file to the list of
$File::Find::name; # all files with this inode number
}
find(\&wanted, $start_dir); # traverse the file tree
while (my ($inum, $files) = each %inum2files) {
next if @$files < 2; # skip non-duplicates
print "\nInode number: $inum\n\n" # print header
or die "$0: print failed: $!\n";
for my $file (@$files) {
print " $file\n" # print file name
or die "$0: print failed: $!\n";
}
}