SYNOPSIS

  ## See File::Finder for normal use of steps

  ## subclassing example:
  BEGIN {
    package My::File::Finder;
    use base File::Finder;

    sub _steps_class { "My::File::Finder::Steps" }
  }
  BEGIN {
    package My::File::Finder::Steps;
    use base File::Finder::Steps;

    sub bigger_than { # true if bigger than N bytes
      my $self = shift;
      my $bytes = shift;
      return sub {
        -s > $bytes;
      }
    }
  }

  my $over_1k = My::File::Finder->bigger_than(1024);
  print "Temp files over 1k:\n";
  $over_1k->ls->in("/tmp");

DESCRIPTION

\*(C`File::Finder::Steps\*(C' provide the predicates being tested for \*(C`File::Finder\*(C'.

\s-1STEPS\s0 \s-1METHODS\s0

These methods are called on a class or instance to add a \*(L"step\*(R". Each step adds itself to a list of steps, returning the new object. This allows you to chain steps together to form a formula.

As in find, the default operator is \*(L"and\*(R", and short-circuiting is performed.

or

Like find's \*(C`or\*(C'.

left

Like a left parenthesis. Used in nesting pairs with \*(C`right\*(C'.

right

Like a right parenthesis. Used in nesting pairs with \*(C`left\*(C'. For example: my $big_or_old = File::Finder ->type('f') ->left ->size("+100")->or->mtime("+90") ->right; find($big_or_old->ls, "/tmp"); You need parens because the \*(L"or\*(R" operator is lower precedence than the implied \*(L"and\*(R", for the same reason you need them here: find /tmp -type f '(' -size +100 -o -mtime +90 ')' -print Without the parens, the -type would bind to -size, and not to the choice of -size or -mtime. Mismatched parens will not be found until the formula is used, causing a fatal error.

begin

Alias for \*(C`left\*(C'.

end

Alias for \*(C`right\*(C'.

not

Like find's \*(C`!\*(C'. Prefix operator, can be placed in front of individual terms or open parens. Can be nested, but what's the point? # list all non-files in /tmp File::Finder->not->type('f')->ls->in("/tmp");

true

Always returns true. Useful when a subexpression might fail, but you don't want the overall code to fail: ... ->left-> ...[might return false]... ->or->true->right-> ... Of course, this is the find command's idiom of: find .... '(' .... -o -true ')' ...

false

Always returns false.

comma

Like \s-1GNU\s0 find's \*(L",\*(R". The result of the expression (or subexpression if in parens) up to this point is discarded, and execution continues afresh. Useful when a part of the expression is needed for its side effects, but shouldn't affect the rest of the \*(L"and\*(R"-ed chain. # list all files and dirs, but don't descend into CVS dir contents: File::Finder->type('d')->name('CVS')->prune->comma->ls->in('.');

follow

Enables symlink following, and returns true.

name(\s-1NAME\s0)

True if basename matches \s-1NAME\s0, which can be given as a glob pattern or a regular expression object: my $pm_files = File::Finder->name('*.pm')->in('.'); my $pm_files_too = File::Finder->name(qr/pm$/)->in('.');

perm(\s-1PERMISSION\s0)

Like find's \*(C`-perm\*(C'. Leading \*(L"-\*(R" means \*(L"all of these bits\*(R". Leading \*(L"+\*(R" means \*(L"any of these bits\*(R". Value is de-octalized if a leading 0 is present, which is likely only if it's being passed as a string. my $files = File::Finder->type('f'); # find files that are exactly mode 644 my $files_644 = $files->perm(0644); # find files that are at least world executable: my $files_world_exec = $files->perm("-1"); # find files that have some executable bit set: my $files_exec = $files->perm("+0111");

type(\s-1TYPE\s0)

Like find's \*(C`-type\*(C'. All native Perl types are supported. Note that \*(C`s\*(C' is a socket, mapping to Perl's \*(C`-S\*(C', to be consistent with find. Returns true or false, as appropriate.

print

Prints the fullname to \*(C`STDOUT\*(C', followed by a newline. Returns true.

print0

Prints the fullname to \*(C`STDOUT\*(C', followed by a \s-1NUL\s0. Returns true.

fstype

Not implemented yet.

user(USERNAME|UID)

True if the owner is \s-1USERNAME\s0 or \s-1UID\s0.

group(GROUPNAME|GID)

True if the group is \s-1GROUPNAME\s0 or \s-1GID\s0.

nouser

True if the entry doesn't belong to any known user.

nogroup

True if the entry doesn't belong to any known group.

links( +/- N )

Like find's \*(C`-links N\*(C'. Leading plus means \*(L"more than\*(R", minus means \*(L"less than\*(R".

inum( +/- N )

True if the inode number meets the qualification.

size( +/- N [c/k])

True if the file size meets the qualification. By default, N is in half-K blocks. Append a trailing \*(L"k\*(R" to the number to indicate 1K blocks, or \*(L"c\*(R" to indicate characters (bytes).

atime( +/- N )

True if access time (in days) meets the qualification.

mtime( +/- N )

True if modification time (in days) meets the qualification.

ctime( +/- N )

True if inode change time (in days) meets the qualification.

exec(@COMMAND)

Forks the child process via \*(C`system()\*(C'. Any appearance of \*(C`{}\*(C' in any argument is replaced by the current filename. Returns true if the child exit status is 0. The list is passed directly to \*(C`system\*(C', so if it's a single arg, it can contain \*(C`/bin/sh\*(C' syntax. Otherwise, it's a pre-parsed command that must be found on the \s-1PATH\s0. Note that I couldn't figure out how to horse around with the current directory very well, so I'm using $_ here instead of the more traditional \*(C`File::Find::name\*(C'. It still works, because we're still chdir'ed down into the directory, but it looks weird on a trace. Trigger \*(C`no_chdir\*(C' in \*(C`find\*(C' if you want a traditional find full path. my $f = File::Finder->exec('ls', '-ldg', '{}'); find({ no_chdir => 1, wanted => $f }, @starting_dirs); Yeah, it'd be trivial for me to add a no_chdir method. Soon.

ok(@COMMAND)

Like \*(C`exec\*(C', but displays the command line first, and waits for a response. If the response begins with \*(C`y\*(C' or \*(C`Y\*(C', runs the command. If the command fails, or the response wasn't yes, returns false, otherwise true.

prune

Sets $File::Find::prune, and returns true.

xdev

Not yet implemented.

newer

Not yet implemented.

eval(\s-1CODEREF\s0)

Ah yes, the master escape, with extra benefits. Give it a coderef, and it evaluates that code at the proper time. The return value is noted for true/false and used accordingly. my $blaster = File::Finder->atime("+30")->eval(sub { unlink }); But wait, there's more. If the parameter is an object that responds to \*(C`as_wanted\*(C', that method is automatically called, hoping for a coderef return. This neat feature allows subroutines to be created and nested: my $old = File::Finder->atime("+30"); my $big = File::Finder->size("+100"); my $old_or_big = File::Finder->eval($old)->or->eval($big); my $killer = File::Finder->eval(sub { unlink }); my $kill_old_or_big = File::Finder->eval($old_or_big)->ls->eval($killer); $kill_old_or_big->in('/tmp'); Almost too cool for words.

depth

Like find's \*(C`-depth\*(C'. Sets a flag for \*(C`as_options\*(C', and returns true.

ls

Like find's \*(C`-ls\*(C'. Performs a \*(C`ls -dils\*(C' on the entry to \*(C`STDOUT\*(C' (without forking), and returns true.

tar

Not yet implemented.

[n]cpio

Not yet implemented.

ffr($ffr_object)

Incorporate a \*(C`File::Find::Rule\*(C' object as a step. Note that this must be a rule object, and not a result, so don't call or pass \*(C`in\*(C'. For example, using \*(C`File::Find::Rule::ImageSize\*(C' to define a predicate for image files that are bigger than a megapixel in my friends folder, I get: require File::Finder; require File::Find::Rule; require File::Find::Rule::ImageSize; my $ffr = File::Find::Rule->file->image_x('>1000')->image_y('>1000'); my @big_friends = File::Finder->ffr($ffr) ->in("/Users/merlyn/Pictures/Sorted/Friends");

contains(pattern)

True if the file contains \*(C`pattern\*(C' (either a literal string treated as a regex, or a true regex object). my $plugh_files = File::Finder->type('f')->contains(qr/plugh/); Searching is performed on a line-by-line basis, respecting the current value of $/.

\s-1EXTENDING\s0

A step consists of a compile-time and a run-time component.

During the creation of a \*(C`File::Finder\*(C' object, step methods are called as if they were methods against the slowly-growing \*(C`File::Finder\*(C' instance, including any additional parameters as in a normal method call. The step is expected to return a coderef (possibly a closure) to be executed at run-time.

When a \*(C`File::Finder\*(C' object is being evaluated as the \*(C`File::Find\*(C' \*(C`wanted\*(C' routine, the collected coderefs are evaluated in sequence, again as method calls against the \*(C`File::Finder\*(C' object. No additional parameters are passed. However, the normal \*(C`wanted\*(C' values are available, such as $_, $File::Find::name, and so on. The \*(C`_\*(C' pseudo-handle has been set properly, so you can safely use \*(C`-X\*(C' filetests and \*(C`stat\*(C' against the pseudo-handle. The routine is expected to return a true/false value, which becomes the value of the step.

Although a \*(C`File::Finder\*(C' object is passed both to the compile-time invocation and the resulting run-time invocation, only the \*(C`options\*(C' self-hash element is properly duplicated through the cloning process. Do not be tempted to add additional self-hash elements without overriding \*(C`File::Finder\*(C''s \*(C`_clone\*(C'. Instead, pass values from the compile-time phase to the run-time phase using closure variables, as shown in the synopsis.

For simplicity, you can also just mix-in your methods to the existing \*(C`File::Finder::Steps\*(C' class, rather than subclassing both classes as shown above. However, this may result in conflicting implementations of a given step name, so beware.

RELATED TO File::Finder::Steps…

File::Finder

BUGS

None known yet.

AUTHOR

Randal L. Schwartz, <[email protected]>

COPYRIGHT AND LICENSE

Copyright (C) 2003,2004 by Randal L. Schwartz, Stonehenge Consulting Services, Inc.

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.2 or, at your option, any later version of Perl 5 you may have available.