]> err.no Git - moreutils/blob - combine
Update name one more place
[moreutils] / combine
1 #!/usr/bin/perl
2
3 =head1 NAME
4
5 combine - combine sets of lines from two files using boolean operations
6
7 =head1 SYNOPSIS
8
9 combine file1 and file2
10
11 combine file1 not file2
12
13 combine file1 or file2
14
15 combine file1 xor file2
16
17 _ file1 and file2 _
18
19 _ file1 not file2 _
20
21 _ file1 or file2 _
22
23 _ file1 xor file2 _
24
25 =head1 DESCRIPTION
26
27 B<combine> combines the lines in two files. Depending on the boolean
28 operation specified, the contents will be combined in different ways:
29
30 =over 4
31
32 =item and
33
34 Outputs lines that are in file1 if they are also present in file2.
35
36 =item not
37
38 Outputs lines that are in file1 but not in file2.
39
40 =item or
41
42 Outputs lines that are in file1 or file2.
43
44 =item xor
45
46 Outputs lines that are in either file1 or file2, but not in both files.
47
48 =back
49
50 "-" can be specified for either file to read stdin for that file.
51
52 The input files need not be sorted, and the lines are output in the order
53 they occur in file1 (followed by the order they occur in file2 for the two
54 "or" operations). Bear in mind that this means that the operations are not
55 commutative; "a and b" will not necessarily be the same as "b and a". To
56 obtain commutative behavior sort and uniq the result.
57
58 Note that this program can be installed as "_" to allow for the syntactic
59 sugar shown in the latter half of the synopsis (similar to the test/[
60 command). It is not currently installed as "_" by default, but you can
61 alias it to that if you like.
62
63 =head1 SEE ALSO
64
65 join(1)
66
67 =head1 AUTHOR
68
69 Copyright 2006 by Joey Hess <joey@kitenet.net>
70
71 Licensed under the GNU GPL.
72
73 =cut
74
75 use warnings;
76 use strict;
77
78 sub filemap {
79         my $file=shift;
80         my $sub=shift;
81
82         open (IN, $file) || die "$file: $!\n";
83         while (<IN>) {
84                 chomp;
85                 $sub->();
86         }
87         close IN;
88 }
89
90 sub hashify {
91         my $file=shift;
92
93         my %seen;
94         filemap $file, sub { $seen{$_}++ };
95         return \%seen;
96 }
97
98 sub compare_or {
99         my ($file1, $file2) = @_;
100
101         filemap $file1, sub { print "$_\n" };
102         filemap $file2, sub { print "$_\n" };
103 }
104
105 sub compare_xor {
106         my ($file1, $file2) = @_;
107
108         my (@lines2, %seen2);
109         filemap $file2,
110                 sub {
111                         push @lines2, $_;
112                         $seen2{$_} = 1;
113                 };
114
115         # Print all lines in file1 that are not in file2,
116         # and mark lines that are in both files by setting
117         # their value in %seen2 to 0.
118         filemap $file1,
119                 sub {
120                         if (exists $seen2{$_}) {
121                                 $seen2{$_} = 0;
122                         }
123                         else {
124                                 print "$_\n";
125                         }
126                 };
127
128         # Print all lines that are in file2 but not in file1.
129         # The value of these lines in seen2 is set to 1.
130         foreach (@lines2) {
131                 print "$_\n" if $seen2{$_};
132         }
133 }
134
135 sub compare_not {
136         my ($file1, $file2) = @_;
137
138         my $seen=hashify($file2);
139         filemap $file1, sub { print "$_\n" unless $seen->{$_} };
140 }
141
142 sub compare_and {
143         my ($file1, $file2) = @_;
144
145         my $seen=hashify($file2);
146         filemap $file1, sub { print "$_\n" if $seen->{$_} };
147 }
148
149 if (@ARGV >= 4 && $ARGV[3] eq "_") {
150         delete $ARGV[3];
151 }
152
153 if (@ARGV != 3) {
154         die "Usage: combine file1 OP file2\n";
155 }
156
157 my $file1=shift;
158 my $op=lc shift;
159 my $file2=shift;
160
161 if ($::{"compare_$op"}) {
162         no strict 'refs';
163         "compare_$op"->($file1, $file2);
164 }
165 else {
166         die "unknown operation, $op\n";
167 }