add proper error handling for all final exec calls
[hband-tools.git] / tabdata / td-collapse
blobf61ec81d4e6777ed5513774ac2907ddc0aafc609
1 #!/usr/bin/env perl
3 =pod
5 =head1 NAME
7 td-collapse - Collapse multiple tabular data records with equivalent keys into one.
9 =head1 SYNOPSIS
11 td-collapse [I<OPTIONS>]
13 =head1 DESCRIPTION
15 It goes row-by-row on a sorted tabular data stream
16 and if 2 or more subsequent rows' first (key) cell are
17 the same then collapse them into one row.
18 This is done by joining corresponding cells' data from each row into one
19 cell, effectively keeping every column's data in the same column.
21 If you want to group by an other column, not the first one, then first
22 reorder the columns by td-select(1). Eg. C<td-select KEYCOLUMN +REST>.
24 =head1 OPTIONS
26 =over 4
28 =item -g, --glue I<STR>
30 Delimiter character or string between joined cell data.
31 Default is space.
33 =back
35 =head1 EXAMPLES
37 This pipeline shows which users are using each of the configured default
38 shells, grouped by shell path.
40 # get the list of users
41 getent passwd |\
43 # transform into tabular data stream
44 tr : "\t" |\
45 td-add-headers USER X UID GID GECOS HOME SHELL |\
47 # put the shell in the first column, and sort, then collapse
48 td-select SHELL USER | td-keepheader sort | td-collapse -g ' ' |\
50 # change header name "USER" to "USERS"
51 td-alter USERS=USER | td-select +ALL -USER
53 B<Output>:
55 | COUNT | SHELL | USERS |
56 | 4 | /bin/bash | user1 user2 nova root |
57 | 5 | /bin/false | fetchmail hplip sddm speech-dispatcher sstpc |
58 | 1 | /bin/sync | sync |
59 | 1 | /sbin/rebootlogon | reboot |
60 | 6 | /usr/sbin/nologin | _apt avahi avahi-autoipd backup bin daemon |
62 =head1 CAVEATS
64 Have to sort input data first.
66 =head1 SEE ALSO
68 td-expand(1) is a kind of inverse to td-collapse(1).
70 =head1 REFERENCES
72 td-collapse(1) roughly translates to SELECT COUNT(*) + GROUP_CONCAT() + GROUP BY in SQL.
74 =cut
77 $OptGlue = " ";
78 %OptionDefs = (
79 'g|glue=s' => \$OptGlue,
82 no if ($] >= 5.018), 'warnings' => 'experimental::smartmatch';
83 do '/usr/lib/tool/perl5/tabdata/common.pl' or die "$@";
85 process_header(scalar <STDIN>);
86 unshift @Header, "COUNT";
87 print join($FS, @Header).$RS;
90 sub output_last_group
92 print $collapsed_rows . $FS . $prev_group_key . $FS . join($FS, map { join $OptGlue, @$_ } @group_members) . $RS;
93 @group_members = ();
94 $collapsed_rows = 0;
97 $group_key = undef;
98 $prev_group_key = undef;
99 @group_members = ();
100 $collapsed_rows = 0;
102 while(not eof STDIN)
104 my @input_row = read_record(\*STDIN);
106 $group_key = $input_row[0];
107 output_last_group if defined $prev_group_key and $group_key ne $prev_group_key;
108 for my $cell_num (1..$#input_row)
110 push @{$group_members[$cell_num-1]}, $input_row[$cell_num];
112 $collapsed_rows++;
113 $prev_group_key = $group_key;
116 END { output_last_group; }