I have recently started learning Perl and one of my latest assignments involves searching a bunch of files for a particular string. The user provides the directory name as an argument and the program searches all the files in that directory for the pattern. Using readdir()
I have managed to build an array with all the searchable file names and now need to search each and every file for the pattern, my implementation looks something like this -
sub searchDir($) {
my $dirN = shift;
my @dirList = glob("$dirN/*");
for(@dirList) {
push @fileList, $_ if -f $_;
}
@ARGV = @fileList;
while(<>) {
## Search for pattern
}
}
My question is - is it alright to manually load the @ARGV array as has been done above and use the <> operator to scan in individual lines or should I open / scan / close each file individually? Will it make any difference if this processing exists in a subroutine and not in the main function?
The @ARGV array holds the command line argument. There is no need to use variables even if you use "use strict". By default, this variable always exists and values from the command line are automatically placed inside this variable. To access your script's command-line arguments, you just need to read from @ARGV array.
Following is the syntax to open file. Here less than < sign indicates that file has to be opend in read-only mode. open(DATA, "<file. txt"); Here DATA is the file handle, which will be used to read the file.
Most often, open gets invoked with three arguments: the required FILEHANDLE (usually an empty scalar variable), followed by MODE (usually a literal describing the I/O mode the filehandle will use), and then the filename that the new filehandle will refer to. or writing to one: open(my $fh, ">", "output.
You can just pass the arguments, using system in the list form: system $^X, $program_name, @array; The list form bypasses the shell so it doesn't interpret metacharacters from your arguments (such as ; and & in the shell).
On the topic of manipulating @ARGV - that's definitely working code, Perl certainly allows you to do that. I don't think it's a good coding habit though. Most of the code I've seen that uses the "while (<>)" idiom is using it to read from standard input, and that's what I initially expect your code to do. A more readable pattern might be to open/close each input file individually:
foreach my $file (@files) {
open FILE, "<$file" or die "Error opening file $file ($!)";
my @lines = <FILE>;
close FILE or die $!;
foreach my $line (@file) {
if ( $line =~ /$pattern/ ) {
# do something here!
}
}
}
That would read more easily to me, although it is a few more lines of code. Perl allows you a lot of flexibility, but I think that makes it that much more important to develop your own style in Perl that's readable and understandable to you (and your co-workers, if that's important for your code/career).
Putting subroutines in the main function or in a subroutine is also mostly a stylistic decision that you should play around with and think about. Modern computers are so fast at this stuff that style and readability is much more important for scripts like this, as you're not likely to encounter situations in which such a script over-taxes your hardware.
Good luck! Perl is fun. :)
Edit: It's of course true that if he had a very large file, he should do something smarter than slurping the entire file into an array. In that case, something like this would definitely be better:
while ( my $line = <FILE> ) {
if ( $line =~ /$pattern/ ) {
# do something here!
}
}
The point when I wrote "you're not likely to encounter situations in which such a script over-taxes your hardware" was meant to cover that, sorry for not being more specific. Besides, who even has 4GB hard drives, let alone 4GB files? :P
Another Edit: After perusing the Internet on the advice of commenters, I've realized that there are hard drives that are much larger than 4GB available for purchase. I thank the commenters for pointing this out, and promise in the future to never-ever-ever try to write a sarcastic comment on the internet.
I would prefer this more explicit and readable version:
#!/usr/bin/perl -w
foreach my $file (<$ARGV[0]/*>){
open(F, $file) or die "$!: $file";
while(<F>){
# search for pattern
}
close F;
}
But it is also okay to manipulate @ARGV
:
#!/usr/bin/perl -w
@ARGV = <$ARGV[0]/*>;
while(<>){
# search for pattern
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With