anonymized user 468275 has asked for the wisdom of the Perl Monks concerning the following question:

Dear monks,

I have a script whose job is nominally to find all files whose modification took place between two exact times. File::Find seemed appropriate for the job, but before making the script more flexible to handle future requirements, I wanted to remove the annoyance of it trying to cd into protected areas such as lost+found and generating error messages in the middle of its output. Of course, I could run it with script 2>/dev/null, but let's assume that for the future requirements this will be unnacceptable - the code either has to suppress any error messages that happen in the find mechanism or prevent them happening by controlling where it actually cds.

If I could get away from needing the 'wanted' subroutine (I can't unless I allow find to get all files and then postprocess them which rather defeats the point of using find instead of my own traversal routine anyway), I could always use the no_chdir option to prevent the behaviour. But the syntax of find doesn't allow both %options and \&wanted. So, learned monks, I should be grateful to know what you would recommend.

Thanks in advance,

Simon.

#!/usr/bin/perl use File::Find; use Time::Local; use strict; use vars qw ( @times ); use warnings; my @trees = ( '/home/ftp/pub/', $ENV{ AC_SYSTEM }, $ENV{ AC_WORKDIR } ); # the two time limits between which files are to be searched local @times = ( timelocal(0,0,15,15,7,105), timelocal(0,0,18,16,7,105) ); find( \&wanted, @trees ); sub wanted { # $_ contains the filename without path /^\./ and return; # clue: it still cd's into these! my $got = $File::Find::name; ( -f $got ) or return; my @stat = stat($got) or return; ( $stat[9] > $times[0] ) or return; ( $stat[9] < $times[1] ) or return; print "$got\n"; # if it cleared all the checks print it }

Update: Unfortunately, having used grandfather's point to solve the dichotomy, the no_chdir option was insufficient to prevent it from issuing error messages for protected directories, so as yet I see no alternative but to abandon Find and write my own Traverse routine that tests the protection before trying to recurse into any given directory - if I use glob, I only expect to be adding a couple of lines compared to the find version.

One world, one people

Replies are listed 'Best First'.
Re: File::Find managing the %options versus \&wanted dichotomy
by GrandFather (Saint) on Aug 17, 2005 at 10:39 UTC

    You put the \&wanted into the options hash. Example from documentation:

    use File::Find; find({ wanted => \&process, follow => 1 }, '.');

    Note also that \&wanted can return 0 to prune the tree (prevent processing sub dirs below the current one).


    Perl is Huffman encoded by design.
      Thanks - the wanted in the options hash solves the (false) dichotomy nicely.

      However, the return of 0 to prune the tree won't work because, for example, lost+found (an example included in the OP) is immediately below the mount point of its device - so that would mean pruning everything except the top directory, to take an extreme example of that approach.

      One world, one people

Re: File::Find managing the %options versus \&wanted dichotomy
by adrianh (Chancellor) on Aug 17, 2005 at 10:59 UTC
    what would you recommend?

    File::Find::Rule

    use strict; use warnings; use File::Find::Rule; use Time::Local; my ($min_time, $max_time) = ( timelocal(0,0,15,15,7,105), timelocal(0,0,18,16,7,105) ); my @directories = ( '/home/ftp/pub/', $ENV{ AC_SYSTEM }, $ENV{ AC_WORK +DIR } ); print File::Find::Rule->file->mtime(">$min_time")->mtime("<$max_time") ->in( @directories );
      The rule idea is very neat, but not a core module and unfortunately cannot be used in the actual situation. Also this solution implements a subset of the rules managed by the code in the OP that already works, but it doesn't add anything, specifically it doesn't address the problem of avoiding protected directories, so unfortunately the particular example fails to justify using Find::File::Rule as an alternative.

      One world, one people

        The rule idea is very neat, but not a core module and unfortunately cannot be used in the actual situation

        Why not? It's pure perl so you can always just copy 'n' paste if you have to.

        Also this solution implements a subset of the rules managed by the code in the OP that already works, but it doesn't add anything, specifically it doesn't address the problem of avoiding protected directories, so unfortunately the particular example fails to justify using Find::File::Rule as an alternative.

        Did you try it? It does exactly what the code in the OP does, in addition to not producing any error messages. F::F::R is bright enough to not chdir into places it's not allowed.

Re: File::Find managing the %options versus \&wanted dichotomy
by bmann (Priest) on Aug 17, 2005 at 16:13 UTC
    Have you considered no warnings 'File::Find'; to suppress those pesky warnings? Uncomment 'no warnings' in the following and the warning is no longer issued.
    $ mkdir a $ chmod -r a $ cat ff.pl #!/usr/bin/perl use warnings; use strict; use File::Find; #no warnings 'File::Find'; find({ wanted => \&wanted }, '.'); sub wanted { /^myscript$/ && print "$_\n" } $ perl ff.pl Can't opendir(./a): Permission denied at ff.pl line 8
    hth
      When testing your suggestion, it's true I didn't get a warning. I got a fatal error instead:
      perl -e 'use File::Find; no warnings 'File::Find'; @fred = ( $ENV{ AC_ +SYSTEM } ); find wanted => undef(), @fred;'
      produced
      unknown warnings category 'File::Find' at -e line 1 BEGIN failed--compilation aborted at -e line 1.
      But more to the point, if it's less code to write when you don't use the module, why bother?

      One world, one people

        You must be using 5.6, right? My version of warnings with 5.8.7 is 1.09, with 5.6.1 there is no version number and it doesn't allow you to suppress warnings:(

        But more to the point, if it's less code to write when you don't use the module, why bother?

        I bother because File::Find handles things like symlinks without me having to think about it. I prefer peer tested, "correct" code to "short" code any time. (That last sentence is about my code, not a criticism of yours.)

Re: File::Find managing the %options versus \&wanted dichotomy
by anonymized user 468275 (Curate) on Aug 17, 2005 at 14:17 UTC
    And this is the version so far with Find replaced by a local subroutine to avoid the new dichotomy of non-core module versus protection errors - surprisingly, it's less code to write and will also perform quicker than using File::Find. The protection test means there are now functionally a total of four rules (counting the within time interval as a single rule).
    #!/usr/bin/perl use Time::Local; use strict; use vars qw ( @times ); use warnings; my @trees = ( '/home/ftp/pub', $ENV{ AC_SYSTEM }, $ENV{ AC_WORKDIR } ); local @times = ( timelocal(0,0,15,14,7,105), timelocal(0,0,18,16,7,105 +) ); for ( @trees ) { MyFind( $_ ); } sub MyFind { for my $file ( grep !/^\./, glob ( shift() . '/*' ) ) { if ( -d $file ) { ( -x $file ) and MyFind( $file ); } else { my @stat = stat $file; ( $stat[9] > $times[0] ) and ( $stat[9] < $times[1] ) and print "$file\n"; } } }

    One world, one people