Reputation: 9317
use strict;
use warnings;
my $numArgs = $#ARGV + 1;
print "thanks, you gave me $numArgs command-line arguments.\n";
while (my $line = <DATA> ) {
foreach my $argnum (0 .. $#ARGV) {
if ($line =~ /$ARGV[$argnum]/)
{
print $line;
}
}
}
__DATA__
A
B
Hello World :-)
Hello World !
when I passed one arg, it works well. Such as I run test.pl A or test.pl B or **test.pl Hello"
when I passed two args, it works some time only.
Successful: When I run test.pl A B or test.pl A Hello or **test.pl B Hello"
Failed: when I run test.pl Hello World*
Produced and output duplicate lines:
D:\learning\perl>t.pl Hello World
thanks, you gave me 2 command-line arguments.
Hello World :-)
Hello World :-)
Hello World !
Hello World !
D:\learning\perl>
How to fix it? Thank you for reading and replies.
[update] I don't want to print duplicate lines.
Upvotes: 2
Views: 654
Reputation: 118166
Assuming you want to print each line from DATA
only once if one or more patterns match, you can use grep
. Note that use of \Q
to quote regex metacharacters in the command line arguments and the use of the @patterns
array to precompile the patterns.
Read if grep { $line =~ $_ } @patterns
out loud: If $line
matches one or more patterns ;-)
#!/usr/bin/perl
use strict; use warnings;
printf "Thanks, you gave me %d command line arguments.\n", scalar @ARGV;
my @patterns = map { qr/\Q$_/ } @ARGV;
while ( my $line = <DATA> ) {
print $line if grep { $line =~ $_ } @patterns;
}
__DATA__
A
B
Hello World :-)
Hello World !
Here are some comments on your script to help you learn:
my $numArgs = $#ARGV + 1;
print "thanks, you gave me $numArgs command-line arguments.\n";
The command line arguments are in @ARGV
(please do read the documentation). In scalar context, @ARGV
evaluates to the number of elements in that array. Therefore, you can simply use:
printf "Thanks, you gave me %d command line arguments.\n", scalar @ARGV;
Further, you can iterate directly over the elements of @ARGV
in your foreach
loop instead of indexed access.
while (my $line = <DATA> ) {
foreach my $arg ( @ARGV ) {
if ( $line =~ /$arg/ ) {
print $line;
}
}
}
Now, what happens to your program if I pass (
to it on the command line? Or, even World?
What should happen?
Upvotes: 1
Reputation: 25060
I don't see the problem, your script processes the __DATA__
and tests all input words against it: since "Hello" and "World" match twice each, it prints 4 rows.
If you don't want it to write multiple lines, just add last;
after the print
statement.
Upvotes: 3
Reputation: 46245
The reason you're getting the duplicate output is because the regex $line =~ /Hello/
matches both "Hello World" lines and $line =~ /World/
also matches both "Hello World" lines. To prevent that, you'll need to add something to remember which lines from the __DATA__
section have already been printed so that you can skip printing them if they match another argument.
Also, some very minor stylistic cleanup:
#!C:\Perl\bin\perl.exe
use strict;
use warnings;
my $numArgs = @ARGV;
print "thanks, you gave me $numArgs command-line arguments.\n";
while (my $line = <DATA> ) {
foreach my $arg (@ARGV) {
if ($line =~ /$arg/)
{
print $line;
}
}
}
__DATA__
A
B
Hello World :-)
Hello World !
Using an array in scalar context returns its size, so $size = @arr
is preferred over $size = $#arr + 1
If you're not going to use a counter for anything other than indexing through an array (for $i (0..$#arr) { $elem = $arr[$i]; ... }
), then it's simpler and more straightforward to just loop over the array instead (for $elem (@arr) { ... }
).
Your foreach
loop could also be replaced with a grep
statement, but I'll leave that as an exercise for the reader.
Upvotes: 3