Filehandle and printing state#

Seven variables that drive Perl’s I/O. They are globals — setting $/ in one routine changes how every other routine in the same process reads from a filehandle. The discipline that keeps that from being a disaster is local-isation: every change to a variable on this page should be inside a local block unless the program genuinely wants the new value to be permanent.

Variable

Reads as

Default

Used by

$/

input record separator

"\n"

<$fh>, readline

$\

output record separator

undef

print, printf, say (forced \n)

$,

output field separator

undef

print, printf (between args)

$"

list separator

" "

array interpolation "@arr"

`$

`

output autoflush

0

$.

current line number

0

last filehandle read

$;

subscript separator

"\034"

$h{$x, $y} multidim emulation

$/ — the input record separator#

Controls what <$fh> and readline treat as the end of one record. The default "\n" means line-by-line reading; setting it to other values produces other reading modes.

Line mode (the default)#

while (my $line = <$fh>) {
    chomp $line;                 # strips the terminator $/
    process($line);
}

chomp removes whatever $/ currently holds — when you change $/, chomp follows it.

Slurp mode#

undef $/ makes <> read the entire file in one go:

my $whole_file = do {
    local $/;
    <$fh>;
};

The local is essential. Without it, every other piece of code that subsequently reads from a filehandle in the same process will silently slurp.

Paragraph mode#

local $/ = ""; reads paragraph-at-a-time, splitting on runs of two or more newlines:

{
    local $/ = "";
    while (my $para = <$fh>) {
        # $para ends with exactly two newlines (or one at EOF)
        process_paragraph($para);
    }
}

This is awk’s RS="" behaviour. Leading blank lines at the start of the file are silently skipped.

Fixed-record mode#

local $/ = \N reads N bytes per call:

local $/ = \4096;                # binary 4-KiB chunks
while (defined(my $chunk = <$fh>)) {
    process($chunk);
}

The reference is to a number, not the number itself. This is the right shape for reading binary data, network packets, or doing bulk copy without scanning for \n.

Multi-character terminators#

local $/ = "\r\n";               # CRLF mode
local $/ = "\nEND-OF-RECORD\n";  # custom marker

The terminator is a literal string, not a regex.

$\ — the output record separator#

Appended to every print call. Default is undef (nothing appended). Setting it changes every subsequent print:

{
    local $\ = "\n";             # all prints get a trailing newline
    print "first";
    print "second";
}
# Output: "first\nsecond\n"

This is the natural fit for -l on the command line — -l sets $\ to \n. In script code, local $\ = "\n" inside a tight reporting loop is occasionally worth it; otherwise say (which forces \n regardless of $\) is the cleaner spelling.

$, — the output field separator#

Inserted between print’s arguments. Default is undef:

print "a", "b", "c";              # "abc"

{
    local $, = ", ";
    print "a", "b", "c";          # "a, b, c"
}

{
    local $, = "\t";
    print @items;                 # tab-separated record
}

$, is per-print, not per-line — it does not separate successive print calls. Combine with $\ for full TSV-style output:

local ($,, $\) = ("\t", "\n");
print 'col1', 'col2', 'col3';     # "col1\tcol2\tcol3\n"

$" — the list separator#

Joins array elements when an array is interpolated into a double-quoted string. Default is " " (single space):

my @arr = (1, 2, 3);
print "items: @arr\n";            # "items: 1 2 3"

{
    local $" = ', ';
    print "items: @arr\n";        # "items: 1, 2, 3"
}

{
    local $" = "\n  ";
    print "items:\n  @arr\n";     # "items:\n  1\n  2\n  3"
}

$" is only consulted on interpolation. print @arr (no quotes) uses $,, not $".

$| — output autoflush#

Forces a flush after every write on the currently selected filehandle:

$| = 1;                          # autoflush STDOUT
print "Loading..."; sleep 1;     # appears immediately, not at exit

The «currently selected» qualification matters. By default, select returns STDOUT, so $| = 1 autoflushes STDOUT. To autoflush a different handle:

my $old = select($log_fh);
$| = 1;
select($old);                    # restore selection

The slightly nicer modern spelling uses IO::Handle:

$log_fh->autoflush(1);

$| is read-as-boolean: print $| shows 1 or 0.

$. — current line number#

The line number of the last <>-style read. Each filehandle has its own counter; reading from a different handle aliases $. to that handle’s count:

while (<$fh>) {
    print "$.: $_";              # numbered listing
}

# After the loop, $. is the total number of lines read.

$. is reset when the filehandle is closed. Reopening (without an explicit close in between) does not reset.

For multi-file iteration via <>, lines are numbered cumulatively across files unless you explicitly close ARGV at each file boundary — see eof for the standard close ARGV if eof; idiom.

$; — the subscript separator#

Used to fake multi-dimensional hashes — a deeply legacy feature preserved for backward compatibility:

$h{$x, $y} = $value;             # equivalent to $h{"$x\034$y"}

The default value is "\034" (FS, «file separator», an obscure ASCII control byte). Real multi-dimensional structures should use nested hash refs ($h{$x}{$y}); $; exists because Perl 4 had no proper references.

When you actually localise these#

The most common shape, by far:

sub slurp_text {
    my ($path) = @_;
    open my $fh, '<', $path or die "open $path: $!";
    local $/;                    # slurp inside this sub only
    return <$fh>;
}

sub paragraphs_of {
    my ($path) = @_;
    open my $fh, '<', $path or die "open $path: $!";
    local $/ = "";
    return <$fh>;                # list context: list of paragraphs
}

sub csv_print {
    my (@row) = @_;
    local ($,, $\) = (",", "\n");
    print @row;
}

The cost of forgetting local is invisible at write time and catastrophic at use time — a subroutine that «just sets $/» can break unrelated code reading from completely different filehandles. Treat $/, $\, $,, $" as global mode flags: always set them with local.

See also#

  • open, readline, <$fh> — the consumers of $/.

  • print, printf, say — the consumers of $\, $,.

  • chomp — removes whatever $/ currently holds; pairs with $/ changes.

  • eof — the close ARGV if eof idiom that makes $. count per-file across <>.

  • select — selects the filehandle that $| and the unprefixed $,/$\ apply to.

  • IO::Handle — provides autoflush, output_record_separator, etc. as method calls.

  • local — the safe way to change any variable on this page.

  • Interpolation — how $" shapes array expansion inside double-quoted strings.