4

I have a need to edit cue files in the first directory and not go recursively in the subdirectories.

find(\&read_cue, $dir_source);
sub read_cue {
    /\.cue$/ or return;

    my $fd = $File::Find::dir;
    my $fn = $File::Find::name; 
    tie my @lines, 'Tie::File', $fn
      or die "could not tie file: $!";

    foreach (@lines) {
        s/some substitution//;
    }

    untie @lines;
}

I've tried variations of

$File::Find::prune = 1;
return;  

but with no success. Where should I place and define $File::Find::prune?

Thanks

1
  • 1
    Please edit your question to help us understand the layout of the directory tree with your cue files. Do you know which directory they're in beforehand? Are you using File::Find because they're at some unknown depth?
    – Greg Bacon
    Commented Sep 6, 2010 at 11:55

4 Answers 4

7

If you don't want to recurse, you probably want to use glob:

for  (glob("*.cue")) {
   read_cue($_);
}
3
  • I've never used the 'glob' function and have no idea where to put your suggestion in the sub. Commented Sep 5, 2010 at 19:14
  • 1
    @thebourneid The code from msw replaces your call to find(). Then simply remove the first 3 lines from your read_cue() method -- that is, all of the stuff related to File::Find, which is the wrong tool for the job in this case.
    – FMc
    Commented Sep 6, 2010 at 0:48
  • As I already pointed out in another comment ... only that glob gets thoroughly confused with blanks spaces inside the file/path segments. Commented Nov 25, 2011 at 18:24
5

If you want to filter the subdirectories recursed into by File::Find, you should use the preprocess function (not the $File::Find::prune variable) as this gives you much more control. The idea is to provide a function which is called once per directory, and is passed a list of files and subdirectories; the return value is the filtered list to pass to the wanted function, and (for subdirectories) to recurse into.

As msw and Brian have commented, your example would probably be better served by a glob, but if you wanted to use File::Find, you might do something like the following. Here, the preprocess function calls -f on every file or directory it's given, returning a list of files. Then the wanted function is called only for those files, and File::Find does not recurse into any of the subdirectories:

use strict;
use File::Find;

# Function is called once per directory, with a list of files and
# subdirectories; the return value is the filtered list to pass to
# the wanted function.
sub preprocess { return grep { -f } @_; }

# Function is called once per file or subdirectory.
sub wanted { print "$File::Find::name\n" if /\.cue$/; }

# Find files in or below the current directory.
find { preprocess => \&preprocess, wanted => \&wanted }, '.';

This can be used to create much more sophisticated file finders. For example, I wanted to find all files in a Java project directory, without recursing into subdirectories starting with ".", such as ".idea" and ".svn", created by IntelliJ and Subversion. You can do this by modifying the preprocess function:

# Function is called once per directory, with a list of files and
# subdirectories; return value is the filtered list to pass to the
# wanted function.
sub preprocess { return grep { -f or (-d and /^[^.]/) } @_; }
2
  • +1 The $File::Find::prune var is evil and has wasted enough of my time! Thanks for this answer!!
    – Steve
    Commented Mar 11, 2014 at 14:52
  • 2
    What is evil about the prune variable? Commented Jun 18, 2014 at 11:46
1

If you only want the files in a directory without searching subdirectories, you don't want to use File::Find. A simple glob probably does the trick:

my @files = glob( "$dir_source/*.cue" );

You don't need that subroutine. In general, when you're doing a lot of work for a task that you think should be simple, you're probably doing it wrong. :)

1
  • ... only that glob gets thoroughly confused with blanks spaces inside the file/path segments. Commented Nov 25, 2011 at 18:23
-1

Say you have a directory subtree with

/tmp/foo/file.cue
/tmp/foo/bar/file.cue
/tmp/foo/bar/baz/file.cue

Running

#! /usr/bin/perl

use warnings;
use strict;

use File::Find;

sub read_cue {
  if (-f && /\.cue$/) {
    print "found $File::Find::name\n";
  }
}

@ARGV = (".") unless @ARGV;
find \&read_cue => @ARGV;

outputs

found /tmp/foo/file.cue
found /tmp/foo/bar/file.cue
found /tmp/foo/bar/baz/file.cue

But if you remember the directories in which you found cue files

#! /usr/bin/perl

use warnings;
use strict;

use File::Find;

my %seen_cue;
sub read_cue {
  if (-f && /\.cue$/) {
    print "found $File::Find::name\n";
    ++$seen_cue{$File::Find::dir};
  }
  elsif (-d && $seen_cue{$File::Find::dir}) {
    $File::Find::prune = 1;
  }
}

@ARGV = (".") unless @ARGV;
find \&read_cue => @ARGV;

you get only the toplevel cue file:

found /tmp/foo/file.cue

That's because $File::Find::prune emulates the -prune option of find that affects directory processing:

-prune

True; if the file is a directory, do not descend into it.

1
  • either your question is really unclear, either your answer does not work. OK, you prune subdirectories, but parallel directories will still be searched (just replace your %seen_cue hash with a simple scalar to change that). Also as others pointed out this solution looks overly complex to look in only one directory.
    – kriss
    Commented Sep 5, 2010 at 23:30

Not the answer you're looking for? Browse other questions tagged or ask your own question.