SysTutorials

Printing to STDERR and STDOUT in Perl

· updated April 11, 2026

Perl gives you straightforward ways to send output to both standard output and standard error. Understanding the difference matters — STDOUT handles normal program output, while STDERR carries diagnostics, warnings, and error messages. Separating them lets users redirect, log, or suppress errors independently.

Basic Output Methods

The simplest approach uses print with filehandles:

print STDOUT "This goes to standard output\n";
print STDERR "This is an error message\n";

When you call print without specifying a filehandle, it defaults to STDOUT:

print "Normal output\n";  # Same as: print STDOUT "Normal output\n";

Printing to Both Streams

Send output to both STDOUT and STDERR by making two separate calls:

my $message = "Important information";
print STDOUT "$message\n";
print STDERR "$message\n";

For cleaner code when handling multiple messages, you might use a helper function:

sub notify {
    my ($msg) = @_;
    print STDOUT "$msg\n";
    print STDERR "$msg\n";
}

notify("System started");

Using warn() for STDERR

Perl’s warn() function automatically writes to STDERR and adds a newline:

warn "This warning goes to STDERR\n";

You can capture and handle warnings with signal handlers:

$SIG{__WARN__} = sub {
    my ($msg) = @_;
    # Custom warning handling
    print STDOUT "Warning caught: $msg";
};

warn "This triggers the handler";

Flushing Output

Buffering can delay output to STDERR. Force immediate output by setting autoflush:

use IO::Handle;

STDOUT->autoflush(1);
STDERR->autoflush(1);

print STDERR "Immediate error\n";

Or disable buffering for a single filehandle:

{
    local $| = 1;  # $| enables autoflush for currently selected filehandle
    print STDERR "This appears immediately\n";
}

Real-World Example

Here’s a practical pattern for logging errors while maintaining normal output:

#!/usr/bin/perl
use strict;
use warnings;
use IO::Handle;

STDERR->autoflush(1);

sub log_error {
    my ($code, $message) = @_;
    print STDERR "[ERROR $code] $message\n";
    return 0;
}

sub log_info {
    my ($message) = @_;
    print STDOUT "[INFO] $message\n";
    return 1;
}

# Usage
log_info("Processing started");

unless (open my $fh, '<', 'config.txt') {
    log_error(1, "Cannot read config: $!");
    exit 1;
}

log_info("Configuration loaded successfully");
close $fh;

Redirecting at Runtime

Users can redirect streams at the shell level without changing your code:

# Send only errors to a file
perl script.pl 2> errors.log

# Send both stdout and stderr to the same file
perl script.pl &> output.log

# Suppress STDERR
perl script.pl 2> /dev/null

# Send STDERR to STDOUT for piping
perl script.pl 2>&1 | grep "ERROR"

Duplicating Output

To write to both streams and a file simultaneously, use Tee::Tiny or implement it manually:

use Tee::Tiny;

my $tee = Tee::Tiny->new;

# Output goes to STDOUT, STDERR, and file simultaneously
$tee->to_file("output.log");
print "This line appears everywhere\n";

Or without a module:

open my $log, '>>', 'app.log' or die "Cannot open log: $!";

sub tee_output {
    my ($msg) = @_;
    print STDOUT $msg;
    print STDERR $msg;
    print $log $msg;
}

tee_output("Logged to all streams\n");
close $log;

Error Handling Best Practices

Always use STDERR for diagnostics and reserve STDOUT for program output. This separation allows downstream tools to handle errors independently:

#!/usr/bin/perl
use strict;
use warnings;

my $result = process_data();

if ($result) {
    print "Success: Data processed\n";  # STDOUT
} else {
    print STDERR "Error: Processing failed\n";  # STDERR
    exit 1;
}

When writing modules or scripts that will be piped or redirected, respecting this convention prevents mixing error messages with data output, making logs and automation more reliable.

← All posts