It is my first day to perl, and I find this warning very confusing.
Parentheses missing around “my” list at ./grep.pl line 10.
It seems
open FILE, $file;
works fine.
What is wrong with
open my $fh, $file;
Thanks!
#!/usr/bin/perl
use strict;
use warnings;
sub grep_all {
my $pattern = shift;
while (my $file = shift) {
open my $fh, $file;
while (my $line = <$fh>) {
if ($line =~ m/$pattern/) {
print $line;
}
}
}
}
grep_all @ARGV;
I’ve been hacking Perl for more than 15 years, and I admit this warning caused me to scratch my head for a minute because almost every example call to
openin the standard Perl documentation and nearly every Perl tutorial in existence containsopenwith no parentheses, just like you wrote it.You wrote this question on your first day with Perl, but you’re already enabling the
strictandwarningspragmata! This is an excellent start.False starts
An easy but dumb way to “fix” the warning is to disable all warnings. This would be a terrible move! Warnings are meant to help you.
Naïve ways to squelch the warning are abandoning the lexical filehandle in favor of the bad old way with a bareword
using explicit parentheses with
openmaking
my‘s parentheses explicitusing circumscribed parentheses
or using 3-argument
open.I recommend against using any of these by themselves because they all have a severe omission in common.
The best approach
In general, the best way to silence this warning about missing parentheses involves adding no parentheses!
Always check whether
opensucceeds, e.g.,To disable Perl’s magic open and treat
$fileas the literal name of a file—important, for example, when dealing with untrusted user input—useYes, both shut up the warning, but the much more important benefit is your program handles inevitable errors rather than ignoring them and charging ahead anyway.
Read on to understand why you got the warning, helpful hints about your next Perl program, a bit of Perl philosophy, and recommended improvements to your code. Finally, you’ll see that your program doesn’t require an explicit call to
open!Write helpful error messages
Notice the important components of the error message passed to
die:$0)"open $file")$!)These special variables are documented in perlvar. Develop the habit now of including these important bits in every error message that you’ll see—although not necessarily those that users will see. Having all of this important information will save debugging time in the future.
Always check whether
opensucceeds!Once again, always check whether
openand other system calls succeed! Otherwise, you end up with strange errors:Explanation of Perl’s warnings
Perl’s warnings have further explanation in the perldiag documentation, and enabling the diagnostics pragma will look up explanations of any warning that perl emits. With your code, the output is
The
-Mdiagnosticscommand-line option is equivalent touse diagnostics;in your code, but running it as above temporarily enables diagnostic explanations without having to modify your code itself.Warning #2 is because
no-such-filedoes not exist, but your code unconditionally reads from$fh.It’s puzzling that you see warning #1 at all! This is the first time I recall ever seeing it in association with a call to
open. The 5.10.1 documentation has 52 example uses ofopeninvolving lexical filehandles, but only two of them have parentheses withmy.It gets curiouser and curiouser:
Parentheses are missing, so where’s the warning?!
Adding one little semicolon, however, does warn about missing parentheses:
Let’s look in perl’s source to see where the warning comes from.
Perl_localizein op.c—which handlesmy,our,state, andlocal—contains the following snippet:Notice the comment on the first line. In My Life With Spam, Mark Dominus wrote, “Of course, this is a heuristic, which is a fancy way of saying that it doesn’t work.” The heuristic in this case doesn’t work either and produces a confusing warning.
The conditional
explains why
perl -we 'open my $fh, $file'doesn’t warn but does with a trailing semicolon. Watch what happens for similar but nonsensical code:We get the warning! The 3-argument
opencase doesn’t warn because"<"preventssigilfrom becoming true, and theor die ...modifier passes muster, in obtuse terms, because theortoken begins with a character other than;or=.The intent of the warning appears to be providing a helpful hint for how to fix code that will otherwise produce surprising results, e.g.,
Here, the warning does make sense, but the case you found is a leak in the heuristic.
Less is more
Perl has syntactic sugar that makes writing Unix-style filters easy, as explained in the perlop documentation.
Using the null filehandle (also known as the diamond operator) makes your code behave like the Unix grep utility.
The diamond operator also handles at least one corner case that your code doesn’t. Note below that bar is present in the input but doesn’t appear in the output.
Keep reading to see how the diamond operator improves readability, economy of expression, and correctness!
Recommended improvements to your code
Rather than hardcoding the path to
perl, useenvto respect the user’s PATH.Rather than blindly assuming the user provided at least a pattern on the command line, check that it’s present or give a helpful usage guide otherwise.
Because your pattern lives in a variable, it might change. This is hardly profound, but that means the pattern may need to be recompiled each time your code evaluates
/$pattern/, i.e., for each line of input. Usingqr//avoids this waste and also provides an opportunity to check that the pattern the user supplied on the command line is a valid regex.The main loop is both idiomatic and compact. The
$_special variable is the default argument for many of Perl’s operators, and judicious use helps to emphasize the what rather than the how of the implementation’s machinery.I hope these suggestions help!