system#
Run a separate program and wait for it to finish.
system does exactly the same thing as exec, except that a
fork is done first and the parent process waits for the child
to exit. The return value is the wait status of the child, not its
output — if you want the output of a command, use backticks or
qx// instead.
Synopsis#
system LIST
system PROGRAM LIST
The indirect PROGRAM LIST form lies to the program about its own
name: PROGRAM is the file actually executed, while the first element
of LIST becomes argv[0] inside the child. Same trick as
exec.
What you get back#
The return value is the raw status word from the underlying
wait call — the same value that lands in $? after the
call. To recover the program’s actual exit code, shift right by eight:
system("make", "install");
my $exit = $? >> 8;
-1 means the program could not be started at all (or the wait
itself failed); inspect $! for the reason. A return of 0
means the child exited successfully with status 0.
Shell-vs-direct execution#
Argument processing depends on the shape of LIST, and mirrors
exec exactly:
More than one argument (or an array with more than one element): the first element is the program, the rest are its arguments. The shell is not invoked;
execvpruns the program directly. This is the safe form.Exactly one scalar argument: Perl scans it for shell metacharacters. If any are present, the entire string is passed to
/bin/sh -cfor parsing. If none are present, Perl splits on whitespace and callsexecvpdirectly — saving a shell.
Prefer the list form whenever the arguments come from untrusted input or contain characters the shell would interpret:
system("ls", "-l", $dir); # safe, no shell
system("ls -l $dir"); # shell parses $dir — hazardous
Signals vs. exit#
The status word combines three pieces of information: a signal number (if the child died on a signal), a core-dump flag, and the exit value. Decode it explicitly when you need to tell them apart:
system(@cmd);
if ($? == -1) {
print "failed to execute: $!\n";
}
elsif ($? & 127) {
printf "child died with signal %d, %s coredump\n",
($? & 127), ($? & 128) ? 'with' : 'without';
}
else {
printf "child exited with value %d\n", $? >> 8;
}
For a structured decode, inspect ${^CHILD_ERROR_NATIVE} —
the full native status word — with the WIFEXITED, WEXITSTATUS,
WIFSIGNALED, WTERMSIG macros from POSIX:
use POSIX qw(WIFEXITED WEXITSTATUS WIFSIGNALED WTERMSIG);
system(@cmd);
my $n = ${^CHILD_ERROR_NATIVE};
if (WIFEXITED($n)) { my $code = WEXITSTATUS($n); ... }
if (WIFSIGNALED($n)) { my $sig = WTERMSIG($n); ... }
SIGINT and SIGQUIT are ignored in the parent while the child runs.
If you want your own program to die when the user hits Ctrl-C during
system, check the return status yourself and re-raise:
system(@cmd);
kill 'INT', $$ if ($? & 127) == 2; # re-raise SIGINT on self
Stdio flushing#
Before the fork, Perl attempts to flush every filehandle opened for
output. This prevents buffered lines from being duplicated by both
parent and child. The flush is best-effort; on platforms where it is
unreliable, set $| ($AUTOFLUSH) or call the autoflush
method from IO::Handle on handles you care about.
The die idiom#
The canonical check — terse, and it embeds the status word in the message so the caller can still decode it:
my @args = ("command", "arg1", "arg2");
system(@args) == 0
or die "system @args failed: $?";
For a module that should throw on any failed system (and many other
bits of Perl), use the autodie pragma.
Examples#
Run a command and ignore the result:
system("clear");
Run a command, die on failure, and keep the status intact for downstream inspection:
system("tar", "xf", $archive) == 0
or die "untar $archive failed: $?";
Detect whether a program is available by running it with --version
and discarding its output:
my $ok = system("which $prog >/dev/null 2>&1") == 0;
Run under a specific shell explicitly, bypassing Perl’s metacharacter-detection heuristic:
system("/bin/bash", "-c", $pipeline);
Lie about argv[0] using the indirect form — the kernel executes
/usr/bin/vim, but the program sees its name as editor:
system { "/usr/bin/vim" } "editor", $file;
Edge cases#
Return of -1: the child was never created, or
waititself failed. Distinct from “child exited with status 255”. Check$!for the reason — typicallyENOENT(program not found) orEACCES(not executable).Exit value 255 from a shell: when the shell form is used and
/bin/shcannot find the program, the shell itself exits with127. Programs that return255from Perl typically come throughdiein another Perl program.SIGCHLDhandler interaction:systeminternally forks and waits. A$SIG{CHLD}handler can observe or race with this wait; seeperlipcfor the reaping discipline.Shell quirks: when the shell form is triggered, return codes and error reporting are whatever
/bin/shsays they are — not always what the underlying program returned. Use the list form to avoid this.Not for capturing output:
systemwrites the child’s output to the inheritedSTDOUT/STDERR. Useqx//or open a pipe withopenif you need the output as a string.Windows: only the
system PROGRAM LISTsyntax reliably avoids the shell. pperl is Linux-only, so this does not apply here; noted for portability.
Differences from upstream#
Fully compatible with upstream Perl 5.42.
See also#
exec— replace the current process instead of forking and waiting; same argument-processing rules assystemfork— create a child process without running a new program;systemisfork+exec+waitpidwaitpid— reap a specific child, used when managing multiple children directlyqx— run a command and capture its output as a string; what you want whensystem’s stdio targeting is not enough$?— child error status word set bysystem, backticks, pipeclose, andwait$!— errno after a failed fork or exec (return value-1)${^CHILD_ERROR_NATIVE}— full native wait status for decoding withPOSIXW*macros