Perl has many ways of launching and managing different programs. This is a Good Thing, because Perl's ability to launch and manage programs -- or child processes -- is one of the reasons it makes such a great "duct-tape of the Internet." The easiest way to launch a child process is with system:
The child process here is the date command. Anything that can be invoked from a shell command prompt can be used in this string. The child process inherits Perl's standard input, output, and error output, so the output of this datecommand will show up wherever Perl's STDOUT was going.
The command can be arbitrarily complex, including everything that /bin/sh (or its equivalent) can handle:
system "for i in *; do echo == \$i ==;
cat \$i; done";
Here, we're dumping out the contents of a directory, one file at a time. The $i vars are backslashed here because Perl would have expanded them to their current Perl values and we want the shell to see its own $i instead. A quick solution here is to use single quotes instead of double quotes. Single quotes, unlike double quotes, prevent the evaluation of references to Perl variables.
system 'for i in *; do echo == $i ==;
cat $i; done';
Or, you can just set the value of Perl's $i to '$i', but that's pretty twisted, and will probably drive the maintenance programmer who inherits your code crazy.
This might look better spaced over multiple lines, so we can use a here-string to fix it:
system <<'END';
for i in *
do
echo == $i ==
cat $i
done
END
Yeah, that cleans it up a bit.
If the argument is simple enough, Perl avoids the shell, finding the program directly. You may wish to adjust $ENV{PATH} before calling system so that the program is found in the right place. Anything complicated forces a shell though.
That shell can get in the way at times. Imagine invoking grep on a few files based on a string in a scalar variable:
system "grep $look_for brief1 brief2 brief3";
Now if $look_for is a nice easy string like Monica,no big deal. But if it's complicated like White House, we now have a problem, because that'll interpolate like this:
system "grep White House brief1 brief2 brief3";
which is looking for White in the other four names, including a file named House. That's broken. Badly. So, perhaps we can fix it by including some quotes:
system "grep '$look_for' brief1 brief2 brief3"
This works for White House, but fails on Don't lie!. And if we change the shell single quotes to double quotes, that will just mess up when $look_for contains double quotes!
Luckily, we can avoid the shell entirely, using the multiple argument version of system:
system "grep", $look_for, "brief1",
"brief2", "brief3";
When system is given more than one argument, the first one must be a program found along the PATH. The others are handed, uninterpreted by any shell, directly to the program.If it were another Perl script, the elements of @ARGVin the called program would match the same elements as this list.
Because we now no longer call a shell, things like I/O redirection no longer work. There are tradeoffs to this method, but it comes in handy. It's also more secure -- there is no chance that a nefarious user to sneak in a new line or semi-colon. Some popular CGI scripts didn't get this right, and ended up triggering a CERT notification as a security hole.
While the child process is executing, Perl is stopped. So if a command takes 35 seconds to run, Perl is stopped for 35 seconds. You can fork a child process in the background by adding the ampersand, just as you would in the shell:
system "long_running_command and the parameters &"
Be aware that you'll have no easy way to interact with this command, or even know its PID to kill it.
The return value of the system operator is the value from the wait (or waitpid) system call. That is, if the child process exited with a zero value (everything went OK), so too will the return value from system be zero. A non-zero value is shifted left 8 bits (or multiplied by 256, if you prefer). If a signal killed the process, that's bitwise-or'ed into the number, and a 128 is added if there's a core file cluttering up the directory now.
If you don't grab the result from system, the same number is available in the special $? variable. That is, until another process is waited for, because $? records only the most recently waited-for process status. So, to get the specs on the most recent exit, it's something like:
$status = ($? >> 8);
$core_dumped = ($? & 128) > 0;
$signal = ($? & 127);
Because the "zero if everything is OK" is backwards from most of the rest of Perl, you shouldn't use or die directly. Instead, the easiest fix is to invert the output of system with a logical "not" operation:
!system "some_maybe_failing_command"
or die "we broke it";