I know I can slurp a file by setting the input record separator ($/) to an undefined value, like
open my $fh, '<', $filename or die "Cannot read from $file: $!";
my $contents = do { local $/; <$fh> };
But recently I came across a very similar but different idiom:
open my $fh, '<', $filename or die "Cannot read from $file: $!";
my $contents = do { local $/ = <$fh> };
(Note the local $/ = <$fh> instead of local $/; <$fh>).
Both of these work, and there are examples on CPAN of both the variant with the assignment and the one without (although the latter is not surprisingly, much more common).
But my question is why does it work? What is the variant with the assignment doing?
PS: I also know that I should be using eg. File::Slurper to slurp files, but life is funny like that sometimes.
This is the result of an undocumented optimization on which you shouldn't rely.
Normally
LHS = RHSis evaluated as follows:As you can see, the right-hand side of the assignment is evaluated first[1]. This allows the following to work:
Obviously, something different —and undocumented— is happening in your case. That's because
LHS = <$fh>is special. Rather than reading from the file then assigning the result to the left-hand side,readline(<>) writes directly to the result of the left-hand side of the assignment.[2]$/and sets it toundefin your case.)$fhis evaluated.readlineis evaluated, writing directly to the result of the left-hand side of the assignment.No assignment is performed.
This optimization is undocumented, and you shouldn't rely on it.
local $/ = uc(<$fh>)wouldn't work, for example.The compiled code has the right-hand side evaluated first:
The following shows the right-hand side evaluated first:
$x = uc(<>)evaluatesuc(<>)before$x, then performs an assignment:$x = uc(<>)evaluates$xbefore<>, and it doesn't perform an assignment:Note the (uppercase)
Snext toreadlinethat wasn't there before. This "special" flag is what tellsreadlineto write to$x.Adding
localdoesn't change anything.