fdf 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # License: GPLv3
  4. # Date: 01 January 2012
  5. # Edit: 11 January 2018
  6. # https://github.com/trizen
  7. # Find and list duplicate files from one or more paths, with options for
  8. # deleting or replacing duplicate files with symbolic links to the main file.
  9. use 5.005;
  10. use strict;
  11. use warnings;
  12. use File::Find qw(find);
  13. use File::Compare qw(compare);
  14. use File::Basename qw(basename);
  15. use Getopt::Long qw(GetOptions);
  16. my %order_callbacks = (
  17. path => sub { sort @_ },
  18. name => sub {
  19. map { $_->[1] }
  20. sort { $a->[0] cmp $b->[0] }
  21. map { [basename($_), $_] } @_;
  22. },
  23. time => sub {
  24. map { $_->[1] }
  25. sort { $a->[0] <=> $b->[0] }
  26. map { [-M $_, $_] } @_;
  27. },
  28. );
  29. my @dirs = grep { (-d) or (-f) } @ARGV;
  30. die <<"HELP" if !@dirs;
  31. usage: $0 [options] /my/path [...]
  32. Options:
  33. -f, --first : keep only the first duplicated file
  34. -l, --last : keep only the last duplicated file
  35. -s, --symlink : replace duplicate files with symbolic links (with -f or -l)
  36. -o, --order=type : order the results by: path, name or time
  37. HELP
  38. my $keep_first;
  39. my $keep_last;
  40. my $create_symlinks;
  41. my $order_by = 'time';
  42. GetOptions(
  43. 'f|first!' => \$keep_first,
  44. 'l|last!' => \$keep_last,
  45. 's|symlink!' => \$create_symlinks,
  46. 'o|order|order-by=s' => \$order_by,
  47. )
  48. or die("$0: error in command line arguments\n");
  49. if (not exists $order_callbacks{$order_by}) {
  50. local $" = ", ";
  51. die "$0: invalid value `$order_by` for `--order`: valid values are: @{[sort keys %order_callbacks]}\n";
  52. }
  53. sub find_duplicated_files (&@) {
  54. my $callback = shift;
  55. my %files;
  56. find {
  57. no_chdir => 1,
  58. wanted => sub {
  59. lstat;
  60. (-f _) && (not -l _) && push @{$files{-s _}}, $_;
  61. }
  62. } => @_;
  63. foreach my $files (values %files) {
  64. next if $#{$files} < 1;
  65. my %dups;
  66. foreach my $i (0 .. $#{$files} - 1) {
  67. for (my $j = $i + 1 ; $j <= $#{$files} ; $j++) {
  68. if (compare($files->[$i], $files->[$j]) == 0) {
  69. push @{$dups{$files->[$i]}}, splice @{$files}, $j--, 1;
  70. }
  71. }
  72. }
  73. while (my ($fparent, $fdups) = each %dups) {
  74. $callback->($order_callbacks{$order_by}($fparent, @{$fdups}));
  75. }
  76. }
  77. return;
  78. }
  79. {
  80. local $, = "\n";
  81. local $\ = "\n";
  82. find_duplicated_files {
  83. my (@files) = @_;
  84. print @files, "-" x 80;
  85. my $main_file = (
  86. $keep_first ? shift(@files)
  87. : $keep_last ? pop(@files)
  88. : return
  89. );
  90. foreach my $file (@files) {
  91. print ":: Removing: `$file`";
  92. unlink($file) or do {
  93. warn "error: can't delete file `$file': $!\n";
  94. next;
  95. };
  96. if ($create_symlinks) {
  97. print ":: Symlinking: `$main_file` <- `$file`";
  98. symlink($main_file, $file) or do {
  99. warn "error: can't create symbolic link for `$file': $!\n";
  100. next;
  101. };
  102. }
  103. }
  104. } @dirs;
  105. }