chayashida has asked for the wisdom of the Perl Monks concerning the following question:
Why does this work:
unless (open (QTREES, $file)) {
print STDERR "WARNING: get_qtrees() can't open $file for filer $fi
+ler!\n";
return;
};
But this doesn't work:
open (QTREES, $file) or {
print STDERR "WARNING: get_qtrees() can't open $file for filer $fi
+ler!\n";
return;
};
Basically, I'm trying to change a "open or die" command to an "open or do this list of things" command.
I get a compilation error. It looks like it might have to do with the scope of the variables:
syntax error at ~chayashida/Perl/Module.pm line 872, near "return"
Global symbol "$filer" requires explicit package name at ~chayashida/P
+erl/Module.pm line 880.
I have the code working using the first version, but I'm curious why the second version doesn't work.
Thanks in advance,
Chris
Re: unless vs. bare block
by choroba (Cardinal) on May 10, 2012 at 00:49 UTC
|
You cannot use or to connect a block. If you need to use a block in place of an expression, use do: open QTREES, $file or do {
print STDERR "WARNING\n";
return;
}
| [reply] [d/l] [select] |
|
Oh, I always forget about do!
Thanks, that answers my question.
Chris
| [reply] |
Re: unless vs. bare block
by eyepopslikeamosquito (Archbishop) on May 10, 2012 at 02:35 UTC
|
open my $QTREES, '<', $file or die "WARNING: get_qtrees() can't open '
+$file' for filer $filer!: $!\n";
First, note the addition of $! in the error message to tell you why the open failed.
Rather than returning with a warning, I have used die.
Using die is usually preferable to printing a warning and continuing because if the open fails, there is usually little point in continuing (aka fail fast). Note that die throws an exception, so you can always catch that and not exit the program by wrapping the subroutine call in an eval block in the rare case that you want to continue after the open fails. As an alternative, you could first check via -f $file that the file exists and return with a warning if it does not exist and die only if it exists yet the open fails
(update: see below and How portable are common $! error codes? for a race-condition-free alternative).
Using lexical file handles (i.e. my $QTREES above) is better style because:
- They are local variables and so avoid the generally evil programming practice of keeping state in global variables.
- They close themselves automatically when their lexical variable goes out of scope.
- They avoid the confusion of barewords. IMHO, barewords are an unfortunate Perl feature and should be avoided (except when writing poetry).
Oh and be sure to start your programs with "use strict;" (just in case you are not doing that already).
Finally, note that I used the three-argument form of open.
As for why the three-argument form of open is preferred, the
old two-argument form of open is subject to various security exploits as described
at Opening files securely in Perl.
| [reply] [d/l] [select] |
|
They are local variables and so avoid the generally evil programming practice of keeping state in global variables.
Generally, avoid the evil practice of being too general. There are lots of cases where it doesn't matter at all, or where other trade offs can be made. Remember, people could have used lexical filehandle references since 5.000, but it wasn't until they autovivified that people actually started to use them on a larger scale. The world didn't burn up in flames in the mean time.
They close themselves automatically when their lexical variable goes out of scope.
Yeah, and so will "bare word" handles. You can even limit their scope by using local.
They avoid the confusion of barewords. IMHO, barewords are an unfortunate Perl feature and should be avoided
Wait. You're saying we should write &foo and "Module::Foo"->new instead of foo and Module::Foo::->new, because they're barewords, and unfortunate? You're sure you mean this?
As for why the three-argument form of open is preferred, the old two-argument form of open is subject to various security exploits
You've no idea where the second argument is coming from. For all we know, there's no problem. Don't scare people.
| [reply] [d/l] [select] |
|
Wait. You're saying we should write &foo and "Module::Foo"->new instead of foo and Module::Foo::->new, because they're barewords, and unfortunate? You're sure you mean this?
No I didn't mean that.
My characterization of all barewords as "unfortunate" was inaccurate and I thank you for pointing that out.
To be more specific, I detest bareword strings,
for example $x = Hello,
except when playing golf and writing poetry
(when I adore them).
Thankfully, "use strict" disallows this particular use of barewords.
I also dislike bareword file handles,
endorsing the point of view expressed in
Why the Modern Perl Book Avoids Bareword Filehandles.
I prefer not to teach beginners about local and dynamic scope,
at least not initially.
OTOH, I do teach them about my and lexical scope in the very first lesson.
For an example of the confusion that can be caused
by bareword file handles see: Bareword "FILE" not allowed while "strict subs".
For similar reasons, I feel that beginners should learn
three argument open first:
there are fewer gotchas and I don't want to overwhelm the
beginning Perl user with many different ways to do the same thing.
Rather, I prefer to teach them first what is
usually the best way of doing something.
I am aware that there are strong differences of opinion
on this matter as indicated by
this long P5P thread.
Update:
Don't scare people.
Don't bully people. :)
In his reply, the OP stated:
"I haven't had any experience with the three-argument version"
followed by "Thanks, I'll check it out".
So the net result of my pointing out three-argument open
was not to "scare" the OP but rather to improve his Perl
knowledge and skill by encouraging him to read about
three-argument open and to learn about the specific
problems it solves in security-sensitive environments.
| [reply] [d/l] |
|
|
|
Thanks for your reply. Typically, I've been using the open or die syntax, but other admins have asked that the code continue even when a file is missing. Though I didn't post the surrounding code, it gracefully handles a null return value for the function.
I haven't looked at the open command in a while, so I haven't had any experience with the three-argument version. I have been using the strict pragma and tainting input (out of habit, from when I first started learning Perl with HTML.
I guess I haven't checked on it to see if there's a better way to do things now. Thanks, I'll check it out.
| [reply] [d/l] |
|
I guess I haven't checked on it to see if there's a better way to do things now. Thanks, I'll check it out.
Might as well check out the free Modern Perl book, a loose description of how experienced and effective Perl 5 programmers work....You can learn this too.
| [reply] |
|
As an alternative, you could first check via -f $file that the file exists and return with a warning if it does not exist and die only if it exists yet the open fails.
But that is a race condition :) The file could become exist after you check with -f
use Errno ;
open ... or do {
if( $!{ENOENT} ){
warn "Uh oh '$file': $!" ;
return;
}
die "Uh oh, unexpected '$file': $!";
}
| [reply] [d/l] |
Re: unless vs. bare block
by ww (Archbishop) on May 10, 2012 at 02:30 UTC
|
unless (open (QTREES, $file)) {
print STDERR "WARNING: get_qtrees() can't open $file for filer $fi
+ler!\n";
is unnecessarily verbose. In the first example, you could achieve the same end with:
unless (open (QTREES, $file) or warn "get_qtrees() can't open $file for filer $filer!\n");
Similarly, in the second,
open (QTREES, $file) or warn "get_qtrees() can't open $file for filer $filer!\n"; | [reply] [d/l] [select] |
|
unnecessarily verbose. In the first example, you could achieve the same end with:
That's a syntax error:
$x=1; unless( $x ) or warn 'Erk!';;
[syntax error at (eval 11) line 1, near ") or"
Remember that unless is a 'negative' if, and Perl doesn't support if without a block.
Ie. In perl, if( cond ) statement must be coded as if( cond ) { statement; }
With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
| [reply] [d/l] [select] |
|
warn "can't open ... " unless open ...;
| [reply] [d/l] |
|
Re: unless vs. bare block
by roboticus (Chancellor) on May 10, 2012 at 10:55 UTC
|
chayashida:
If you don't care what you return, you can simplify it a bit like this:
open(QTREES,$file1) or return print "foo";
If you do care, then you can use:
open(QTREES,$file1) or (print "foo") && return;
Example:
$ touch file1; touch file2
$ perl blenge.pl
$ rm file2; perl blenge.pl
Error opening file2: No such file or directory
$ rm file1; perl blenge.pl
Error opening file1: No such file or directory
$ cat blenge.pl
#!/usr/bin/perl
use strict;
doit();
sub doit {
open QTREES, 'file1' or return print STDERR "Error opening file1:
+$!";
open RTREES, 'file2' or (print STDERR "Error opening file2: $!") &
+& return;
}
...roboticus
When your only tool is a hammer, all problems look like your thumb. | [reply] [d/l] [select] |
|
|