Recently I came across the need to see the difference between two directories. After a bit of hacking I produced dirdiff! From the pod:
dirdiff is a simple script to show a files diff for two directories. Similar to the real 'diff', things in directory a are prefixed with a '<'. Things in directory b are prefixed with a '>' and things in both directories are prefixed with a '='. The prefix may be removed with the --plain option. Options exist to display results by themselves or in combination. The default setting is to display files in a, not in b, and files in b, not in a. To add the intersection of a and b, use the --inter option. Note that the default entry for the path2 is '.'.
No doubt there is a better unix solution, but I don't live there so to speak!
#!/usr/bin/perl # dirdiff.pl -- files diff for two directories. use strict; use warnings; use diagnostics; use File::Find qw(find); use Getopt::Long; use Pod::Usage; use Cwd qw(abs_path); use Sort::Naturally; our $VERSION = '0.05'; my @opt_exts; GetOptions( 'debug=i' => \( my $debug = 0 ), 'help|?' => \( my $opt_help ), 'man' => \( my $opt_man ), 'version' => \( my $opt_version ), 'aonly' => \( my $opt_aonly ), 'bonly' => \( my $opt_bonly ), 'inter' => \( my $opt_inter ), 'jonly' => \( my $opt_jonly ), 'plain' => \( my $opt_plain ), 'order' => \( my $opt_order ), 'ext=s' => \@opt_exts, ) or pod2usage(2); if ($opt_version) { print "$0 vrs. $VERSION\n"; exit; } pod2usage(1) if $opt_help; pod2usage( -verbose => 2 ) if $opt_man; pod2usage("$0: No paths given.") unless @ARGV; @opt_exts = split ( /,/, join ( ',', @opt_exts ) ) if @opt_exts; push ( @ARGV, '.' ); my @dir1 = scandir( $ARGV[0], @opt_exts ); my @dir2 = scandir( $ARGV[1], @opt_exts ); if ( not( $opt_bonly or $opt_jonly ) ) { print_diff( '<', abs_path( $ARGV[0] ), diff( \@dir1, \@dir2 ) ); } if ( not( $opt_aonly or $opt_jonly ) ) { print_diff( '>', abs_path( $ARGV[1] ), diff( \@dir2, \@dir1 ) ); } if ( $opt_inter or $opt_jonly ) { print_diff( '=', '', isect( \@dir1, \@dir2 ) ); } sub print_diff { my ( $decor, $dir, @filename ) = @_; my $prefix = ''; $prefix .= "$decor " if $opt_plain; $prefix .= "$dir" if defined $dir and length $dir; # awkward :-( print map "$prefix$_\n", $opt_order ? nsort(@filename) : @filename +; } sub scandir { my ( $dir, @extensions ) = @_; my @dirlist; find( sub { return if /^\.$/ or /^\.\.$/; if (@extensions) { return unless matched( $_, @extensions ); } push ( @dirlist, substr( $File::Find::name, index( $File::Find::name, ' +/' ) ) ); }, $dir ); return @dirlist; } sub matched { my $pat = shift; my @pats = @_; return grep $pat =~ /$_$/, @pats; } sub diff { my $a = shift; my $b = shift; my %seen; my @aonly; @seen{@$b} = (); for (@$a) { push ( @aonly, $_ ) unless exists $seen{$_}; } return @aonly; } sub isect { my $a = shift; my $b = shift; my %union; my %isect; for ( @$a, @$b ) { $union{$_}++ && $isect{$_}++; } return keys %isect; } __END__ =head1 NAME dirdiff - files diff for two directories. =head1 SYNOPSIS dirdiff [options] path1 [path2] Options: --debug set debug level, default is off --help brief help message --man full documentation --version version number --aonly files in a not in b only --bonly files in b not in a only --inter files in a and b --jonly files in a and b only --plain display result with out prefix; '>', '<' or '=' --order sorted display of result --ext allowed file extensions Switches that don't define a value can be done in long or short form. eg: dirdiff --man dirdiff -m =head1 OPTIONS =over 8 =item B<--debug> Display debug information as program is executed. Control is set by le +vel of the value passed on the command line. Default value is off (debug == 0). =item B<--help> Print a brief help message and exit. =item B<--man> Print the manual page (full documentation) and exit. =item B<--version> Print the version number and exit. =item B<--aonly> Print files in directory a not in directory b. =item B<--bonly> Print files in directory b not in directory a. =item B<--inter> Print files jointly in both directories (intersection of directory a a +nd directory b). =item B<--jonly> Print files jointly in both directories only. =item B<--plain> Print results free of decoration, i.e. no '>', '<' or '='. =item B<--order> Sort level. Defaults to the sort of no sort. Using this options will sort the results in ascending order. =item B<--ext> Allows for specification of file extension to limit result. May be use +d multiple times or with a comma separated list. =back =head1 DESCRIPTION dirdiff is a simple script to show a files diff for two directories. S +imilar to the real 'diff', things in directory a are prefixed with a '<'. Things in +directory b are prefixed with a '>' and things in both directories are prefixed wi +th a '='. The prefix may be removed with the --plain option. Options exist to displa +y results by themselves or in combination. The default setting is to display files +in a, not in b, and files in b, not in a. To add the intersection of a and b, us +e the --inter option. Note that the default entry for the path2 is '.'. =head1 AUTHOR Hugh S. Myers hsmyers@sdragons.com =head1 BUGS None that I know of. =head1 TODO 0. Edit POD. ONGOING. 1. Add some mechanism for wild card specification. DONE. 2. Add --stats to display date and size information with diff accordi +ngly. 3. Add --order to allow sorted output. DONE. =head1 UPDATES 1. Removed File::Spec::Functions in favor of Cwd, which has the virtu +re of working! 2. Added refactor ala Aristotle---thank you sir! 3. Added --ext to allow specifying extensions. 4. Added --order for sorted output. 5. Added use Sort::Naturally. 6. Aristotle refactors his refactor---thanks again! 7. Restored --order that my implementation of #6. stepped on! =cut

Update 1. Added Aristotle's suggestion/refactor.
Update 2. Added --order and --ext.
Update 3. Added Sort::Naturally and Aristotle's refactor to his refactor.
Update 4. Fix stepping on self with previous update!

--hsm

"Never try to teach a pig to sing...it wastes your time and it annoys the pig."

In reply to A directory diff by hsmyers

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.