File Coverage

blib/lib/App/SortCopies.pm
Criterion Covered Total %
statement 26 52 50.0
branch 0 14 0.0
condition 0 6 0.0
subroutine 9 10 90.0
pod 0 1 0.0
total 35 83 42.1


line stmt bran cond sub pod time code
1             package App::SortCopies;
2              
3 1     1   126417 use 5.008003;
  1         4  
4 1     1   8 use strict;
  1         2  
  1         31  
5 1     1   5 use warnings;
  1         2  
  1         45  
6 1     1   5 use File::Spec;
  1         2  
  1         36  
7 1     1   5 use File::Path qw(make_path);
  1         2  
  1         78  
8 1     1   6 use Digest::MD5;
  1         2  
  1         44  
9 1     1   584 use File::Copy qw(move);
  1         6038  
  1         71  
10 1     1   10 use File::Basename;
  1         3  
  1         94  
11 1     1   5 use feature 'say';
  1         2  
  1         761  
12              
13             =head1 NAME
14              
15             App::SortCopies - The copy sorter! (What did you expect?)
16              
17             =head1 VERSION
18              
19             Version 0.01
20              
21             =cut
22              
23             our $VERSION = '0.02';
24              
25             sub run {
26 0 0   0 0   my $src = shift @ARGV or die "Usage: sortcopies dir\n";
27 0 0 0       die "'$src' is not readable" unless -d $src and -r $src;
28              
29 0           my $dup_dir = File::Spec->catdir($src, "copies");
30 0 0         make_path($dup_dir) unless -d $dup_dir;
31              
32 0 0         opendir(my $d, $src) or die $!;
33             my @unsorted_stuff = grep {
34 0           -f File::Spec->catfile($src, $_)
  0            
35             } readdir($d);
36 0           closedir($d);
37              
38 0           my %fried;
39              
40 0           for my $file (@unsorted_stuff) {
41 0           my $path = File::Spec->catfile($src, $file);
42              
43 0 0         open(my $potato, '<', $path) or do {
44 0           warn "I couldn't open '$file'\n";
45 0           next;
46             };
47              
48 0           binmode($potato);
49 0           my $hash = Digest::MD5->new->addfile($potato)->hexdigest;
50 0           close($potato);
51              
52 0 0         if ($fried{$hash}) {
53 0           my $dest = File::Spec->catfile($dup_dir, $file);
54 0           my $i = 1;
55              
56 0           my ($name, undef, $ext) = fileparse($file, qr/\.[^.]*/);
57              
58 0   0       $dest = File::Spec->catfile(
59             $dup_dir,
60             "${name}_$i$ext"
61             ) while -e $dest and $i++;
62              
63 0 0         move($path, $dest) or warn $!;
64 0           say "Duplicate $file is moved to $dest";
65             }
66             else {
67 0           $fried{$hash} = 1;
68             }
69             }
70              
71 0           say "All copies in $src have been moved to $dup_dir!";
72             }
73              
74             1;
75              
76             =head1 SYNOPSIS
77              
78             This sorts a directory, in a non-recursive way.
79              
80             Copies of files are moved to a ./copies folder created in the directory being sorted
81              
82             sortcopies ~/path/to/dir_with_dupes
83              
84              
85             =head1 AUTHOR
86              
87             Semandi
88              
89             =head1 BUGS
90              
91             Please report any bugs or feature requests to C, or through
92             the web interface at L. I will be notified, and then you'll
93             automatically be notified of progress on your bug as I make changes.
94              
95              
96             =head1 SUPPORT
97              
98             You can find documentation for this module with the perldoc command.
99              
100             perldoc App::SortCopies
101              
102             You can also look for information at:
103              
104             =over 4
105              
106             =item * RT: CPAN's request tracker (report bugs here)
107              
108             L
109              
110             =item * Search CPAN
111              
112             L
113              
114             =back
115              
116             =head1 ACKNOWLEDGEMENTS
117              
118             To all the foxes of the world...
119              
120             =head1 LICENSE AND COPYRIGHT
121              
122             This software is Copyright (c) 2026 by Semandi .
123              
124             This is free software, licensed under:
125              
126             The Artistic License 2.0 (GPL Compatible)
127              
128             =cut
129              
130             1; # End of App::SortCopies