Processes

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; execvp runs 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 -c for parsing. If none are present, Perl splits on whitespace and calls execvp directly — 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 wait itself failed. Distinct from “child exited with status 255”. Check $! for the reason — typically ENOENT (program not found) or EACCES (not executable).

  • Exit value 255 from a shell: when the shell form is used and /bin/sh cannot find the program, the shell itself exits with 127. Programs that return 255 from Perl typically come through die in another Perl program.

  • SIGCHLD handler interaction: system internally forks and waits. A $SIG{CHLD} handler can observe or race with this wait; see perlipc for the reaping discipline.

  • Shell quirks: when the shell form is triggered, return codes and error reporting are whatever /bin/sh says they are — not always what the underlying program returned. Use the list form to avoid this.

  • Not for capturing output: system writes the child’s output to the inherited STDOUT/STDERR. Use qx// or open a pipe with open if you need the output as a string.

  • Windows: only the system PROGRAM LIST syntax 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 as system

  • fork — create a child process without running a new program; system is fork + exec + waitpid

  • waitpid — reap a specific child, used when managing multiple children directly

  • qx — run a command and capture its output as a string; what you want when system’s stdio targeting is not enough

  • $? — child error status word set by system, backticks, pipe close, and wait

  • $! — errno after a failed fork or exec (return value -1)

  • ${^CHILD_ERROR_NATIVE} — full native wait status for decoding with POSIX W* macros