Question

I understand that I can get the current line number of a file I am looping through with the builtin variable $.. As an experiment, I used that to prefix each line in a file with the value of $. (the current line number). However, this did not work as expected. I.e. given the following file contents

line one
line two
line three

then I would expect the following code to prefix each line with its line number

for my $line (<FILE>) {
    print "$. : $line";
}

but, instead, it gives the following output

3 line one
3 line two
3 line three

prefixing each line with the number of lines in the file. Instead of the current line.

Was it helpful?

Solution

That's because the way you wrote the loop reads the entire file before looping over the lines. Unless you have a special reason to need something better than simple sequential access to the file, you should use while instead of for, like this:

while (my $line = <FILE>) {
  print "$. : $line";
}

When < filehandle > is called in list context (as it is in your for loop), it returns the entire contents of the file as a list of lines. Therefore, your code behaves in much the same way as if you had written this instead:

my @lines = <FILE>;            # now $. is set to the end of the file 
for my $line (@lines) { ... }  # you're just looping over an array, not touching $.

To achieve your desired result, you should call <> repeatedly in scalar context (which the assignment in the while condition does), to fetch one line at a time from the file and execute the body of the loop with $. set to the correct number.

Also, global filehandles are considered to be bad practice. For several reasons, it's better to use a filehandle referenced by a lexical variable instead, like this:

open my $file, '<', $filename or die $!;
while (my $line = <$file>) {
  print "$. : $line";
}

Also, since $. is a global variable containing the line number from the most recently executed read operation, you shouldn't rely on it if there's any chance of another read occurring between the <$file> and the print. Instead, ask the filehandle you're using for its line number:

open my $file, '<', $filename or die $!;
while (my $line = <$file>) {
  print $file->input_line_number, " : $line";
}

Which even works, if somewhat more awkwardly, with a global filehandle:

while (my $line = <FILE>) {
  print ${\*FILE}->input_line_number, " : $line";
}

... even the default one read by an empty <>, which is really named ARGV:

while (my $line = <>) {
  print ${\*ARGV}->input_line_number, " : $line";
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top