简体   繁体   中英

Passing Subroutine to Perl Subroutine

I'm trying to make a subroutine that mimics Perl6/Raku's dir . dir can search directories very easily and quickly, with file tests in a directory.

I'm trying to add a recursive option to dir .

I have a script that can accomplish this, but I'm trying to add a recursive option using File::Find script that I already know works:

use File::Find;
my @files;
find({ wanted => \&process_file, no_chdir => 1 }, '.');
sub process_file {
    if ((-f $_) && ($_ =~ m/\.pl$/)) {#various tests on the files
            push @files, $_;
#        print "This is a file: $_\n";
    }
}

However, I want to do everything in a single subroutine.

I have tried this:

use strict;
use warnings FATAL => 'all';
use feature 'say';
use autodie ':all';
use Carp 'confess';
use File::Find 'find';

sub dir { # a Perl5 mimic of Perl6/Raku's "dir"
    my %args = ( # default arguments listed below
        tests => 'f', dir => '.', regex   => '.', recursive => 0,
    @_, # argument pair list goes here
    );
    if ($args{tests} =~ m/[^rwxoRWXOezfdlpSbctugkTB]/){# sMAC returns are non-boolean, and are excluded
        confess "There are some unrecognized characters in $args{tests}";
    }
    my @items = split '', $args{tests};
    my $tests = '-' . join (' -', @items);
    undef @items;
    $args{regex} = qr/$args{regex}/;
    opendir my $dh, $args{dir} or die "Can't opendir on $args{dir}: $!";
    while (my $item = readdir $dh) {
        if (($item !~ $args{regex}) or ($item =~ m/^\.{1,2}$/)) { next  }
        my $i = "$args{dir}/$item";
        if (not defined $i) {
            confess "\$i isn't defined.";
        }
        my $rv = eval "${tests} \$i" || 0;
        if ($rv == 0) { next }
        if ($args{dir} eq '.') {
            push @items, $item
        } else {
            push @items, $i
        }
    }
# this is the new part, and what doesn't work
    if ($args{recursive} == 1) {
        find({
            wanted => \sub {
                if ((eval "${tests} $_") && ($_ =~ /$args{regex}/)) {
                    push @items, $_;
                }
            },
            chdir => 1,
            '.'
        });
    }
    @items
}
foreach my $pl (dir(recursive => 1, regex => '\.pl$')) {
    say $pl
}

this gives an error:

Odd number of elements in anonymous hash at clean.pl line 44

I think that this is because of the find function in the bottom, as the error indicates.

Passing one subroutine to another subroutine is somewhat similar to my question, but I don't see how to apply there to my case.

Passing wanted => sub and wanted => \sub gives the same error.

How can I get this wanted subroutine to pass to find without errors?

Perl tries to use every odd argument as hash key and every even one as hash value, so with an odd number of arguments you get that "odd number of arguments" error cause you put all arguments to find in an anonymous hash ref. The file name / path has no hash key, so move that out of the hashref.

Then pass the wanted routine like this:

wanted => sub { ... }

That said, my File::Find only knows no_chdir , not chdir , so you probably want to switch the value.

This works for me:

find(
    {
        wanted => sub {
            if ((eval "${tests} $_") && ($_ =~ /$args{regex}/)) {
                push @items, $_;
            }
        },
        no_chdir => 0,
    },
    '.'    # File outside the hash ref
);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM