How can I implement Unix grep in Perl?
As you already accepted an answer, I am writing this answer for reference for future readers searching for similar problems, but not exactly yours:
As people have answered already, the way of simulating grep with perl is to use the online approach. For the use of perl as a 'better' grep (and find and cut and...) I recomend the book minimal perl and you are lucky because the chapter for 'perl as a "better" grep' is one of the sample chapters.
Here you have more examples inspired from the book:
perl -wnle '/foo/ and print' null.txt # normal grep
perl -wnle '/foo/ and print "$ARGV: $_"' null.txt # grep -H
perl -wnle '/foo/ and print $ARGV and close ARGV' null_1.txt null_2.txt # grep -l
In the last example ARGV is the current filehandle, and as with -l you are interested in finding files with the match you can print the file name and go for the next file after the first match in a file.
Also you can search by paragraph instead by line:
$ perl -00 -wnl -e '/\bBRIBE\b/i and print;' SenQ.testimony
I knew I'd be in trouble if
I ACCEPTED THE BRIBE!
So I did not.
My minimum bribe is $100k, and she only offered me $50k,
so to preserve my pricing power, I refused it.
Or find only the first match:
$ perl -00 -wnl -e '/\bBRIBE\b/i and close ARGV;' SenQ.testimony
I knew I would be in trouble if
I ACCEPTED THE BRIBE!
So I did not.
And finally if you ask about grep and perl, I think thay I should mention ACK. It implements, in perl, the grep functionality and extend it. This is a wonderful tool and as a plus you can have it also as a CPAN package. I have always use as a command line, I don't know if you can access its methods directly from your perl programs but this would be very nice.
In Perl to refer an entire array we use @
. But to refer the individual elements, which are scalar we use $
.
So, you need to use $
and not @
on these lines:
$pattern = @ARGV[0];
$file= @ARGV[1];
Also
this
@lines = grep $pattern, @arr;
should be
@lines = grep /$pattern/, @arr;
the grep in Perl has the general syntax of:
grep EXPR,LIST
It evaluates the EXPR
for each element of LIST
and returns the list value consisting of those elements for which the expression evaluated to true.
The EXPR
in your case is searching for the pattern $pattern
in array @arr
. To search you need to use the /PATTERN/
without the /
the string $pattern
will be evaluated for true or false.
Of course, codaddict's answer is right, but i'd like to add some remarks :
You should always begin your scripts with these two lines :
use strict;
use warnings;
Use three args open and test for errors:
open my $fh, '<', $file or die "unable to open '$file' for reading : $!";
And because of use strict
you have to declare all variables. So your script will be like:
#!/usr/bin/perl
use strict;
use warnings;
my $pattern = $ARGV[0];
my $file = $ARGV[1];
open $fh, '<', $file or die "unable to open file '$file' for reading : $!";
my @arr = <$fh>;
close $fh; # close as soon as possible
my @lines = grep /$pattern/, @arr;
print @lines;
If your file is large, you can avoid read it entirely in memory:
#!/usr/bin/perl
use strict;
use warnings;
my $pattern = qr/$ARGV[0]/;
my $file= $ARGV[1];
print "pattern=$pattern\n";
my @lines;
open my $fh, '<', $file or die "unable to open file '$file' for reading : $!";
while(my $line=<$fh>) {
push @lines, $line if ($line =~ $pattern);
}
close($fh);
print @lines;
You can approximate a primitive version of grep
directly on the command line. The -e
option allows you to define a Perl script on the command line. The -n
option wraps your script roughly like this: while (<>){ SCRIPT }
.
perl -ne 'print if /PATTERN/' FILE1 FILE2 ...
A slightly better approximation of grep
would prefix the file name in front of each printed match. Note that this example, like the one above, does not go through the hassle of opening any files. Instead we use Perl's <>
construct to iterate through all of the files, and the $ARGV
variable provides the current file name.
use strict;
use warnings;
my $pattern = shift;
while (my $line = <>){
print $ARGV, ':', $line if $line =~ $pattern;
}