Here's a question suitable for a fairly new Perl programmer. This is a bug in one of my programs from a few weeks ago. I've stripped down the code to the bare essentials. Can you find the bug?

The program creates some pages from a database, saves them locally, then uploads them to a remote server. The program takes a few seconds to run. After it exits, one file is always empty on the remote server. It is not empty on the local system.

my @built; my @files = get_files_to_build(); for (@files) { my $built = build_file($_); unless ($built) { warn "Could not build $_!\n"; next; } my $filename = File::Spec->catfile($path, $_); open(OUTPUT, "> " . $filename) or die "Can't open $filename: $!"; print OUTPUT $built; push @built, [ $filename, $_ ]; } my $ftp = Net::FTP->new($remotehost, Debug => 1) or die "Can't connect to $remotehost: $@\n"; $ftp->login($username, $password) or die "Couldn't authenticate!\n"; $ftp->cwd('www'); foreach my $updated (@built) { my ($location, $name) = @$updated; $ftp->put($name, $location) or warn "Couldn't put $name: $@\n"; } $ftp->quit();
(Apologies for any errors in transcription. This is a logic bug, not a typo.)

There are I came up with four possible solutions. One is very easy, the second works implicitly. The third may not work on every platform, and the fourth is crazy.

I'm including a couple of hints in HTML comments. I know several saints who would pick this out right off the bat, but let's leave this for people who don't normally speak up.

Hints follow.

Update: So far, ZZamboni and Albannach have both caught it. Good job! arturo also got it right, pointing out a legitimate typo that wasn't the bug. Then Spudnuts sent me a /msg, and I decided it would be okay for people to post their own answers. It might be good to stick them in HTML comments (<!-- comment here -->), though. So far no one's provided anything but the first and most direct fix.

tilly spotted two other typos and came up with four fixes. Abigail also gets a gold star, but if either of those two are novices, I'm secretly Jon Orwant.

Replies are listed 'Best First'.
Re: Novice Quiz: File is created locally, uploaded empty
by Abigail (Deacon) on Jun 22, 2001 at 04:52 UTC
    I guessed the "problem" from just reading the description, hardly needed to look at the code. The problem comes from not closing the filehandle. Then the buffer isn't flushed, and hence the file is empty. You will never have more than one file suffering from this problem, and it's always the last file you wrote - this is because opening the same handle closes it first (if it was open). Good thing you didn't localize the handle!

    One way of solving it is by using an autovivified scalar as handle:

    for (@files) { ... stuff ... open my $output => ">", $filename or die "...."; print $output $built; push @built => [$filename => $_]; }
    then when $output goes out of scope, that is, when the for block reaches the end, the file is automatically closed.

    It is slightly better to do an explicit close yourself, as that allows you to inspect the return value of close. A close might fail (filesystem full, for instance).

    -- Abigail

      Actually if you localize the handle, Perl will close it for you as the handle goes out of scope. So that is a perfectly fine way to solve this problem.
      using an autovivified scalar as handle

      I like that. So there is no real need to "use symbol", is there?

        Well, autovivified scalars as filehandles is new in 5.6. You might want to use use Symbol; (note the capitalation) in older Perls.

        -- Abigail