releasing version 0.5
[moreutils.git] / combine
blob955ddb8a3f199b39378fd480dfcb327a8c7316e3
1 #!/usr/bin/perl
3 =head1 NAME
5 combine - combine the lines in two files using boolean operations
7 =head1 SYNOPSIS
9 combine file1 and file2
11 combine file1 not file2
13 combine file1 or file2
15 combine file1 xor file2
17 _ file1 and file2 _
19 _ file1 not file2 _
21 _ file1 or file2 _
23 _ file1 xor file2 _
25 =head1 DESCRIPTION
27 B<combine> conbines the lines in two files. Depending on the boolean
28 operation specified, the contents will be combined in different ways:
30 =over 4
32 =item and
34 Outputs lines that are common to both files.
36 =item not
38 Outputs lines that are in file1 but not in file2.
40 =item or
42 Outputs lines that are in file1 or file2.
44 =item xor
46 Outputs lines that are in either file1 or file2, but not in both files.
48 =back
50 "-" can be specified for either file to read stdin for that file.
52 The input files need not be sorted, and the lines are output in the order
53 they accur in file1 (or file2 for the two "or" operations).
55 Note that this program can be installed as "_" to allow for the syntactic
56 sugar shown in the latter half of the synopsis (similar to the test/[
57 command). It is not currently installed as "_" by default, but you can
58 alias it to that if you like.
60 =head1 AUTHOR
62 Copyright 2006 by Joey Hess <joey@kitenet.net>
64 Licensed under the GNU GPL.
66 =cut
68 use warnings;
69 use strict;
71 sub filemap {
72 my $file=shift;
73 my $sub=shift;
75 open (IN, $file) || die "$file: $!\n";
76 while (<IN>) {
77 chomp;
78 $sub->();
80 close IN;
83 sub hashify {
84 my $file=shift;
86 my %seen;
87 filemap $file, sub { $seen{$_}++ };
88 return \%seen;
91 sub compare_or {
92 my ($file1, $file2) = @_;
94 my $seen;
95 filemap $file1, sub { print "$_\n"; $seen->{$_}++ };
96 filemap $file2, sub { print "$_\n" unless $seen->{$_} };
99 sub compare_xor {
100 my ($file1, $file2) = @_;
102 compare_not($file1, $file2);
103 compare_not($file2, $file1);
106 sub compare_not {
107 my ($file1, $file2) = @_;
109 my $seen=hashify($file2);
110 filemap $file1, sub { print "$_\n" unless $seen->{$_} };
113 sub compare_and {
114 my ($file1, $file2) = @_;
116 my $seen=hashify($file2);
117 filemap $file1, sub { print "$_\n" if $seen->{$_} };
120 if (@ARGV >= 4 && $ARGV[3] eq "_") {
121 delete $ARGV[3];
124 if (@ARGV != 3) {
125 die "usage: combine file1 OP file2\n";
128 my $file1=shift;
129 my $op=shift;
130 my $file2=shift;
132 if ($::{"compare_$op"}) {
133 no strict 'refs';
134 "compare_$op"->($file1, $file2);
136 else {
137 die "unknown operation, $op\n";