Contents:
Using system and exec
Using Backquotes
Using Processes as Filehandles
Summary of Process Operations
Win32::Process
Exercises
Like the command shell, a Perl program can launch new processes, and like most other operations, has more than one way to do so.
The simplest way to launch a new process is to use the system
function. In its simplest form, this function hands a single string to a brand new command shell to be executed as a command. When the command is finished, the system
function returns the exit value of the command (typically 0
if everything went OK). Here's an example of a Perl program executing a dir command using a shell:
system("dir");
We're ignoring the return value here, but the dir command is not likely to fail anyway.
Where does the command's output go? In fact, where does the input come from, if it was a command that wanted input? These are good questions, and the answers to these questions are most of what distinguishes the various forms of process creation.
For the system
function, the three standard files (standard input, standard output, and standard error) are inherited from the Perl process. So, for the dir command in the previous example, the output goes wherever the print
STDOUT
output goes - probably to the invoker's command prompt. Because you are firing off another command shell, you can change the location of the standard output using the normal I/O redirections. For example, to put the output of the directory command into a file named this_dir, something like this will work just fine:
system("dir >this_dir") && die "cannot create this_dir";
This time, we not only send the output of the dir command into a file with a redirection to the new command shell, but also check the return status. If the return status is true (nonzero), something went wrong with the shell command, and the die
function will do its deed. This is backwards from normal Perl operator convention - a nonzero return value from the system
operator generally indicates that something went wrong. You can feed anything to the system
function that you can feed to your command shell.
Here's an example of generating a dir command and sending the output to a filename specified by a Perl variable:
$where = "dir_out.".++$i; # get a new filename system "dir >$where";
The double-quoted string is variable interpolated, so Perl replaces $where
with its value.
In addition to the standard filehandles, the current directory and the environment variables are inherited by the child. These variables are typically created by the command shell set command and accessed or altered using the %KEYNAME% construct. Environment variables are used by many utilities, including the command shell itself, to alter or control the way that utility operates.
Perl gives you a way to examine and alter current environment variables through a special hash called %ENV
(uppercase). Each key of this hash corresponds to the name of an environment variable, with the corresponding value being, well, the corresponding value. Examining this hash shows you the environment handed to Perl by the parent process - altering the array affects the environment used by Perl and by its children processes, but not that of its parents.
For example, here's a simple program that prints out all of your environment variables:
foreach $key (sort keys %ENV) { print "$key=$ENV{$key}\n"; }
Note that the equal sign here is not an assigment, but simply a text character that the print
function is using to say stuff like USERNAME=eriko
or COMSPEC=c:\nt\system32\cmd.exe
.
Here's a program snippet that alters the value of PATH
to make sure that the nmake command run by system
is looked for only in the correct places:
$oldPATH = $ENV{"PATH"}; # save previous path $ENV{"PATH"} = "c:\\msdev\\bin;c:\\winnt;c:\\winnt\\system32"; # force known path system("nmake myproj.mak >output"); # run command $ENV{"PATH"} = $oldPATH; # restore previous path
That's a lot of typing. It'd be faster just to set a local value for this hash element.
Despite its other shortcomings, the local
operator can do one thing that my
cannot: it can give just one element of an array or a hash a temporary value. For example:
{ local $ENV{"PATH"} = "c:\\msdev\\bin;c:\\winnt;c:\\winnt\\system32"; system("nmake fred bedrock >output"); }
The system
function can also take a list of arguments rather than a single argument. In this case, rather than handing the list of arguments off to a command shell, Perl treats the first argument as the command to run (located according to the PATH
if necessary) and the remaining arguments as arguments to the command without normal shell interpretation. In other words, you don't need to quote whitespace or worry about arguments that contain angle brackets because those are all merely characters to hand to the program. So, the following two commands are equivalent:
system "nmake 'fred flintstone.mak' buffaloes"; # using command shell system "nmake","fred flintstone.mak","buffaloes"; # using list
Giving system
a list rather than giving it a simple string saves one command shell process as well, so do this when you can. (In fact, when the one-argument form of system
is simple enough, Perl itself optimizes away the shell invocation entirely, calling the resulting program directly as if you had used the multiple-argument invocation.)
Here's another example of equivalent forms:
@cfiles = ("fred.c","barney.c"); # what to compile @options = ("-DHARD","-DGRANITE"); # options system "cc -o slate @options @cfiles"; # using shell system "cc","-o","slate",@options,@cfiles; # avoiding shell