Why does the following perl code:
for(open(my $fh, ">>", "test.txt")){
print $fh "one\n";
}
for(open(my $fh, ">>", "test.txt")){
print $fh "two\n";
}
for(open(my $fh, ">>", "test.txt")){
print $fh "three\n";
}
Write the following to test.txt?
three
two
one
Why is the order is getting reversed? My understanding is that each for
block will automatically close the file when the block exits. Shouldn't this cause perl to flush any buffers before the the next block starts? I expected this code to open the file, write a line, close the file, then repeat all of those steps two more times. What am I missing?
I tested this with Perl 5.26.1, running on Ubuntu 18.04.3.
(Yes, I know I could easily get the lines to be written in the correct order by just putting all the print
statements in the same block. That's not the question here. I want to understand why this behavior is happening.)
For bonus weirdness, when I run the following code:
for my $val (qw/ one two three /) {
for(open(my $fh, ">>", "test.txt")){
print $fh "$val\n";
}
}
It gives me the following output:
one
two
three
This code seems like it should be functionally identical to the previous code. Why is it behaving differently?
That's just a complicated version of
open(my $fh, ">>", "test.txt");
print $fh "one\n";
open(my $fh, ">>", "test.txt");
print $fh "two\n";
open(my $fh, ">>", "test.txt");
print $fh "three\n";
Let's make small change to make things more readable and easier to discuss:
open(my $fh1, ">>", "test.txt");
print $fh1 "one\n";
open(my $fh2, ">>", "test.txt");
print $fh2 "two\n";
open(my $fh3, ">>", "test.txt");
print $fh3 "three\n";
This is equivalent because each my
creates a new variable.
So what's happening?
open(my $fh1, ">>", "test.txt"); # You create a file handle.
print $fh1 "one\n"; # You write to the file handle's buffer.
open(my $fh2, ">>", "test.txt"); # You create a file handle.
print $fh2 "two\n"; # You write to the file handle's buffer.
open(my $fh3, ">>", "test.txt"); # You create a file handle.
print $fh3 "three\n"; # You write to the file handle's buffer.
# Implicit close($fh3); # You close the file handle, flushing its buffer.
# Implicit close($fh2); # You close the file handle, flushing its buffer.
# Implicit close($fh1); # You close the file handle, flushing its buffer.
Since Perl doesn't guarantee the order in which variable are destroyed, you could easily get the output in any order.
The solution is to flush the handles after printing to them ($fh->flush;
, or $fh->autoflush(1);
), or close the handles earlier.
{ open(my $fh, ">>", "test.txt"); print $fh "one\n"; }
{ open(my $fh, ">>", "test.txt"); print $fh "two\n"; }
{ open(my $fh, ">>", "test.txt"); print $fh "three\n"; }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With