is there a way to locally change the input record separator in perl?

Viewed 132

Limiting the scope of a variable $x to a particular code chunk or subroutine, by means of my $x, saves a coder from a world of "global variable"-caused confusion.

But when it comes to the input record separator, $/, apparently its scope cannot be limited. Am I correct in this?

As a consequence, if I forget to reset the input record separator at the end of a loop, or inside a subroutine, the code below my call to the subroutine can give unexpected results. The following example demonstrates this.

#!/usr/bin/perl
use strict; use warnings;
my $count_records; my $infile = $ARGV[0]; my $HANDLEinfile;

open $HANDLEinfile, '<', $infile or die "cannot open $infile for reading";
$count_records = 0;
while(<$HANDLEinfile>)
{
    $count_records++; 
    print "$count_records:\n";
    print;
}
close $HANDLEinfile;

look_through_other_file();

print "\nNOW, after invoking look_through_other_file:\n";
open $HANDLEinfile, '<', $infile or die "cannot open $infile for reading";
$count_records = 0;
while(<$HANDLEinfile>)
{
    $count_records++; 
    print "$count_records:\n";
    print;
}
close $HANDLEinfile;

sub look_through_other_file
{
    $/ = undef;
    # here, look through some other file with a while loop
    return;
}

Here is how it behaves on an input file:

> z.pl junk
1:
All work
2:
and
3:
no play
4:
makes Jack a dull boy.

NOW, after invoking look_through_other_file:
1:
All work
and
no play
makes Jack a dull boy.
> 

Note that if one tries to change to

my $/ = undef;

inside the subroutine, this generates an error.

Incidentally, among the stackoverflow tags, why is there no tag for "input record separator"?

2 Answers

The answer for the my $/ = undef; question is to change it to local $/ = undef;. Then the revised code is as follows.

#!/usr/bin/perl
use strict; use warnings;
my $count_records; my $infile = $ARGV[0]; my $HANDLEinfile;

open $HANDLEinfile, '<', $infile or die "cannot open $infile for reading";
$count_records = 0;
while(<$HANDLEinfile>)
{
    $count_records++; 
    print "$count_records:\n";
    print;
}
close $HANDLEinfile;

look_through_other_file();

print "\nNOW, after invoking look_through_other_file:\n";
open $HANDLEinfile, '<', $infile or die "cannot open $infile for reading";
$count_records = 0;
while(<$HANDLEinfile>)
{
    $count_records++; 
    print "$count_records:\n";
    print;
}
close $HANDLEinfile;

sub look_through_other_file
{
    local $/ = undef;
    # here, look through some other file with a while loop
    return;
}

Then there is no need to return the input record separator to another value, or to the default, $/ = "\n";, by hand.

You can use local to temporarily update the value of a global variable, including $/.

sub look_through_other_file {
    local $/ = undef;
    # here, look through some other file with a while loop
    return;
}

will use an undefined $/ as long as the look_through_other_file subroutine is in the call stack.

You may encounter this construction in this common idiom, to slurp the entire contents of a file into a variable without altering the value of $/ for the rest of the program:

open my $fh, "<", "/some/file";
my $o = do { local $/; <$fh> };
Related