[GRLUG] Open a file based on Content [Perl]

Al Tobey tobert at gmail.com
Tue Mar 6 10:29:33 EST 2007


On 3/6/07, Justin Denick <justin.denick at gmail.com> wrote:
> This is a cross posted message, if that offends you than seek consoling.
>
> I have the need to open files and manipulate them into helping me take over
> the world. Muhahahah.
> I can do it in bash, but I really want to learn perl, so hopefully one of
> you guys or girls
> could help. (do we have any women on this list?)
>
> Here's how I gitrdun in bash.
>
> > x=0
> > pattern="pattern"
> > files=`grep -l $pattern list-of-files`
> >
> > for this in $files do;
> >    if [ $x -eq 0 ]
> >       sed s/\~/\\r\\n/g $files > name.txt
> >
>
>
> > x=`expr $x + 1`
> >
>
>
> >    else
> >       sed s/\~/\\r\\n/g  $files > name.txt$x
> >
> >    fi
> > done
>
>
>
> But I want learn how to do this in perl
> so it would be something like (and please pardon my ignorance)
>
> > my $x = 0;
> > my $pattern = "pattern";
> >
> > open DH, "path" or die;
> > my @files = (readdir DH);
> > my @found = grep($pattern, @files);
> > for my $source (@found) {
> > my destination = "$source.new"
> >    open INPUT, "<$source" or die;
> >    open OUTPUT ">$destination" or die;
> >    while (<INPUT>) {
> >       s/\176/\012\015/g;
> >       print OUTPUT $_;
> >       rename $destination, $source;
> >    }
> >
> > }
>
> Thanks in advance for your help. '

grep() in perl is quite different from grep(1) in Unix.   It takes a
statement and a list of scalars and runs the block across them
returning items for which the statement evaluated to true.  So,

grep /~/, @lines;

will return a list of lines that contain a tilde.  Check out "perldoc -f grep".

This is a quick-n-dirty, completely untested, and unverified version.
 It also tries to be efficient about opening files multiple times,
which the shell version can't do.   I highly recommend the Perl
Cookbook from O'Reilly, since it is geared towards "Git-R-Done" rather
than a more academic approach to learning Perl.

#!/usr/bin/perl

use File::Find;

# see perldoc -f qr
my $pattern = qr/pattern/;

sub wanted {
    # recent versions of perl allow/prefer "my $fh" over barewords
    open my $fh, "< $File::Find::name";
    return unless ( $fh ); # skip this file if open() failed

    while ( my $line = <$fh> ) {
        # only scan the file up to the first match, then immediately jump to
        # processing it then bail out of this loop
        if ( $line =~ /$pattern/ ) {
            process( $fh, $File::Find::name );
            last; # exit the loop now that the file is processed
        }
    }
}

sub process {
    my( $fh, $filename ) = @_;

    # move the file handle back to the top of the file -- it may be more
    # efficient for large files to only seek backwards a little bit, but this
    # is easier to follow for now
    seek $fh, 0, 0;

    open my $out, "> $filename.new"
        or die "Unable to open $filename.new for writing: $!";

    while ( my $line = <$fh> ) {
        $line =~ s/\176/\012\015/g;
        print $out $line;
    }

    close $out;
    close $fh;

    rename "$filename.new" $filename;
}

# this will recurse a directory tree
find({ wanted => \&wanted, no_chdir => 1 }, '.' );

-Al Tobey


More information about the grlug mailing list