Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I have a command-line download tool that extracts a list of filenames/urls upon which to operate. I added support to filter the list by accepting regex strings from --accept and --reject options. What I have works, but feels off because of the string eval. Is there another approach to this?
GetOptions( 'accept|a=s' => \my @accept, 'reject|r=s' => \my @reject, ) or die "Error with options\n"; my @filter = map eval "sub { \$_[0] =~ /$_/ }", @accept; push @filter, map eval "sub { \$_[0] !~ /$_/ }", @reject; # my @input = ([url, filename], ...); my @filtered = filter(@input); sub filter { return unless @filter; return map $_->[0], grep { my $f = $_; all { $_->($f->[1]) } @filter } @_; }

Replies are listed 'Best First'.
Re: Adding regex filters from command-line options
by ikegami (Patriarch) on Mar 17, 2025 at 19:12 UTC

    Use a closure which captures the pattern.

    my @filters = ( ( map { my $re = $_; sub { $_[0] =~ $re } } @accept ), ( map { my $re = $_; sub { $_[0] !~ $re } } @reject ), );

    You could even pre-compile the patterns.

    my @filters = ( ( map { my $re = qr/$_/; sub { $_[0] =~ $re } } @accept ), ( map { my $re = qr/$_/; sub { $_[0] !~ $re } } @reject ), );

    I suspect that building a single pattern would be faster, though.

    my $filter; if ( @accept || @reject ) { $filter = join "", ( map "(?=(?s:.*?)$_)", @accept ), ( map "(?!(?s:.*?)$_)", @reject ); $filter = qr/^$filter/; } else { $filter = qr/(*FAIL)/; } sub filter { map $_->[0], grep { $_->[1] =~ $filter } @_ }
      Nice! But note the first two solutions require an extra set of enclosing parenthesis or the @reject filters will silently not be applied.

        woops! Fixed.

Re: Adding regex filters from command-line options
by Anonymous Monk on Mar 25, 2025 at 08:29 UTC
    In "all { $_->($f->1) }" what is "all"? is it a Perl keyword?

      Is it most likely List::Util::all and the snippet given in the OP just does not include the use List::Util 'all'; line.


      🦛