Control flow · Modules

do#

Execute a block of code or run a Perl source file as if it were part of the current program.

do wears two unrelated hats that share only a keyword. do BLOCK groups statements into a single expression and returns the value of the last one — a syntactic tool, not a function. do EXPR treats the string as a filename and compiles-and-runs the file at runtime. Read whichever section matches the form you see; mixing them up is the most common source of confusion around this keyword.

Synopsis#

do BLOCK                        # group statements, yield last value
do EXPR                         # load and run a Perl file
do { ... } while CONDITION;     # post-test loop idiom

What you get back#

  • do BLOCK — the value of the last expression evaluated in the block, in whatever context the surrounding expression imposes.

  • do EXPR — the value of the last expression evaluated in the file on success; undef on failure, with $@ set to the compile error or $! set to the I/O error.

do BLOCK — group statements into one expression#

do BLOCK is not a function. It is a syntactic construct that turns a brace-delimited sequence of statements into a single term whose value is the value of the last statement. Reach for it when the language wants an expression but you have several statements to run:

my $x = do {
    my $tmp = fetch_raw();
    $tmp =~ s/\s+\z//;
    lc $tmp;
};                              # $x = cleaned, lower-cased value

When do BLOCK is modified by a trailing while or until, the block runs once before the condition is tested — this is the standard post-test loop idiom:

do {
    $line = <$fh>;
} while defined $line && $line !~ /^END\b/;

On any other statement, while and until as statement modifiers test the condition first. do is the exception.

next, last, redo do not work inside do BLOCK#

A do BLOCK is not a loop, even when trailed by while or until. Loop-control keywords do not see it:

do {
    last if $done;              # WRONG — not a loop
} while $more;

If you need loop control, wrap the body in a bare block, which is a loopable scope:

{
    last if $done;
    redo if $retry;
}

Or use an explicit while loop.

return inside a bare do BLOCK#

return in a do BLOCK returns from the enclosing subroutine, not from the block itself. That is occasionally what you want and occasionally a bug — either way, know which one you are writing:

sub classify {
    my $n = shift;
    my $kind = do {
        return "zero" if $n == 0;   # returns from classify(), not the do
        $n > 0 ? "pos" : "neg";
    };
    "$kind ($n)";
}

To yield a value from the block without returning from the sub, just let the last expression be the value — that is the whole point of do BLOCK.

do EXPR — load and run a Perl source file#

do EXPR evaluates EXPR, treats the result as a filename, reads the file, compiles it, and runs it in the current package. It is similar to, but not quite the same as,

eval `cat stat.pl`;

The do form runs no external process, keeps the original filename for error messages, and cannot see lexicals in the enclosing scope — which eval STRING can. Like eval STRING, the file is reparsed on every call, so do FILE inside a loop is almost always wrong.

Path resolution and @INC#

The filename is interpreted like this:

  • Absolute paths (/foo/stat.pl), paths beginning with ./, and paths beginning with ../ are used as-is.

  • Any other relative path is searched along @INC, and on success the found path is recorded in %INC under the key you passed.

do '/etc/myapp/stat.pl';        # exact file
do './stat.pl';                 # exact file, current directory
do 'stat.pl';                   # search @INC
do 'MyApp/config.pl';           # search @INC

Since Perl 5.26 . is not in @INC by default, so a bare do 'stat.pl' no longer falls back to the current directory. If the file is not found, Perl emits the hint:

do "stat.pl" failed, '.' is no longer in @INC;
did you mean do "./stat.pl"?

Pass ./stat.pl when you mean the current directory.

Return value and error reporting#

do EXPR has three outcomes you must distinguish:

Outcome

Return

$@

$!

File compiled and ran

value of last expression

""

unchanged

File read but did not compile

undef

compile error

may also be set

File could not be read

undef

""

I/O error

Because compilation failure can incidentally set $! as well, always check $@ first:

my $return;
for my $file ("/etc/myapp.rc", "$ENV{HOME}/.myapprc") {
    unless ($return = do $file) {
        warn "couldn't parse $file: $@" if $@;
        warn "couldn't do $file: $!"    unless defined $return;
        warn "couldn't run $file"       unless $return;
    }
}

The three warns correspond to the three failure shapes above plus the fourth case — the file ran but its last expression was false.

Why not use do FILE for modules#

For loading library code, use require or use instead of do EXPR. require caches through %INC (so a file is loaded at most once), raises an exception on failure instead of returning undef, and integrates with @INC hooks. use adds compile-time loading and import handling on top. do EXPR exists for configuration files and ad-hoc script inclusion — not for libraries.

Examples#

Group statements into an expression:

my $config = do {
    open my $fh, "<", $path or die $!;
    local $/;
    <$fh>;
};                              # $config = whole file as one string

Post-test loop with do while:

my $n;
do {
    print "enter a positive number: ";
    chomp($n = <STDIN>);
} until defined $n && $n =~ /\A\d+\z/ && $n > 0;

Read a Perl-syntax configuration file, with full three-way error handling:

my $cfg = do '/etc/myapp/config.pl';
if (!defined $cfg) {
    die $@ ? "config parse error: $@"
           : "config read error: $!";
}
die "config did not return a true value" unless $cfg;

Conditional value selection where each branch is several statements:

my $greeting = do {
    if ($user) {
        my $name = $user->display_name;
        "hello, $name";
    }
    else {
        "hello, stranger";
    }
};

Temporary localisation that must span several statements and still yield a value:

my $dump = do {
    local $Data::Dumper::Sortkeys = 1;
    local $Data::Dumper::Indent   = 1;
    Data::Dumper::Dumper(\%state);
};

Global state it touches#

  • @INC — searched when do EXPR is called with a relative path other than ./… or ../….

  • %INC — updated with the resolved path when do EXPR succeeds on an @INC search.

  • $@ — set by do EXPR on compile failure; cleared on success.

  • $! — set by do EXPR on I/O failure (file could not be read).

  • $0, __FILE__, __LINE__ — the currently compiling filename is set to the file being loaded while do EXPR parses it, so warnings and die messages point at the right file.

do BLOCK touches no globals; it is a pure syntactic construct.

Edge cases#

  • do BLOCK is not a loop. next, last, and redo inside a do while do not apply to the block. Use a bare block or a real while loop.

  • return in do BLOCK returns from the enclosing sub. Not from the block. There is no way to return a value from a do BLOCK other than making it the last expression evaluated.

  • Statement modifiers are pre-test everywhere else. STATEMENT while COND tests first; do BLOCK while COND tests after the first run. This is a deliberate language exception.

  • Relative paths without ./ search @INC. Since 5.26, . is not in @INC, so do 'stat.pl' does not mean “current directory file” any more; pass ./stat.pl if that was the intent.

  • do EXPR cannot see enclosing lexicals. The file is parsed as if at the top level. If you need access to lexicals, use eval STRING — with the usual taint and injection caveats.

  • The file is re-parsed every call. do EXPR is not cached via %INC the way require is. Calling it in a loop re-reads and re-compiles the file each iteration.

  • Check $@ before $!. A compilation failure can also set $! as a side effect; the compile error in $@ is the one you actually want to report.

  • A file that compiles but returns a false value is ambiguous. unless ($ret = do $file) { } fires for both “file failed” and “file ran and returned 0”. If your config file is allowed to end in a false value, use defined and $@ explicitly instead of truth-testing the return.

  • do with no argument is a syntax error. Both forms require a term: a block or an expression.

Differences from upstream#

Fully compatible with upstream Perl 5.42.

See also#

  • evaleval BLOCK traps exceptions without reading a file; eval STRING parses arbitrary code and can see enclosing lexicals, unlike do EXPR

  • require — load a library file with %INC caching and automatic exception on failure; the right tool for modules

  • return — note that return inside do BLOCK returns from the enclosing subroutine, not from the block

  • @INC — module search path consulted by do EXPR on relative filenames

  • %INC — records the resolved path after a successful @INC-searched do EXPR

  • $@ — compile-time error from do EXPR

  • $! — I/O error from do EXPR when the file cannot be read