fix two bugs writing the output file
[moreutils.git] / combine
bloba695935b24a5f2789c71a8affc4486859a41f737
1 #!/usr/bin/perl
3 =head1 NAME
5 combine - combine sets of lines from 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> combines 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 in file1 if they are also present in file2.
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 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.
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.
63 =head1 SEE ALSO
65 join(1)
67 =head1 AUTHOR
69 Copyright 2006 by Joey Hess <joey@kitenet.net>
71 Licensed under the GNU GPL.
73 =cut
75 use warnings;
76 use strict;
78 sub filemap {
79 my $file=shift;
80 my $sub=shift;
82 open (IN, $file) || die "$file: $!\n";
83 while (<IN>) {
84 chomp;
85 $sub->();
87 close IN;
90 sub hashify {
91 my $file=shift;
93 my %seen;
94 filemap $file, sub { $seen{$_}++ };
95 return \%seen;
98 sub compare_or {
99 my ($file1, $file2) = @_;
101 filemap $file1, sub { print "$_\n" };
102 filemap $file2, sub { print "$_\n" };
105 sub compare_xor {
106 my ($file1, $file2) = @_;
108 compare_not($file1, $file2);
109 compare_not($file2, $file1);
112 sub compare_not {
113 my ($file1, $file2) = @_;
115 my $seen=hashify($file2);
116 filemap $file1, sub { print "$_\n" unless $seen->{$_} };
119 sub compare_and {
120 my ($file1, $file2) = @_;
122 my $seen=hashify($file2);
123 filemap $file1, sub { print "$_\n" if $seen->{$_} };
126 if (@ARGV >= 4 && $ARGV[3] eq "_") {
127 delete $ARGV[3];
130 if (@ARGV != 3) {
131 die "Usage: combine file1 OP file2\n";
134 my $file1=shift;
135 my $op=lc shift;
136 my $file2=shift;
138 if ($::{"compare_$op"}) {
139 no strict 'refs';
140 "compare_$op"->($file1, $file2);
142 else {
143 die "unknown operation, $op\n";