Errata

Perl Cookbook

Errata for Perl Cookbook

Submit your own errata for this product.

The errata list is a list of errors and their corrections that were found after the product was released.

The following errata were submitted by our customers and have not yet been approved or disproved by the author or editor. They solely represent the opinion of the customer.

Color Key: Serious technical mistake Minor technical mistake Language or formatting error Typo Question Note Update

Version Location Description Submitted by Date submitted
Printed (online version)) Section 8.8.2 (in Recipe 8.8. Reading a Particular Line in a File)

do { $LINE = <HANDLE> } until $. = = $DESIRED_LINE_NUMBER || eof;

should be

do { $LINE = <HANDLE> } until $. == $DESIRED_LINE_NUMBER || eof;

Anonymous   
Printed Page xxx
We'd LIke to Hear from You

This is most disappointing. The webpage pointed to by this page where one is supposed to be able to download the book's examples from reults in a 2009 (?) link (the 2nd edition was published in 2003) to archived files contain examples having nothing to do with the book!

The file reference numbers (e.g. 01-01.plx) have examples that are not even close to the sections they should represent from the book (the one mentioned here is not even relevent to Section 1.1's example code).

Please fix this link (it's acutally in your "Examples" link under the left side of the web book description).

Please, I have many O'Relly books (both paper nad electronic editions) but this sort of qulaity makes me wonder what's going on back there these days.

Marty

http://oreilly.com/catalog/9780596003135/
http://examples.oreilly.com/9780596003135/

Marty Turner  Jul 15, 2010 
Printed Page 6
both print statments

The first statement is :
printf "char %s is code %d, %#04x
", $char, $code, $code;

the supposed output (given previous assignments) is
char [capital delta] is code 916, 0x394

Using 5.8.4, I get the character ['I' with an arch over it]

The second statement is:
print "xC4 and x{0394} look different
";

The supposed output is:
char ['A' with an umlaut over it] and [capital delta] look different

I get:
Wide character in print at ...
['A' with '~' over it] and ['I' with an arch over it] look different

Anonymous   
Printed Page 23
Mid-page example

Where both Unicode code points "Combining caron" and "Combining cedilla" are show
together, they are reversed from the order given in the numeric values.

Either swap the code lines
$string = v99.807.780;
and
$string = v99.780.807;

or swap the comment lines
# COMBINING CARON
# COMBINING CEDILLA
and
# COMBINING CEDILLA
# COMBINING CARON

Guide is that
COMBINING CARON = U+030C = 780
COMBINING CEDILLA = U+0327 = 807

Anonymous   
Printed Page 45
3rd to last line from bottom of page

"Text:CSV" should be "Text::CSV"

Anonymous   
Printed Page 82
first line of code in Discussion

"#or Math::Complex->new(3,5);" is redundant, perhaps it was intended to be "new Math::Complex (3,5);"

Anonymous   
PDF Page 97
Last paragraph

The sentence starts with "If you have distinct DMYMHS values", but it makes much more sense to be "If you have distinct DMYHMS values" because everywhere the hours are before the minutes.

Vladimir Kirov  Oct 12, 2017 
Printed Page 123
middle of page, sub EOL function defn.

Page# is from the 1st edition, but the sample program is the same in the current examples file.

I tried to wrap this program in as a column-print function in my own program. It works perfectly
the first time through, but all subsequent calls to the function the "EOL" function never
resolves true resulting in some odd formatting.

After a couple of hours of debugging I tried replacing the function call with a simple "if
(($item+1..." call -- effectively replacing what was in the function definition with the same
code in-line. This worked perfectly.

I think the function as presented in the book is fine if it's the program itself, however for
people like me who want some nice columnar output as a function, the use of the global variables
and no paramater/reference passing causes it to break in unpredictable ways.

Anonymous   
Printed Page 131
4th block of code

In order to get the output "Time Flies Like An Arrow", the first splice statement
should not include the original '@initiates' array. The current statement produces
the unexpected output:

Time Flies Like An Arrow An Arrow

The splice statement should instead read

splice(@members, 2, 0, "Like");

Anonymous   
Printed Page 133

"Unlike pop and unshift, though, which always delete and return just one element ..."

should be SHIFT instead of UNSHIFT

Anonymous  Mar 31, 2013 
Printed Page 146
In sub getwinsize

On my system:
mike@/deb40a:~> uname -a
Linux playground 2.4.27 #1 Mon Dec 24 13:55:08 PST 2007 i586 GNU/Linux

In sub getwinsize
# my $TIOCGWINSZ = 0x40087468; # from 'Perl Cookbook' sets $cols = 80
my $TIOCGWINSZ = 0x00005413; # from man ioctl_list, gives 25x80

Anonymous  Feb 05, 2009 
Printed Page 163
5th paragraph

The line that reads, "The first time through, that hash value is undefined, so Perl automatically allocates a new anonymous hash ....."

It should have read, "The first time through, that hash value is undefined, so Perl automatically allocates a new anonymous
array ..."

Anonymous  Nov 29, 2012 
Printed Page 191
7th line from bottom of page

The value of $hdr_line is missing the initial "^" that is present on the 3rd line
from the bottom of the page.
It now reads:
$hdr_line = '(?m:[^:]*:.*)';
It should read:
$hdr_line = '(?m:^[^:]*:.*)';

Anonymous   
PDF Page 200
first example code

About '...' operator, the comment says:
"# operate only between first and last line, not inclusive"

However, the difference between '..' and '...' is not related to being inclusive. '...' operates between first and last line, still inclusive.

Anonymous  May 07, 2015 
Printed Page 202
Solution

The supplied glob2pat function does not convert ranges like [0-9] correctly, they get converted to [0\-9]. See also http://www.perlmonks.com/?node_id=708493.

A simplistic fix is:

sub glob2pat {
my $globstr = shift;
my %patmap = (
'*' => '.*',
'?' => '.',
'[' => '[',
']' => ']',
'-' => '-', # Added by Clive
'\\' => '\\', # also added by Clive
);

$globstr =~ s{(.)} { $patmap{$1} || "\Q$1" }ge;

return '^' . $globstr . '$';
}

However, this still does not cope with escaped characters like \?, \[, or \*, or these characters inside [] which should not be converted. They get converted regardless.

Anonymous  Sep 02, 2008 
Printed Page 205
First paragraph

"A better solution is the qr// operator, which first appeared in v5.6"

The qr// operator first appeared in Perl version 5.005.

Anonymous   
Printed Page 220
Last solution on page

On pg 220, I was unable to make your sample solution work:
/(?=^(?:(?!BAD).)*$)GOOD/s

The above solution appears after the following sentence:
"True if pattern BAD does not match, but pattern GOOD does:"

However, I *was* able to make it work by adding a .* before GOOD:
/(?=^(?:(?!BAD).)*$).*GOOD/s

On a minor note, in the previous solutions on this page, the user-specified pattern
references (/ALPHA/, /BETA/, /PAT/) are wrapped with '/' slashes in the descriptive
sentences, except for this one, where you refer to them as GOOD and BAD instead of
/GOOD/ and /BAD/.

NOTE: I am still using Perl 5.6.1, so its possible that the regex engine has changed
by v5.8. If so, a comment should be made to that effect

Anonymous   
Printed Page 220
second solution

The "may overlap" regular expression: /^(?=.*ALPHA)BETA/s
is incorrect. The "^" should not be present.

Anonymous   
Printed Page 220
second solution

"True if both /ALPHA/ and /BETA/ match, but may overlap ..."

The RE listed requires that BETA be at the beginning of the string (and even without "^", it has
to be before ALPHA)

A correct RE is:
/(?:(?=.*ALPHA)BETA)|(?:(?=.*BETA)ALPHA)/s

Anonymous   
Printed Page 237
Third item from top

Search for 'HREF' too aggressive and needs to require a separator before 'HREF'.
Perhaps should include '' like so:
... <A[^>]+?HREF .

Anonymous   
Printed Page 242
last line of the example code

The code in the 2nd edition (8/05 reprint) reads

my($from, to) = ( versive(), apparent() );

It should be

my ($from, $to) = ( versive(), apparent() );

The variable 'to' is missing a $ sigil.
It might also read better if there was a whitespace after the 'my' word.

Anonymous   
Printed Page 242
last line of the example code

G'day

The code in the 2nd edition (8/05 reprint) reads

my($from, to) = ( versive(), apparent() );

It should be

my ($from, $to) = ( versive(), apparent() );

The variable 'to' is missing a $ sigil.
It might also read better if there was a whitespace after the 'my' word.

Best regards

Andrew Dent

Anonymous  May 16, 2008 
Printed Page 253
Chapter 7.14, Solution: 3rd example, last "chunk" of example

Example for non-blocking I/O using sysread() reads:

$rv = sysread(HANDLE, $buffer, $BUFSIZ);
or die "sysread: $!";
if (!defined($rv) && $! == EAGAIN) {
# would block
} else {
# successfully read $rv bytes from HANDLE
}

The block of code needs reworking, as sysread returns zero on EOF
(and there's a syntax error on the first line -- the line ends with
a ";"):

$rv = sysread(HANDLE, $buffer, $BUFSIZ);
if (!defined($rv)) {
if ($! == EAGAIN) {
# would block
} else {
die "sysread: $!";
} else {
# successfully read $rv bytes from HANDLE
}

Anonymous   
Printed Page 265
First code under title "Solution"

Code line
use File::Temp qw/ tempdir /;
should be
use File::Temp qw/ tempfile /;

Each routine used from module File::Temp must be specifically exported to be used
without fully-qualifying the name. As written error message "Undefined subroutine
&main::tempfile called at PC2ed02.pl line 12." is obtained at run-time.

Similarly the next section of code should have line
use File::Temp qw/ tempdir /;
changed to
use File::Temp qw/ tempdir tempfile /;
to export both routines.

Anonymous   
Printed Page 304
Last code line above section title "Advanced Operations"

In
binmode(FH, "<:raw:crlf");
the 'layer' string contains an extra '<' character, perhaps from being copied from
open() examples. While it does not cause any errors at compile-time, at execution
time one gets the error:
Invalid separator character '<' in PerlIO layer specification <:raw:crlf at
PC2ed01.pl line 10. The line should read
binmode(FH, ":raw:crlf");

Anonymous   
Printed Page 304
last sentence in I/O Layers section

"Recipes 8.18, 8.19, and 8.20" should read
"Recipes 8.19, 8.21, and 8.21" instead.

Anonymous   
Printed Page 306
second line from page top

The phrase "and stores 5 characters into $block," is at best confusing. Read
literally it is plain wrong. Perhaps the entire last part of the sentence could be
something like
The sysread calls reads 256 characters from INFILE and
stores them into $block after skipping past the first
5 characters of $block.

Anonymous   
Printed Page 315
Solution of 8.7

@lines = shuffle(@lines);
NOWREADS
@reordered = shuffle(@lines);

Anonymous   
Printed Page 317
second code line from page bottom

I believe the line should be changed from
if (@lines > $line_number) {
to
if (@lines < $line_number) {

The same logic line in Example 8-3 on the next page may also need to be changed
similarly

[Chaper 9 example 7]
The lst cookbook example (Chap 9 example 7) is really useful but it has a typo in it on line 10.

..... or die << DEATH;

should not have spaces around the <<

i.e.

...... or die<<DEATH;

Anonymous   
Printed Page 359
In code section started by "opendir(DH, ...."

I believe the code is incorrect unless line
next unless /.[ch]$/i;
is changed to
next unless $file =~ /.[ch]$/i;


Anonymous   
Printed Page 359
example in middle of page

The fourth line in the assigement to @dirs is:
map {[$_, "$path/$_"]}

The value of $path is never set.


Anonymous   
Printed Page 360
bottom

return unless -f && -s _ > $saved_size;
$saved_size = -s _;

should be:

return unless -f && -s $_ > $saved_size;
$saved_size = -s $_;

Anonymous   
Printed Page 361
1st full example

$age = (stat(_))[9];

should be:

$age = (stat($_))[9];


Anonymous   
Printed Page 367
discussion

The last sentence in the first paragraph of the Discussion, "For more fine-grained control,
Stat::lsMode offers format_mode ..." describes how file_mode is stated to work in the previous
sentence.

The next paragraph confuses the issue further. "The format_perms function ... " is not in the
following example. The example shows file_mode working as it did in the first Discussion
paragraph, but format_mode returns a 9-character string, which is what format_perms was supposed
to do.

Anonymous   
Printed Page 367
entire page

addition to previous submission:

The last sentence in the first Discussion paragraph states that format_mode takes a numeric
permissions value. That now seems to be OK, but the Solution is supposed to convert numeric
permissions, yet file_mode is called with $pathname as an argument.

The whole Solution and Discussion need to be rewritten.

Anonymous   
Printed Page 394
Code under title "Using local() for temporary values for globals"

More specifically the line having the comment "pass filehandle by glob reference." I
think the intent of the author was to show the form "*FH". As it is this line is
identical to the line above. I believe the second line reading
$para = get_paragraph(*FH);
should read
$para = get_paragraph(*FH);
to agree with the comment.

Anonymous   
Printed Page 401
Solution example

The example includes:
case (@array) ...
case (%hash) ...

The lines ought to be

case [@array] ...
case [%hash] ...

Anonymous   
Printed Page 410
Near page bottom, line of code beginning "return ..."

Quite unusual usage - undef is most often seen without parentheses. Perhaps at some
previous point the line read "return $errcount ? () : %record;" which is a different
idiom.

Anonymous   
Printed Page 417
in the example in the "Discussion" section of recipe 11.3

hey there: in the example in the Discussion it says:

foreach $ref (\%ENV, \%INC ) etc, etc...

However as far as i know %INC is not predefined as a hash but as an array @INC so when you run this example ( i tried it on perl 5.8.2 it will print out all the environment variables as expected , but it will not print out any of the include directories

Sincerely,
Claudio

Claudio Jose Abud  Jul 17, 2009 
Printed Page 430
Fourth(?) paragraph, beginning "Options available as of ..."

Option named 'quoteHighBit' is repeated twice, possibly because it is repeated twice
in the documentation for module Dumpvalue.

Also, wondering why module Dumpvalue is not mentioned under "See Also" for this
section.

Anonymous   
Printed Page 438
first paragraph (code for sub insert_value)

I believe that insert_value is incorrect since it weakens all references to the newly
created node, exposing it to garbage
collection as soon as &insert_value returns. Clearly, at least one reference to
$node must stay strong. Since $node is not returned from $insert_value, its caller
cannot maintain the strong reference.

I believe it would be better to have for example all PREV references weak and all
NEXT references strong with the exception of the last one, i.e., $ring->{PREV}-
>{NEXT} which should be weak (to break the circle).

This state of affairs can be maintained by not calling weaken on $node->{NEXT} nor on
$ring->{NEXT} in &insert_value, by not calling weaken on $node->{PREV}->{NEXT} in
&delete_node and by continuing to call weaken on both $node->{NEXT} and $node->{PREV}
in &node.

Anonymous   
Printed Page 495
sub modname

line 68 is (page 495 - sub modname):
if (index($_, $Start_Dir . "/") = = 0) {
But should be:
if (index($_, $Start_Dir . "/") == 0) {

Also, I thought that the line 73 adds some extra spaces.

line 73 is (page 495 - sub modname):
s { .p(m|od)$ } { }x;
But was replaced by:
s { .p(m|od)$ } {}x;

Anonymous   
Printed Page 496
while(<POD>) { if(/=head\d\s+NAME/) { chomp($_ = <POD>);

This throws some needless errors. To correct, it should read:

$_ = <POD>) {
if($_ = <POD>) {
last if(!defined($_ = <POD>)); # defensive code here
chomp;

Ralph Winslow  Mar 06, 2009 
Printed Page 497
end of pmdesc program

lines 139-141 (page 497 - end of program):
format = ^<<<<<<<<<<<<<<<<<~~^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$Module, $_
.
But write command in page 496 (sub wanted, inside while) is commented, so I commented
lines 139-141:
#format =
^<<<<<<<<<<<<<<<<<~~^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
#$Module, $_
#.

After this correction, the following error is eliminated:
syntax error at ./pmdesc.pl line 139, near "format ="
Execution of ./pmdesc.pl aborted due to compilation errors.

Anonymous   
Printed Page 497
Code at the bottom of the page

The code at the bottom of the page doesn't work as of 12/10, most likely because the CPANPLUS module was altered since the printing of the book. Here's a version that works:

#!/usr/bin/perl -X
use CPANPLUS::Backend;
use CPANPLUS::Module;

$cp = CPANPLUS::Backend->new;
@installed = $cp->installed; # fetch list of installed mods

foreach my $module(sort {$a->name cmp $b->name} @installed)
{
# get the module's information
$details = $module->details;

# display the fields we care about
printf("%-35.35s %44.44s\n", $module->name, $details->{Description});
}

Anonymous  Dec 01, 2010 
Printed Page 533
Second code section from top

On this page and next, confusion as to whether the routine is called 'insert' or
'insert_value'. The definition is
sub insert_value {
However the call on page 533 is
for ($i = 0; $i < $COUNT; $i++) { $r->insert($i) }
and the comment on page 534 is
# $ring->insert( $value ) : insert $value into the ring structure

Since 'delete_value' is defined and used with that name I believe that the two
occurrences of 'insert' should be changed to 'insert_value'.

Anonymous   
Printed Page 535
penultimate paragraph

The paragraph incorrectly states that the functions will be called as method
invocation. They'll simply be called as functions that are passed the object as its
first argument.

This is critical when you consider what happens if you later subclass TimeNumber and
redefine any of the functions in the subclass. If the functions had been named
rather than passed in by reference:

use overload '=' => "my_plus",
'-' => "my_minus", etc

Then the methods in the subclass would be called. As the example stands however the
functions in the origianl class are called.

Anonymous   
Printed Page 540
as_string function at the top of the page

The following line of code is incomplete:

return sprintf("STR%s: %.*f", ref($self),
defined($self->{PLACES}) ? $self->{PLACES} : $PLACES,


It should have an extra argument to sprintf:

return sprintf("STR%s: %.*f", ref($self),
defined($self->{PLACES}) ? $self->{PLACES} : $PLACES, $self->{VALUE});

Anonymous  Dec 07, 2010 
558
United States

Code doesn't match output ("Two" / "Tom")

print "Two Toming: $tom1 $tom2\n";
Tom Toming: ARRAY(0x73048) ARRAY(0x73e4c)

output should be
Two Toming: ARRAY(0x73048) ARRAY(0x73e4c)

Charles  Jun 25, 2013 
Printed Page 574
code in discussion

The code calls "param" and "url", but since the snipped includes no "use" statemtents, it's not possible to know where these subroutines come from. The "start" parameter is read near the bottom of page 574, but there is no code that reads it. The entire example needs to be redone.

Anonymous  Oct 03, 2008 
Printed Page 612
Example code for section 15.17.

The string "$^0" appears to be an error. However, I am told that the correct string
is "$^O". It's an oh, not a zero. This seems impossible to determine given the font,
and might conceivably be worth a clarifying note, especially in light of the
expression being used in a way that suggests numbered, passed-in environmental data.

[664 (3rd Edition)] Solution 16.21;
Background SunOS, (Perl 5.8.0) and Win XP (ActiveState 5.8.8):
Timing out the operation as per the code of the example fails. $@ is always '' at the the line
'die if $a && $@ !~ /.../ #reraise'

I've tried two "patches" --- In the first I added a variable and test it after the eval. --- In
the second I re-raised the "die $@ if ($@" as in
[code]
eval {
local $SIG{ALRM}=sub { die "Timing out!"; };
alarm 1;
eval {
die "Dying, dying, dying!";
};
alarm 0;
# added
die $@ if ($@);
# added
};
alarm 0;
print "after eval $@='$@'
";
die $@ if ($@ && $@ !~ /Timing out!/);
[/code]

Anonymous   
Printed Page 617
first example code in Discussion

In Section "17.6 Using UNIX Domain Sockets", the example code uses LocalAddr and PeerAddr, when it should be Local and Peer (the stupid IO::Socket module doesn't complain, but also doesn't work if you pass in the wrong parameter).

Anonymous  Sep 03, 2008 
Printed Page 627
Section 17.11 Forking servers:

sub REAPER {
1 until ( -1 == waitpid(-1, WNOHANG));
$SIG{CHLD} = \&REAPER;
}


the problem with

1 until ( -1 == waitpid(-1, WNOHANG));

Is that this will loop until ALL children of the main loop are
finished. If you have a long running child (in my case it was a credit
card auth that took 90 seconds to time out), then all attempts to
connect to your server will hang until the long running child completes.

Better would be

1 until ( 0 >= waitpid(-1, WNOHANG));

or
1 while (waitpid(-1, WNOHANG > 0));

Anonymous  Jul 28, 2011 
Printed Page 638
Solution Listing

First the close(WRITEME) must done _before_ the output could be captured.
Second output may be multiline, so output should be an array.

$pid = open2(*README, *WRITEME, $program);
print WRITEME "here's your input
";
close(WRITEME); # <--- CHANGE
@output = <README>; # <--- CHANGE
close(README);
waitpid($pid, 0);

Anonymous   
Printed Page 648
the code for example #10 in recipe 16.11 shows an error

now:
$SIG{ALRM} = sub { close(FIFO) }; # move on to the next queued process

should be:
$SIG{ALRM} = sub { close($fifo) }; # move on to the next queued process

to be consistent with the rest of the program. The global filehandle
FIFO isn't used anywhere else.

Anonymous   
Printed Page 663
Solution -> To unblock

The listed code first correctly masks off SIGINT:

sigprocmask(SIG_BLOCK, $sigset, $old_sigset)

However, to unblock, it suggests:

sigprocmask(SIG_UNBLOCK, $old_sigset)

This won't work. If $old_sigset was empty (because nothing was blocked initially), then unblocking the empty set won't change anything.

Either:

sigprocmask(SIG_UNBLOCK, $sigset)

Or:

sigprocmask(SIG_SETMASK, $old_sigset)

I'd prefer the SIG_UNBLOCK to undo a previous SIG_BLOCK (and SIG_SETMASK to restore after a previous SIG_SETMASK).

Steven Bakker  Jan 18, 2011 
Printed Page 679
last paragraph

Change:
which is nicely wrapped by the standard IO::Socket class
to:
which is nicely wrapped by the standard IO::Select class

Anonymous   
Printed Page 707
2nd paragraph

Text currently reads:

Fork once, and let the parent exit
$pid=fork;
exit if $pid; #ERROR HERE
$die "Couldn't fork: $!" unless defined($pid);

The line marked ERROR HERE should instead be

exit if $pid==0

or equivalent (such as "exit unless $pid"). This is because we want to run a daemon as the child and have the parent exit not the child. In its current form in the text both the parent and child processes will die or exit, leaving nothing around to do any work. In the corrected version the parent correctly exits and the child is left to do work if defined.

David Guthrie  Oct 05, 2009 
Printed Page 748
XML-RPC Client Solution code

"$result = $server->call('ClassName.handler', @ARGS);"
should be
"$call = $server->call('ClassName.handler', @ARGS);"

Anonymous   
Printed Page 750
SOAP Client Solution code

"$result = $server->call('ClassName.handler', @ARGS);"
should be
"$call = $server->call('ClassName.handler', @ARGS);"

Anonymous   
Printed Page 753
4th line of code

The line:

if ($stat[2] & 177) {

should read:

if ($stat[2] & 0177) {

Anonymous   
Printed Page 760
Example 19-1

The hiweb cgi script will not run as is. It does not ask for user input, it generates
an error "Use of uninitialized value in string eq at (eval 6) line 3." and it prints
nothing out after the "You typed:" prompt.

I believe for the problem may be that there should be an HTML page that describes the
simple FORM that invokes this cgi script. It would be nice if complete examples were
given, not merely the PERL aspect of an example. I am invoking the hiweb script
directly from a browers ala:
http:/mywebserver.com/cgi-bin/hiweb.cgi

Anonymous   
PDF Page 772
6th line

Where it reads:
die "cannot fork: $!" unless defined($pid = open(KID_TO_WRITE, "-|"));

should read:
die "cannot fork: $!" unless defined($pid = open(KID_TO_WRITE, "|-"));

Tessio Fechine  Dec 19, 2011 
Printed Page 794
table at bottom of page

The third entry under "Guess" is file:/etc/passwd - it ought to be file:///etc/passwd

Anonymous   
Printed Page 796
first sentence of Solution

The first sentence ends "use the GET method on an LWP::UserAgent object:", but the code uses
LWP::Simple and calls get() as a procedure.

Anonymous   
Printed Page 801
code in Discussion

The call to format_string() has no argument that holds the string to be converted to ASCII.

Anonymous   
Printed Page 808
example 20-7

At the bottom of the page, after "END_OF_SELECT", there should be a line like that in example
20-8: $sth-execute() or die "couldn't execute SQL";

Anonymous   
842
Bottom of SECTION 8.4.2

use Tie::File;
tie(@lines, "Tie::File", $FILENAME, mode => 0)
or die "Can't tie $FILENAME: $!";
$max_lines = $#lines;
for ($i = $max_lines; $i; $i--) {

The test in the for loop is $i. When $i becomes 0, the loop ends. Therefore the $lines[0] is never printed.

Anonymous  Apr 18, 2013