14.1. The system FunctionThe simplest way to launch a child process in Perl to run a program is the system function. For example, to invoke the Unix date command from within Perl, it looks like this: system "date"; The child process runs the date command, which inherits Perl's standard input, standard output, and standard error. This mean the normal short date-and-time string generated by date ends up wherever Perl's STDOUT was going. The parameter to the system function is generally whatever you'd normally type at the shell. If it were a more complicated command, like "ls -l $HOME", we'd just have put all that into the parameter: system 'ls -l $HOME'; We had to switch here from double quotes to single quotes since $HOME is the shell's variable. Otherwise, the shell would never have seen the dollar sign since that's an indicator for Perl to interpolate. Alternatively, we could write this: system "ls -l \$HOME"; But that can become unwieldly. The date command is output-only, but say it had been a chatty command, asking first "for which time zone do you want the time?"[*] That'll end up on standard output, and then the program will listen on standard input (inherited from Perl's STDIN) for the response. You'll see the question, type in the answer (like "Zimbabwe time"), and date will finish its duty.
While the child process is running, Perl is patiently waiting for it to finish. If the date command took 37 seconds, then Perl is paused for those 37 seconds. You can use the shell's facility to launch a background process[]:
system "long_running_command with parameters &"; Here the shell launches, notices the ampersand at the end of the command line, and puts long_running_command into the background. And then the shell exits quickly, which Perl notices and moves on. In this case, the long_running_command is a grandchild of the Perl process, to which Perl has no direct access or knowledge. When the command is "simple enough," no shell gets involved. For the date and ls commands, the requested command is launched directly by Perl, which searches the inherited PATH[] to find the command if necessary. But if there's anything weird in the string (such as shell metacharacters like the dollar sign, semicolon, or vertical bar), Perl invokes the standard Bourne Shell (/bin/sh[§]) to work through the complicated stuff. In that case, the shell is the child process, and the requested commands are grandchildren (or further offspring). For example, you can write an entire little shell script in the following argument:
system 'for i in *; do echo = = $i = =; cat $i; done'; Here again, we're using single quotes because the dollar signs are meant for the shell and not for Perl. Double quotes would have permitted Perl to expand $i to its current Perl value and not let the shell expand it to its own value.[**] By the way, that little shell script goes through all of the normal files in the current directory, printing out each one's name and contents; you can try it if you don't believe us.
14.1.1. Avoiding the ShellThe system operator may be invoked with more than one argument,[*] in which case a shell doesn't get involved no matter how complicated the text:
my $tarfile = "something*wicked.tar"; my @dirs = qw(fred|flintstone <barney&rubble> betty ); system "tar", "cvf", $tarfile, @dirs; In this case, the first parameter ("tar" here) gives the name of a command found in the normal PATH-searching way, and the remaining arguments are passed, one by one, directly to that command. Even if the arguments have shell-significant characters, such as the name in $tarfile or the directory names in @dirs, the shell never gets a chance to mangle the string. So, that tar command will get five parameters. Compare this with: system "tar cvf $tarfile @dirs"; # Oops! Here, we've now piped a bunch of stuff into a flintstone command, put it into the background, and opened betty for output. That's a bit scary,[] especially if those variables are from user input, such as from a web form or something. So, if you can arrange things to use the multiple-argument version of system, you probably should use that way to launch your subprocess. (You'll have to give up the ability to have the shell do the work for you to set up I/O redirection, background processes, and the like, though. There's no such thing as a free launch.)
Note that redundantly, a single-argument invocation of system is nearly equivalent to the proper multiple-argument version of system: system $command_line; system "/bin/sh", "-c", $command_line; But nobody writes the latter unless you want things processed by a different shell, like the C-shell: system "/bin/csh", "-fc", $command_line; Even this is rare since the One True Shell[*] seems to have a lot more flexibility, especially for scripted items.
The return value of the system operator is based upon the exit status of the child command[]. In Unix, an exit value of 0 means that everything is all right, and a nonzero exit value usually indicates that something went wrong:
unless (system "date") { # Return was zero - meaning success print "We gave you a date, OK!\n"; } This is backward from the normal "true is goodfalse is bad" strategy for most of the operators, so to write a typical "do this or die" style, we'll need to flip false and true. The easiest way is to prefix the system operator with a bang (the logical-not operator): !system "rm -rf files_to_delete" or die "something went wrong"; In this case, including $! in the error message would be inappropriate because the failure is most likely somewhere within the experience of the rm command, and it's not a system-call related error within Perl that $! can reveal. |