App-csvtool
view release on metacpan or search on metacpan
lib/App/csvtool.pm view on Meta::CPAN
# You may distribute under the terms of either the GNU General Public License
# or the Artistic License (the same terms as Perl itself)
#
# (C) Paul Evans, 2021-2024 -- leonerd@leonerd.org.uk
package App::csvtool 0.04;
use v5.26;
use warnings;
use experimental 'signatures';
use Commandable 0.11;
=head1 NAME
C<App::csvtool> - implements the F<csvtool> core commands
=head1 DESCRIPTION
This module provides the main commands for the F<csvtool> wrapper script.
=head1 COMMANDS
=cut
package App::csvtool::cut
{
=head2 cut
$ csvtool cut -fFIELDS INPUT...
Extracts the given field column(s).
=head3 --fields, -f
A comma-separated list of field indexes (defaults to 1).
A field index of C<u> will result in an undefined (i.e. empty) field being
emitted. This can be used to create spaces and pad out the data.
=cut
use constant COMMAND_DESC => "Extract the given field(s) to output";
use constant COMMAND_OPTS => (
{ name => "fields|f=", description => "Comma-separated list of fields to extract",
default => "1" },
);
use constant WANT_READER => 1;
use constant WANT_OUTPUT => 1;
sub run ( $pkg, $opts, $reader, $output )
{
my @FIELDS = split m/,/, $opts->{fields};
# 1-indexed
$_ eq "u" || $_-- for @FIELDS;
while( my $row = $reader->() ) {
$output->( [ map { $_ eq "u" ? undef : $row->[$_] } @FIELDS ] );
}
}
}
package App::csvtool::grep
{
=head2 grep
$ csvtool grep PATTERN INPUT...
Filter rows by the given pattern. The pattern is always interpreted as a Perl
regular expression.
=head3 --ignore-case, -i
Ignore case when matching.
=head3 --invert-match, -v
Output only the lines that do not match the filter pattern.
=cut
use constant COMMAND_DESC => "Filter rows based on a regexp pattern";
use constant COMMAND_OPTS => (
{ name => "field|f=", description => "Field to filter by",
default => 1 },
{ name => "ignore-case|i", description => "Match ignoring case" },
{ name => "invert-match|v", description => "Selects only the non-matching rows" },
);
use constant COMMAND_ARGS => (
{ name => "pattern", description => "regexp pattern for filtering" },
);
use constant WANT_READER => 1;
use constant WANT_OUTPUT => 1;
sub run ( $pkg, $opts, $pattern, $reader, $output )
{
my $FIELD = $opts->{field};
my $INVERT = $opts->{invert_match} // 0;
$pattern = "(?i:$pattern)" if $opts->{ignore_case};
# 1-based
$FIELD--;
my $re = qr/$pattern/;
while( my $row = $reader->() ) {
$output->( $row ) if $INVERT ^ $row->[ $FIELD ] =~ $re;
( run in 0.455 second using v1.01-cache-2.11-cpan-3782747c604 )