exec#
Abandon this program and run another in the same process.
exec replaces the current process image with another program. It
does not return on success — execution continues in the new
program, with the same process id, open filehandles subject to the
close-on-exec flag, and whatever environment was in effect. It
returns false (and sets $!) only when the target could not
be launched and it was executed directly rather than through the
shell. Reach for system when you want the child to run and
control to come back.
Synopsis#
exec LIST
exec PROGRAM LIST
exec { PROGRAM } LIST
What you get back#
Nothing, on success — the process has been replaced and this Perl
interpreter is gone. On failure exec returns false and leaves the
reason in $!. Failure is only detectable for the direct-exec
path; when the argument is handed to the shell, a missing program is
reported by the shell itself and exec still counts as having
“succeeded” in launching /bin/sh.
exec '/usr/bin/vi', $file
or die "couldn't exec vi: $!";
Because exec normally does not return, Perl emits a warning under
use warnings if it appears in void context followed by another
statement that is not die, warn, or exit. Either
handle the failure inline with or die, or wrap the call in a block
to silence the warning:
exec('foo') or die "couldn't exec foo: $!";
{ exec('foo') }; die "couldn't exec foo: $!";
Shell or no shell — the single-string rule#
exec picks between two launch mechanisms based on the shape of
LIST:
Two or more arguments →
execcallsexecvp(3)directly with the list. No shell is involved, no metacharacter interpretation, no word splitting.Exactly one argument →
execscans that single string for shell metacharacters. If any are found, the whole string is passed to/bin/sh -cfor parsing. If none are found, the string is split on whitespace and handed toexecvpdirectly.
The rule matters for both correctness and security. A one-argument
string containing a space but no metacharacters is split into words
and executed directly — which is often what you want. A one-argument
string containing |, >, $, ;, glob characters, or quotes goes
through the shell, and the shell will happily expand wildcards,
interpret variables, and split on $IFS.
exec '/bin/echo', 'Your arguments are:', @ARGV; # direct execvp
exec "sort $outfile | uniq"; # pipeline → shell
exec "ls /tmp"; # no metachars → split + execvp
To force the no-shell path even with a single logical command, use the indirect-object form described next.
Indirect-object form — set argv[0], skip the shell#
Placing a block or scalar before LIST (with no comma) tells exec
two things at once: run this program, and pass those arguments
as argv. The first element of LIST becomes argv[0] — the name
the child sees for itself — which need not match the program path.
exec { '/bin/echo' } 'echo', 'hi'; # argv[0] is "echo"
my $shell = '/bin/csh';
exec $shell '-sh'; # login-shell convention: argv[0] = "-sh"
exec { '/bin/csh' } '-sh'; # same, more direct
The indirect-object form always treats LIST as a multi-valued
list, even if it has only one element. That disables the
metacharacter scan entirely:
my @args = ('echo surprise');
exec @args; # one element → shell scan → /bin/sh -c 'echo surprise'
exec { $args[0] } @args; # always direct execvp, "echo surprise" is argv[0]
In the second form there is no program on disk literally named
echo surprise, so execvp fails, exec returns false, and
$! holds the reason. That’s the safe behaviour: the caller
gets to decide what to do, rather than the shell running something
unexpected.
Global state it touches#
$!— set on failure to theerrnofrom the underlyingexecvp(3)call. Only meaningful whenexecactually returns.$?— left unchanged byexecitself, but a failed shell-mediated attempt may leave the shell’s exit status visible to a wrappingsystem.The process’s open filehandles, environment (
%ENV), current working directory,umask, signal dispositions, and resource limits all carry into the replacement program —execreplaces the program, not the process.
Examples#
Run a program with an explicit argument list, direct execvp:
exec '/usr/bin/git', 'status', '--short'
or die "exec git: $!";
Hand a command line to the shell deliberately, using metacharacters:
exec "find . -name '*.tmp' -print0 | xargs -0 rm"
or die "exec pipeline: $!";
Set a distinctive argv[0] so the child’s ps line identifies its
role:
exec { '/usr/local/bin/worker' } 'worker[queue=email]', @flags
or die "exec worker: $!";
Fork-and-exec — the classic pattern for launching a child without replacing yourself:
my $pid = fork // die "fork: $!";
if ($pid == 0) {
exec '/usr/bin/gzip', '-9', $file
or die "exec gzip: $!";
}
waitpid $pid, 0;
Guarantee the no-shell path when the argument list was assembled from data:
my @cmd = build_command(); # may contain one element or many
exec { $cmd[0] } @cmd
or die "exec $cmd[0]: $!";
Edge cases#
Never returns on success. Any code after a bare
execis reachable only on failure. Underuse warnings, a following statement other thandie,warn, orexittriggers a warning — Perl’s hint that you probably meantsystem.Single-argument trap.
exec $cmdwhere$cmdcame from user input is a shell-injection vector if$cmdcontains metacharacters. Use the indirect-object form or pass a pre-split list.Output buffering. Perl attempts to flush handles opened for output before handing control to the new program, but this is not guaranteed on every platform. If you have pending output on an unflushed handle, set
$|on the selected handle — or call theautoflushmethod on eachIO::Handle— beforeexec.ENDblocks andDESTROY.execdoes not runENDblocks, and it does not invokeDESTROYon your objects. The process image is replaced; Perl-level cleanup never happens. Anything that needs a clean shutdown (temp files, lockfile removal, buffered log flushes) must happen beforeexec.Close-on-exec. Filehandles with the
FD_CLOEXECflag set are closed by the kernel duringexecvp; others stay open. Control this withfcntlor the$^Fsystem filehandle threshold.Shell-mediated failure is invisible. When
execroutes through/bin/sh -c, a missing target program is reported by the shell andexecitself has already been replaced by/bin/sh. Your Perl code will not see a failure return.One-arg list with no metacharacters is still split on whitespace and passed to
execvp— soexec "ls /tmp"is a direct exec of/bin/lswith one argument, not a shell call.Empty
LISTis a runtime error; there is no program to run.
Differences from upstream#
Fully compatible with upstream Perl 5.42.
See also#
system— run a program and wait for it; what you almost certainly wanted ifexecis at the top of the filefork— pair withexecin the child to launch a program without replacing the parentwait— collect a child produced byfork+execdie— terminate the current program with an error; the idiomatic partner ofexec ... or die$!— error reason set when direct-exec fails$|— autoflush flag; set beforeexecso no buffered output is lost when the process image is replaced