Re^2: unquoted string error??!!
by tchrist (Pilgrim) on May 04, 2011 at 14:42 UTC
|
I realize this is heresy, but saying “it is best to avoid BAREWORD filehandles and 2 argument open” is not as good advice as the boilerplate responses to that effect would have one believe. Advice without explanation is for children.
In particular, the examples given in the very perlintro(1) manpage that you reference indeed recommend that style right at the front:
Files and I/O
You can open a file for input or output using the open
function. It’s documented in extravagant detail in perlfunc and perlopentut, but in short:open(INFILE, "input.txt") or die "Can't open input.txt: $!";
open(OUTFILE, ">output.txt") or die "Can't open output.txt: $!";
open(LOGFILE, ">>my.log") or die "Can't open logfile: $!";
...et cetera... Considering that those are the only sorts of I/O examples that you’ll find in the perlintro(1) manpage, I don’t understand the misconnect between recommending against it and recommending for it.
The only thing really “wrong” with those cited examples out of perlintro(1) in a modern text-processing environment is that they neglect the encoding, which can be remedied with a use open pragma — amongst several other ways, such as the newish PERLIO envariable or via post-facto binmoding as it has always been done.
People have been programming Perl this way for more than two decades now. There is no need to go all PC-police on people for code that works perfectly well for their purposes. There are millions of lines of working Perl code out there that work in just this way.
Yes, there are times when more dedicated, non-shell-like constructs are more suitable.
But this is not one of them.
| [reply] [d/l] [select] |
|
|
Here is my (humble) opinion on why it is best to avoid bareword file handles...(Update: except for STDIN, STDERR, DATA, ARGV, and the like...)
While it doesn't matter in a situation as simple as in the OP, in a non-trivial situation, if you use a lexical file handle (and 'use strict'), you can avoid the following bug:
#!/usr/bin/perl
use strict;
use warnings;
my $file = "file.txt";
CreateFile($file);
system frobnicate => $file;
sub CreateFile {
my $f = shift;
open(OUTPUT_FILE, ">$f") or die "Err: $!";
for (1..100) {
print OUTPUT_FILE "$_\n";
}
# blah, blah, blah...
close OUTPUT_FH
}
This exact sort of thing happened here@work, where there is a serious lack of lexical file handle use, and took a non-zero amount of time to debug. It also failed to influence anyone's decision in choice of file handle type :-( | [reply] [d/l] |
|
|
Here is my (humble) opinion on why it is best to avoid bareword file handles...
Yeah, let's avoid reading from STDIN, or avoid writing to STDOUT or STDERR. Or make use of the magical handles DATA or ARGV.
As long as the three most important filehandles (important enough that by default (at least on Unix), all processes will have them) are bare file handles, I cannot stop thinking "uttered by someone with limited knowledge of Perl" when hearing advice like "is best to avoid bareword file handles".
As for you example bug; first of all, your use of warnings would have caught any typos in filehandle names - close OUTPUT_FH generates the warning Name "main::OUTPUT_FH" used only once: possible typo at: .... Furthermore, the above does have a serious bug: it's not checking the return value of closed. If it did, it would have notices the failure of closing a handle that wasn't opened.
| [reply] [d/l] [select] |
|
|
|
|
If you willfully ignore all warnings and meaningful return values, then of course you should expect to have inherently buggy code:% perl -Mautodie /tmp/buggy
Name "main::OUTPUT_FILE" used only once: possible typo at /tmp/buggy l
+ine 16.
Can't close filehandle 'OUTPUT_FH': 'Bad file descriptor' at /tmp/bugg
+y line 19
Did you somehow expect something else? Perl gives you the tools to diagnose and debug such buggery. Do not blame Perl if you ignore its prudent advice.
PROOF: Lexicals Filehandles Aren’t
I have a problem with this whole “lexical filehandle” folderol. That problem is that the name does not fit the thing.
-
This is not a lexical filehandle:
use vars qw($fh);
undef $fh;
open($fh, "> /tmp/data.$$")
|| die "can't open /tmp/data.$$: $!");
print $fh "I am so NOT a lexical filehandle.\n";
close($fh)
|| die "can't close /tmp/data.$$: $!";
- This is a lexical filehandle:
my $fh = *STDOUT;
print $fh "I am SO TOO lexical filehandle.\n";
I therefore submit that the thing you are talking about is not “lexical filehandles”, and to call them what they are not is to risk introduction of bugs in one’s mental model. I believe what you are referring to is not “lexical handles”, but instead autovivified anonymous handles, which may — or may not —happen to be stored in lexical variables.
| [reply] [d/l] [select] |
|
|
|
|
|
|
|
|
|
Advice without explanation is for children.
What's the best way, in your estimation, to explain to novice Perl 5 programmers that, given the vagaries and heuristics of S_intuit_method in toke.c, it's often easier to reason about the local and global effects of any individual unit of code if there are no barewords?
(I can count on my fingers the number of people who may be able to explain all eight rules for S_intuit_method without having to consult the code as a refresher, and I might be overestimating the number of people so qualified. If anyone, you're one of them, but I don't count on having all novices as experienced or ready to understand in full as you.)
This isn't about political correctness. It's about reducing the possibility of error in the same way that explaining that always, always, always adding a space between the file open mode and the filename in the two-argument form is as much a pattern for people to emulate as using the three-argument form. You know as well as anyone that novices tend to emulate examples without understanding them fully. Encouraging them to prefer constructs where, for example, the lack of an invisible character has no potential security flaws, seems to me to be more useful.
| [reply] [d/l] [select] |
|
|
chromatic wrote:
What's the best way, in your estimation, to explain to novice Perl
programmers that, given the vagaries and heuristics of S_intuit_method in
toke.c, it's often easier to reason about the local and global effects of
any individual unit of code if there are no barewords?
Sure, but there are even more important places where this issue arises. See below.
(I can count on my fingers the number of people who may be able to explain
all eight rules for S_intuit_method without having to consult the code as a
refresher, and I might be overestimating the number of people so qualified.
If anyone, you're one of them, but I don't count on having all novices as
experienced or ready to understand in full as you.)
Here, take a shot at it yourself. ☻ Your goal is to uncomment just one from
each set of sorted alternatives to produce this exact output:
fileno is 2
fileno is 2
That's all, folks!
Here’s the code:
sub new {
die "$0: main::new() sub called\n";
}
sub Class {
die "$0: main::Class() sub called\n";
}
### PICK EXACTLY ONE OF:
#1# local *new ;
#2# local *new = Class new ;
#3# local *new = Class->new() ;
#4# local *new = Class::->new() ;
#5# local *new = new Class ;
#6# local *new = new Class:: ;
### PICK EXACTLY ONE OF:
#1# open( new, "> &=STDERR") || die;
#2# open( new, "> &STDERR") || die;
#3# open( new, ">& STDERR") || die;
#4# open( new, ">&", *STDERR) || die;
#5# open( new, ">&=STDERR") || die;
#6# open(*new, ">&=STDERR") || die;
#7# open(*new, ">&", *STDERR) || die;
my $fd = fileno(*new) // die "no fileno";
die "wrong fileno" unless $fd == 2;
my $output = "fileno is $fd\n";
syswrite(*main::STDOUT, $output, length($output));
### PICK EXACTLY ONE OF:
#1# (print new $output ) || die;
#2# (print "new" $output ) || die;
#3# (print *new $output ) || die;
#4# (print ::new $output ) || die;
#5# (print {*new} $output ) || die;
### PICK EXACTLY ONE OF:
#1# close( new ) ? done() : die "can't close new: $!";
#2# close( new::) ? done() : die "can't close new: $!";
#3# close( "new" ) ? done() : die "can't close new: $!";
#4# close( *new ) ? done() : die "can't close new: $!";
#5# close( ::new ) ? done() : die "can't close new: $!";
#6# close(*::new ) ? done() : die "can't close new: $!";
sub done {
print "That's all, folks!\n";
}
package Class;
sub new {
require "IO/Handle.pm";
return IO::Handle::->new();
}
Good luck. :)
This isn't about political correctness. It's about reducing the possibility
of error in the same way that explaining that always, always, always adding
a space between the file open mode and the filename in the two-argument
form is as much a pattern for people to emulate as using the three-argument
form.
Yes, ok: that’s sound advice. One problem is people are unclear on what a mode
even is. See all the flailing above.
You know as well as anyone that novices tend to emulate examples
without understanding them fully.
There are something like 650 uses of vintage filehandles in the standard
documentation set, and perhaps 400 such uses in the Camel. What do you
propose to do, change all those to meet the new purity laws? We already
had handle autovivification in 5.6.1, and didn’t see fit to do so then.
Has something changed since then? Or was that a terrible blunder? And what should be done in future? You perceive this weighs on my mind.
And what do you do when a user comes to you unhappy that the
old standard copy pattern:
print OUTPUT while <INPUT>;
Has no corresponding clean translation? How do you explain that one to them? There is no nice story here: lexical filehandles can too easily break several standard practices that people have come to rely on more than they realize. It is never a pleasant task to explain these strange failures that can result.
And even if the thousand or so uses of vintage filehandles were expunged
from the online docs and the Camel (which I personally find to be a terrifically intimidating amount of work — which I do not care to sign myself up to!), what then do you do about the millions
of uses of them in existing code that are already out there? Ban them? The current doc policy seems to be
to remove all mention of things “we don’t like”; how does that serve the
public good?
Encouraging them to prefer constructs
where, for example, the lack of an invisible character has no potential
security flaws, seems to me to be more useful.
Security flaws? Don’t you think that’s unnecessarily overstating things?
That’s like saying that the old rename script has security flaws:
$op = shift() || die;
for (@ARGV) {
$was = $_;
eval $op; die if $@;
rename($was, $_) || die "rename: $!" unless $was eq $_;
}
The point here is that if you are allowing untrusted antagonists to specify
the arguments to your syscalls, then you have bigger problems than
mode bits.
That said, I was aghast to find this embarrassing silliness
still in perlfunc:
If you want to select on many filehandles, you may
wish to write a subroutine like this:
sub fhbits {
my(@fhlist) = split(' ',$_[0]);
my($bits);
for (@fhlist) {
vec($bits,fileno($_),1) = 1;
}
$bits;
}
$rin = fhbits('STDIN TTY SOCK');
I of course fixed it to read what it should have
read since oh, probably perl4 or so:
sub fhbits {
my @fhlist = split(" ", $_[0]);
my $bits ;
for (@fhlist) {
vec($bits, fileno($_), 1) = 1;
}
return $bits;
}
$rin = fhbits(*STDIN, *TTY, *SOCK);
Filehandles seem to me to be the least of several worrisome bareword issues.
In front of that concern come not just bareword strings but most especially
an agonizing confusion as to what is and what is not a subroutine call, a method invocation, or even a class name. Doesn’t that bother you?
Used reasonably, vintage filehandles work perfectly well.
I’m not sure the same can be said of those others.
One needn’t
resort to such games as this one:
no strict; no warnings; no less tricksy;
foo(lish);
his::bar(tab);
silly->stuff;
come on, please give up;
package UNIVERSAL;
sub AUTOLOAD { print "I am masquerading as $AUTOLOAD(@_)\n" }
to realize there’s a massively bigger bareword problem lurking right in
front of us than that of filehandles alone. All the various multiple‐choice
alternatives in my long code segment above should have by now made that
starkly clear.
| [reply] [d/l] [select] |
|
|
|
|
|
|
|
|
|
|
|
|