Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

Delete files matching pattern

by bofh_of_oz (Hermit)
on May 09, 2005 at 15:22 UTC ( [id://455200]=perlquestion: print w/replies, xml ) Need Help??

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

Hi all,

I am trying to make a script to delete all files in the current folder that match a certain pattern. I know that I can use if (-x filename) to check for file existence, and I know that I can use regexp to matcha filename against a pattern. What I do not know is how to combine the two together.
Right now I am using the following code to do the task (forgive me for C style code):

opendir(DIRHANDLE, ".") or die "Cannot opendir: $!"; foreach $fname (readdir(DIRHANDLE)) { if ((-f $fname) && (lc($fname) =~ m/se\d{4}ab/)) { unlink $fname; } } closedir(DIRHANDLE);

While the code does the job, I would like to find a shorter way to do it (and learn a bit more Perl as well). I would appreciate anyone pointing me in the right direction.

UPDATE Sorry, should have specified that i'm using the script in Win environment, so can't use shell...

--------------------------------
An idea is not responsible for the people who believe in it...

Replies are listed 'Best First'.
Re: Delete files matching pattern
by deibyz (Hermit) on May 09, 2005 at 15:38 UTC
    A little shorter:

    -f and /se\d{4}ab/i and unlink for glob("*");
Re: Delete files matching pattern
by dragonchild (Archbishop) on May 09, 2005 at 15:25 UTC
    Use the shell, Luke.
    find . -type f -name '.*se\d\d\d\dab.*' -exec rm {} \; # Or ... find . -type f | grep '.*se\d\d\d\dab.*' | rm

    If you have to do it in Perl, that's about the best you're going to get.


    • In general, if you think something isn't in Perl, try it out, because it usually is. :-)
    • "What is the sound of Perl? Is it not the sound of a wall that people have stopped banging their heads against?"
      You can use find2perl to make Perl code, and change to your needs:
      find2perl . -type f -name '.*se\d\d\d\dab.*' -exec rm {} \;
      #! /usr/bin/perl -w eval 'exec /usr/bin/perl -S $0 ${1+"$@"}' if 0; #$running_under_some_shell use strict; use File::Find (); # Set the variable $File::Find::dont_use_nlink if you're using AFS, # since AFS cheats. # for the convenience of &wanted calls, including -eval statements: use vars qw/*name *dir *prune/; *name = *File::Find::name; *dir = *File::Find::dir; *prune = *File::Find::prune; sub wanted; # Traverse desired filesystems File::Find::find({wanted => \&wanted}, '.'); exit; sub wanted { my ($dev,$ino,$mode,$nlink,$uid,$gid); (($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_)) && -f _ && /^\..*se\d\d\d\dab\..*\z/s && (unlink($_) || warn "$name: $!\n"); }

      --
      Marco Antonio
      Rio-PM

      Sorry, should've included it in the post - I'm using the script on Win platform, so no shell option for me...

      Is it not possible to say something along the lines of

      if (-x /se\d{4}ab/) {unlink _;}

      Of course the one I just wrote doesn't work but I mean, the general idea of combining it like this... I realize my ideas might look silly but my eyes still glaze over when I see regexps...

      --------------------------------
      An idea is not responsible for the people who believe in it...

        You're not far off, actually:

        foreach (@files) { unlink $_ if (/se\d{4}ab/i && -x) }
        would work if @files contains the list of all the files you want to check. A quick note -- others picked up on it in code, but didn't mention explicitly -- the i at the end of the regex will match in a case-insensitive manner, which is faster than invoking the lc() on each potential filename as you do in your original code.

        The Eightfold Path: 'use warnings;', 'use strict;', 'use diagnostics;', perltidy, CGI or CGI::Simple, try the CPAN first, big modules and small scripts, test first.

Re: Delete files matching pattern
by Transient (Hermit) on May 09, 2005 at 15:39 UTC
    Not much different, but...
    foreach ( glob("./*se*ab*") ) { unlink $_ if (-f $_ && /se\d{4}ab/); }
    (mandatory CYA: might also not hurt to use 1 while unlink $_)
Re: Delete files matching pattern
by sh1tn (Priest) on May 09, 2005 at 16:17 UTC
    my $pattern = qr{se\d{4}ab}i; opendir(DIRHANDLE, ".") or die "Cannot opendir: $!"; my @files = grep { -e and /$pattern/ } readdir DIRHANDLE; unlink for @files;


      No need for temp array:

      my $pattern = qr{se\d{4}ab}i; opendir(DIRHANDLE, ".") or die "Cannot opendir: $!"; unlink grep { -e and /$pattern/ } readdir DIRHANDLE;
Re: Delete files matching pattern
by radiantmatrix (Parson) on May 09, 2005 at 16:41 UTC
    I suggest using File::Find, as it will simplify your problem tremendously.


    The Eightfold Path: 'use warnings;', 'use strict;', 'use diagnostics;', perltidy, CGI or CGI::Simple, try the CPAN first, big modules and small scripts, test first.

      Sorry, but I think that's rather doubtful. The suggestions above that use readdir or globs are really quite simple and compact, in addition to being very direct and easy to comprehend.

      The calling and callback mechanisms for File::Find, in contrast, are relatively Byzantine -- obscure and fraught with traps for the unwary and uninitiated. (Much more study and learning is needed to use File::Find at all, let alone use it effectively.) Not only that, but File::Find tends to consume more run-time than other approaches. It has it's uses, but I don't think this is one of them.

      Speaking of reducing run-time, when adopting one of the glob or readdir approaches above, I'd put the regex match before the "-f" test, since doing a stat on every file will take a little extra time, compared to stat'ing just the ones that match the file name pattern.

        First, I didn't say it would be faster, just simpler. I don't know what you mean about being Byzantine and obscure. It's pretty straightforward to me:

        use File::Find; find(\&matches_regex, '.'); sub matches_regex { return undef unless (m/.+\.txt$/ && -f); unlink $File::Find::name,"\n"; ## scalar contains full path }

        This will find and unlink all TXT files in a tree. Now, that may not be as concise, but I personally found it easier to control and understand than readdir loops. YMMV, I suppose. If performance was the primary goal, I would unequivocally agree that File::Find is probably not what one wants.


        The Eightfold Path: 'use warnings;', 'use strict;', 'use diagnostics;', perltidy, CGI or CGI::Simple, try the CPAN first, big modules and small scripts, test first.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://455200]
Approved by Paladin
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others taking refuge in the Monastery: (2)
As of 2024-04-25 05:24 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found