#!/usr/bin/perl -w # # $Id: fs-compare.pl,v 1.17 2003/01/13 05:28:43 jmates Exp $ # # Copyright (c) 2002, Jeremy Mates. This script is free software; # you can redistribute it and/or modify it under the same terms as # Perl itself. # # Run perldoc(1) on this file for additional documentation. # ###################################################################### # # REQUIREMENTS require 5; use strict; ###################################################################### # # MODULES use Carp; # better error reporting use Getopt::Std; # command line option processing ###################################################################### # # VARIABLES my $VERSION; ($VERSION = '$Revision: 1.17 $ ') =~ s/[^0-9.]//g; my (%opts, %A, %B, $only_first, $quiet, @option, @optionb, %exclude, %passthru); ###################################################################### # # MAIN # parse command-line options getopts('h?1e:p:q', \%opts); help() if exists $opts{'h'} or exists $opts{'?'}; # read from STDIN if no args left chomp(@ARGV = ) unless @ARGV; # and flag the help text if nothing from STDIN help() unless @ARGV; $quiet = 1 if exists $opts{'q'}; $only_first = 1 if exists $opts{'1'}; if (exists $opts{'e'}) { @exclude{split /[ ,]/, $opts{'e'}} = undef; } if (exists $opts{'p'}) { @passthru{split /[ ,]/, $opts{'p'}} = undef; } my $filea = shift; my $fileb = shift; open FILEA, $filea or die "problem opening $filea: $!\n"; open FILEB, $fileb or die "problem opening $fileb: $!\n"; while (not eof FILEA or not eof FILEB) { unless (eof FILEA) { my $linea = ; my $skipa; chomp $linea; if ($linea =~ /^#/) { if ($linea =~ /^# Options: (.+)$/) { @option = split / /, $1; } $skipa = 1; } if ($linea =~ /^\s*$/) { $skipa = 1; } unless ($skipa) { my @tmp = split /\t/, $linea; if (exists $B{$tmp[0]}) { if ($only_first) { print "I\t", $tmp[0], "\n"; } else { review($tmp[0], [@tmp[1 .. $#tmp]], $B{$tmp[0]}); } delete $B{$tmp[0]}; } else { $A{$tmp[0]} = [@tmp[1 .. $#tmp]]; } } } unless (eof FILEB) { my $lineb = ; my $skipb; chomp $lineb; if ($lineb =~ /^#/) { if ($lineb =~ /^# Options: (.+)$/) { @optionb = split / /, $1; } $skipb = 1; } if ($lineb =~ /^\s*$/) { $skipb = 1; } unless ($skipb) { my @tmp = split /\t/, $lineb; if (exists $A{$tmp[0]}) { if ($only_first) { print "I\t", $tmp[0], "\n"; } else { review($tmp[0], $A{$tmp[0]}, [@tmp[1 .. $#tmp]]); } delete $A{$tmp[0]}; } else { $B{$tmp[0]} = [@tmp[1 .. $#tmp]]; } } } } # maybe these should honor passthru so can see f/d types and stuff?? # should also look into maintaining insertion order... for (sort keys %A) { print "A\t", $_, "\n"; } for (sort keys %B) { print "B\t", $_, "\n"; } # sanity check on @option arguments if ("@option" ne "@optionb") { warn "Warning: option headers differ between files, differences may be bogus\n"; } exit; ###################################################################### # # SUBROUTINES # compares and reports on subelement differences... # this allows showing identical files whose size/date/etc has changed sub review { my $filepath = shift; my $ref_a = shift; my $ref_b = shift; my ($different, @results); for (0 .. $#{@$ref_a}) { $ref_a->[$_] = '' unless defined $ref_a->[$_]; $ref_b->[$_] = '' unless defined $ref_b->[$_]; if (exists $passthru{($option[$_ + 1] || $_ + 1)}) { push @results, ($option[$_ + 1] || $_ + 1) . ':' . $ref_a->[$_]; } unless (exists $exclude{($option[$_ + 1] || $_ + 1)}) { if ($ref_a->[$_] ne $ref_b->[$_]) { push @results, ($option[$_ + 1] || $_ + 1) . ':' . $ref_a->[$_] . '|' . $ref_b->[$_]; $different = 1; } } } if ($different) { print "D\t", $filepath, "\t", join ("\t", @results), "\n"; } else { print "I\t", $filepath, "\n"; } } # a generic help blarb sub help { print <<"HELP"; Usage: $0 [opts] [file1 file2] Compares output of fs-snapshot.pl runs. Options for version $VERSION: -h/-? Display this message. -q Become less chatty about various things. -1 Only consider first column for differences. -e xx Exclude columns xx from difference considerations. -p yy Force columns yy to be displayed in output. Run perldoc(1) on this script for additional documentation. HELP exit; } ###################################################################### # # DOCUMENTATION =head1 NAME fs-compare.pl - compares output of fs-snapshot.pl runs. =head1 SYNOPSIS $ fs-compare.pl snap-before snap-after > changes =head1 DESCRIPTION =head2 Overview This program reports the differences between two file system snapshots made with the fs-snapshot.pl utility. As such, this script expects a specific format, at present a series of tab separated values, with the full filename being the first element. The input data may need to be sorted first, if the snapshots were generated on two different physical filesystems. This is due to the snapshot utility listing files in the default order as returned by readdir(3) in L, which may be different on different systems. Output is tab separated; the first field denotes whether the file is only in the first (A), only in the second (B), identical (I), or present in both but with other differences (D). The second field contains the filepath. Subsequent fields are used to show the specific differences between entries for D results. =head2 Normal Usage $ fs-compare.pl [options] [file1 file2] See L<"OPTIONS"> for details on the command line switches supported. Two filenames are required. If neither is found on the command line, they will be looked for on STDIN. =head1 OPTIONS This script currently supports the following command line switches: =over 4 =item B<-h>, B<-?> Prints a brief usage note about the script. =item B<-q> Become less chatty about various things. =item B<-1> Only consider the first column of data in each file when computing the differences. This disables all D-labeled output. =item B<-e> I Exclude options in the supplied list from consideration when listing differences. Allows one to ignore differences such as ctime or mtime to focus on other changes in the supplied files. Format is a comma or space separated list of option names (from the Options: line in the data file). If there is no Options line, column numbers can be used instead, starting from column 2 (column 1 is the filename, which is not considered when doing sub-differences). =item B<-p> I Force the named options to appear first in the output. This allows the inclusion of columns to cross reference other differences against. Such options passed through will only have a single value such as C, as opposed to excluded entries, which look like C. =back =head1 EXAMPLES Compare two fs-snapshot.pl runs (files C and C), excluding C and C from difference display, and including the C column for easy reference. $ fs-compare.pl -e mtime,ctime -p type one two D /tmp/test type:d size:92|126 D /tmp/test/a type:f size:0|4 I /tmp/test/b B /tmp/test/c =head1 BUGS =head2 Reporting Bugs Newer versions of this script may be available from: http://sial.org/code/perl/ If the bug is in the latest version, send a report to the author. Patches that fix problems or add new features are welcome. =head2 Known Issues No known issues. =head1 SEE ALSO perl(1) =head1 AUTHOR Jeremy Mates, http://sial.org/contact/ =head1 COPYRIGHT Copyright (c) 2002, Jeremy Mates. This script is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 VERSION $Id: fs-compare.pl,v 1.17 2003/01/13 05:28:43 jmates Exp $ =cut