Chapter 14. Process Management

One of the best parts of being a programmer is launching someone else’s code so that you don’t have to write it yourself. It’s time to learn how to manage your children[1] by launching other programs directly from Perl.

And like everything else in Perl, There’s More Than One Way To Do It, with lots of overlap, variations, and special features. So if you don’t like the first way, just read on for another page or two for a solution more to your liking.

Perl is very portable; most of the rest of this book doesn’t need many notes saying that it works this way on Unix systems and that way on Windows and the other way on VMS. But when you’re starting other programs on your machine, different programs are available on a Macintosh than you’ll likely find on a Cray. The examples in this chapter are primarily Unix-based; if you have a non-Unix system, you can expect to see some differences.

The system Function

The 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:

system "date";

The child process runs the date command, which inherits Perl’s standard input, standard output, and standard error. This mean that the normal short date-and-time string generated by date ends up wherever Perl’s STDOUT was already going.

The parameter to the system function is generally whatever you’d normally type at the shell. So 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';

Note that 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 also an indicator for Perl to interpolate. Alternatively, we could write:

system "ls -l $HOME";

But that can get quickly unwieldly.

Now, the date command is output-only, but let’s say it had been a chatty command, asking first “for which time zone do you want the time?”[2] 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, and type in the answer (like “Zimbabwe time”), and then date will finish its duty.

While the child process is running, Perl is patiently waiting for it to finish. So 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,[3] however:

system "long_running_command with parameters &";

Here, the shell gets launched, which then notices the ampersand at the end of the command line, causing the long_running_command to be made into a background process. And then the shell exits rather quickly, which Perl notices and moves on. In this case, the long_running_command is really a grandchild of the Perl process, to which Perl really has no direct access or knowledge.

When the command is “simple enough,” no shell gets involved, so for the date and ls commands earlier, the requested command is launched directly by Perl, which searches the inherited PATH [4] 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), then the standard Bourne Shell (/bin/sh[5] ) gets invoked 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 argument:

system 'for i in *; do echo == $i ==; cat $i; done';

Here again, we’re using single quotes, because the dollar signs here 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.[6] 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 out yourself if you don’t believe us.

Avoiding the Shell

The system operator may also be invoked with more than one argument,[7] 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, while 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 precisely five parameters. Compare this with:

system "tar cvf $tarfile @dirs";  # Oops!

Here, we’ve now piped a bunch of stuff into a flintstone command and put it into the background, and opened betty for output.

And that’s a bit scary,[8] especially if those variables are from user input—such as from a web form or something. So, if you can arrange things so that you can 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 to be processed by a different shell, like the C-shell:

system "/bin/csh", "-fc", $command_line;

Even this is pretty rare, since the One True Shell[9] 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[10]. In Unix, an exit value of 0 means that everything is OK, and a non-zero exit value usually indicates that something went wrong:

unless (system "date") {
  # Return was zero - meaning success
  print "We gave you a date, OK!
";
}

Note that this is backward from the normal “true is good—false 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 simply 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 not be appropriate, 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.

The exec Function

Everything we’ve just said about system syntax and semantics is also true about the exec function, except for one (very important) thing. The system function creates a child process, which then scurries off to perform the requested action while Perl naps. The exec function causes the Perl process itself to perform the requested action. Think of it as more like a “goto” than a subroutine call.

For example, suppose we wanted to run the bedrock command in the /tmp directory, passing it arguments of -o args1 followed by whatever arguments our own program was invoked with. That’d look like this:

chdir "/tmp" or die "Cannot chdir /tmp: $!";
exec "bedrock", "-o", "args1", @ARGV;

When we reach the exec operation, Perl locates bedrock, and “jumps into it.” At that point, there is no Perl process any more,[11] just the process running the bedrock command. When bedrock is finished, there’s no Perl to come back to, so we’d get a prompt back if we invoked this program from the command line.

Why is this useful? Well, if the purpose of this Perl program were to set up a particular environment to run another program, the purpose is fulfilled as soon as the other program has started. If we’d used system instead of exec, we’d have a Perl program just standing around tapping its toes waiting for the other program to complete, just so Perl could finally immediately exit as well, and that’s a wasted resource.

Having said that, it’s actually quite rare to use exec, except in combination with fork (which we’ll see later). If you are puzzling over system versus exec, just pick system, and nearly all of the time, you’ll be just fine.

Because Perl is no longer in control once the requested command has started, it doesn’t make any sense to have any Perl code following the exec, except for handling the error when the requested command cannot be started:

exec "date";
die "date couldn't run: $!";

In fact, if you have warnings turned on, and if you have any code after the exec other than a die,[12] you’ll get notified.

The Environment Variables

When you’re starting another process (with any of the methods discussed here), you may need to set up its environment in one way or another. As we mentioned earlier, you could start the process with a certain working directory, which it inherits from your process. Another common configuration detail is the environment variables.

The best-known environment variable is PATH . (If you’ve never heard of it, you probably haven’t used a system that has environment variables.) On Unix and similar systems, PATH is a colon-separated list of directories that may hold programs. When you type a command like rm fred, the system will look for the rm command in that list of directories, in order. Perl (or your system) will use PATH whenever it needs to find the program to run. If the program in turn runs other programs, those may also be found along the PATH. (Of course, if you give a complete name for a command, such as /bin/echo, there’s no need to search PATH. But that’s generally much less convenient.)

In Perl, the environment variables are available via the special %ENV hash; each key in this hash represents one environment variable. At the start of your program’s execution, %ENV holds values it has inherited from its parent process (generally the shell). Modifying this hash changes the environment variables, which will then be inherited by new processes and possibly used by Perl as well. For example, suppose you wished to run the system’s make utility (which typically runs other programs), and you want to use a private directory as the first place to look for commands (including make itself). And let’s say that you don’t want the IFS environment variable to be set when you run the command, because that might cause make or some subcommand do the wrong thing. Here we go:

$ENV{'PATH'} = "/home/rootbeer/bin:$ENV{'PATH'}";
delete $ENV{'IFS'};
my $make_result = system "make";

Newly created processes will generally inherit from their parent the environment variables, the current working directory, the standard input, output, and error streams, and a few more-esoteric items. See the documentation about programming on your system for more details. (But your program can’t change the environment for the shell or other parent process that started it, on most systems.)

Using Backquotes to Capture Output

With both system and exec, the output of the launched command ends up wherever Perl’s standard output is going. Sometimes, it’s interesting to capture that output as a string value to perform further processing. And that’s done simply by creating a string using backquotes instead of single or double quotes:

my $now = `date`;             # grab the output of date
print "The time is now $now"; # newline already present

Normally, this date command spits out a string approximately 30 characters long to its standard output, giving the current date and time followed by a newline. When we’ve placed date between backquotes, Perl executes the date command, arranging for its standard output to be captured as a string value, and in this case assigned to the $now variable.

This is very similar to the Unix shell’s meaning for backquotes. However, the shell also performs the additional job of ripping off the final end-of-line to make it easier to use the value as part of other things. Perl is honest; it gives the real output. To get the same result in Perl, we can simply add an additional chomp operation on the result:

chomp(my $no_newline_now = `date`);
print "A moment ago, it was $no_newline_now, I think.
";

The value beween backquotes is just like the single-argument form of system,[13] and is interpreted as a double-quoted string, meaning that backslash-escapes and variables are expanded appropriately.[14] For example, to fetch the Perl documentation on a list of Perl functions, we might invoke the perldoc command repeatedly, each time with a different argument:

my @functions = qw{ int rand sleep length hex eof not exit sqrt umask };
my %about;

foreach (@functions) {
  $about{$_} = `perldoc -t -f $_`;
}

Note that $_ will be a different value for each invocation, letting us grab the output of a different command varying only in one of its parameters. Also note that if you haven’t seen some of these functions yet, it might be useful to look them up in the documentation to see what they do!

There’s no easy equivalent of single quotes for backquotes[15] ; variable references and backslash items are always expanded. Also, there’s no easy equivalent of the multiple-argument version of system (where a shell is never involved). If the command inside the backquotes is complex enough, a Unix Bourne Shell (or whatever your system uses instead) is invoked to interpret the command automatically.

At the risk of actually introducing the behavior by demonstrating how not to do it, we’d also like to suggest that you avoid using backquotes in a place where the value isn’t being captured.[16] For example:

print "Starting the frobnitzigator:
";
`frobnitz -enable`; # please don't do this!
print "Done!
";

The problem is that Perl has to work a bit harder to capture the output of this command, even when you’re just throwing it away, and then you also lose the option to use multiple arguments to system to precisely control the argument list. So from both a security standpoint and an efficiency viewpoint, just use system instead, please.

Standard error of a backquoted command is inherited from Perl’s current standard error output. If the command spits out error messages to standard error, you’ll probably see them on the terminal, which could be confusing to the user who hasn’t personally invoked the frobnitz command. If you want to capture error messages with standard output, you can use the shell’s normal “merge standard error to the current standard output,” which is spelled 2>&1 in the normal Unix shell:

my $output_with_errors = `frobnitz -enable 2>&1`;

Note that this will make the standard error output intermingled with the standard output, much as it appears on the terminal (although possibly in a slightly different sequence because of buffering). If you need the output and the error output separated, there are many harder-to-type solutions.[17]

Similarly, standard input is inherited from Perl’s current standard input. Most commands we typically use with backquotes do not read standard input, so that’s rarely a problem. However, let’s say the date command asked which time zone (as we imagined earlier). That’ll be a problem, because the prompt for “which time zone” will be sent to standard output, which is being captured as part of the value, and then the date command will start trying to read from standard input. But since the user has never seen the prompt, he or she doesn’t know to be typing anything! Pretty soon, the user calls you up and tells you that your program is stuck.

So, stay away from commands that read standard input. If you’re not sure whether something reads from standard input, then add a redirection from /dev/null for input, like this:

my $result = `some_questionable_command arg arg argh </dev/null`;

Then the child shell will redirect input from /dev/null, and the grandchild questionable command will at worst try to read and immediately get an end of file.

Using Backquotes in a List Context

If the output from a command has multiple lines, the scalar use of backquotes returns it as a single long string containing newline characters. However, using the same backquoted string in a list context yields a list containing one line of output per element.

For example, the Unix who command normally spits out a line of text for each current login on the system as follows:

merlyn     tty/42     Dec 7  19:41
rootbeer   console    Dec 2  14:15
rootbeer   tty/12     Dec 6  23:00

The left column is the username, the middle column is the tty name (that is, the name of the user’s connection to the machine), and the rest of the line is the date and time of login (and possibly remote login information, but not in this example). In a scalar context, we get all that at once, which we would then need to split up:

my $who_text = `who`;

But in a list context, we automatically get the data broken up by lines:

my @who_lines = `who`;

We’ll have a number of separate elements in @who_lines, each one terminated by a newline. Of course, adding a chomp around the outside of that will rip off those newlines, but let’s go a different direction. If we put that as part of the value for a foreach, we’ll iterate over the lines automatically, placing each one in $_:

foreach (`who`) {
  my($user, $tty, $date) = /(S+)s+(S+)s+(.*)/;
  $ttys{$user} .= "$tty at $date
";
}

This loop will iterate three times for the data above. (Your system will probably have more than three active logins at any given time.) Notice that we’ve got a regular expression match, and in the absence of the binding operator (”=~"), that’s matching against $_, which is good because that’s where the data is.

Also notice the regular expression is looking for a nonblank word, some whitespace, a nonblank word, some whitespace, and then the rest of the line up to, but not including, the newline (since dot doesn’t match newline by default).[18] That’s also good, because that’s what the data looks like each time in $_. That’ll make $1 be "merlyn“, $2 be "tty/42“, and $3 be "Dec 7 19:41“, as a successful match on the first time through the loop.

However, this regular expression match is in a list context, so instead of returning back a true/false value (as when you have a regular expression match in a scalar context), we take the memory variables and bundle them up in sequence as a list. In this case, the right side of that assignment is thus a three-element list, which happens to correspond to the three elements of the literal list on the left, and we get those nice corresponding assignments. So, $user ends up being "merlyn", and so on.

The second statement inside the loop simply stores away the tty and date information, appending to a (possibly undef) value in the hash, because a user might be logged in more than once, as user "rootbeer" was in our example.

Processes as Filehandles

So far, we’ve been looking at ways to deal with synchronous processes, where Perl stays in charge, launches a command, (usually) waits for it to finish, then possibly grabs its output. But Perl can also launch a child process that stays alive, communicating[19] to Perl on an ongoing basis until the task is complete.

The syntax for launching a concurrent (parallel) child process is to put the command as the “filename” for an open call, and either precede the command or follow the command with a vertical bar, which is the “pipe” character. For that reason, this is often called a piped open:

open DATE, "date|" or die "cannot pipe from date: $!";
open MAIL, "|mail merlyn" or die "cannot pipe to mail: $!";

In the first example, with the vertical bar on the right, the command is launched with its standard output connected to the DATE filehandle opened for reading, similar to the way that the command date | your_program would work from the shell. In the second example, with the vertical bar on the left, the command’s standard input is connected to the MAIL filehandle opened for writing, similar to what happens with the command your_program | mail merlyn. In either case, the command is now launched and continues independently of the Perl process.[20]

The open fails if the child process cannot be created. If the command itself does not exist or exits erroneously, this will (generally) not be seen as an error when opening, but as an error when closing. We’ll get to that in a moment.

For all intents and purposes, the rest of the program doesn’t know, doesn’t care, and would have to work pretty hard to figure out that this is a filehandle opened on a process rather than on a file. So, to get data from a filehandle opened for reading, we’ll just do the normal read:

my $now = <DATE>;

And to send data to the mail process (waiting for the body of a message to deliver to merlyn on standard input), a simple print-with-a-filehandle will do:

print MAIL "The time is now $now"; # presume $now ends in newline

In short, you can pretend that these filehandles are hooked up to magical files, one that contains the output of the date command, and one that will automatically be mailed by the mail command.

If a process is connected to a filehandle that is open for reading, and then exits, the filehandle returns end-of-file, just like reading up to the end of a normal file. When you close a filehandle open for writing to a process, the process will see end-of-file. So, to finish sending the email, close the handle:

close MAIL;
die "mail: non-zero exit of $?" if $?;

Closing a filehandle attached to a process waits for the process to complete, so that Perl can get the process’s exit status. The exit status is then available in the $? variable (reminiscent of the same variable in the Bourne Shell), and is the same kind of number as the value returned by the system function: zero for success, nonzero for failure. Each new exited process overwrites the previous value though, so save it quickly if you want it. (The $? variable also holds the exit status of the most recent system or backquoted command, if you’re curious.)

The processes are synchronized just like a pipelined command. If you try to read and no data is available, the process is suspended (without consuming additional CPU time) until the sending program has started speaking again. Similarly, if a writing process gets ahead of the reading process, the writing process is slowed down until the reader starts to catch up. There’s a buffer (usually 4K bytes or so) in between so they don’t have to stay precisely in lock step.

Why use processes as filehandles? Well, it’s the only easy way to write to a process based on the results of a computation. But if you’re just reading, backquotes are often much easier to manage, unless you want to have the results as they come in.

For example, the Unix find command locates files based on their attributes, and it can take quite a while if used on a fairly large number of files (such as starting from the root directory). You can put a find command inside backquotes, but it’s often nicer to see the results as they are found:

open F, "find / -atime +90 -size +1000 -print|" or die "fork: $!";
while (<F>) {
  chomp;
  printf "%s size %dK last accessed on %s
",
    $_, (1023 + -s $_)/1024, -A $_;
}

The find command here is looking for all the files that were not accessed within the past 90 days and that are larger than 1000 blocks. (These are good candidates to be moved off to longer-term storage.) While find is searching and searching, Perl can wait. As each file is found, Perl responds to the incoming name and displays some information about that file for further research. Had this been written with backquotes, we’d not see any output until the find commmand had finished, and it’s comforting to see that it’s actually doing the job even before it’s done.

Getting Down and Dirty with Fork

In addition to the high-level interfaces already described, Perl provides nearly direct access to the low-level process management system calls of Unix and some other systems. If you’ve never done this before,[21] you will probably want to skip this section. While it’s a bit much to cover all that stuff in a chapter like this, let’s at least look at a quick reimplementation of this:

system "date";

Let’s look at how that would be done using the low-level system calls:

defined(my $pid = fork) or die "Cannot fork: $!";
unless ($pid) {
  # Child process is here
  exec "date";
  die "cannot exec date: $!";
}
# Parent process is here
waitpid($pid, 0);

Here, we’ve checked the return value from fork, which will be undef if it failed. Usually it will succeed, causing two separate processes to continue to the next line, but only the parent process has a nonzero value in $pid, so only the child process executes the exec function. The parent process skips over that and executes the waitpid function, waiting for that particular child to finish (if others finish in the meantime, they are ignored). If that all sounds like gobbledygook, just remember that you can continue to use the system function without being laughed at by your friends.

When you go to this extra trouble, you also have full control over arbitary pipe creation, rearranging filehandles, and noticing your process ID and your parent’s process ID (if knowable). But again, that’s all a bit complicated for this chapter, so see the details in the perlipc manpage (and in any good book on application programming on your system) for further information.

Sending and Receiving Signals

A Unix signal is a tiny message sent to a process. It can’t say much; it’s like a car horn honking—does that honk you hear mean “look out—the bridge collapsed” or “the light has changed—get going” or “stop driving—you’ve got a baby on the roof” or “hello, world”? Well, fortunately, Unix signals are a little easier to interpret than that, because there’s a different one for each of these situations.[22]

Different signals are identified by a name (such as SIGINT , meaning “interrupt signal”) and a corresponding small integer (in the range from 1 to 16, 1 to 32, or 1 to 63, depending on your Unix flavor). Signals are typically sent when a significant event happens, such as pressing the interrupt character (typically Control-C) on the terminal, which sends a SIGINT to all the processes attached to that terminal.[23] Some signals are sent automatically by the system, but they can also come from another process.

You can send signals from your Perl process to another process, but you have to know the target’s process ID number. How to figure that out is a bit complicated,[24] but let’s say you know that you want to send a SIGINT to process 4201. That’s easy enough:

kill 2, 4201 or die "Cannot signal 4201 with SIGINT: $!";

It’s named “kill” because one of the primary purposes of signals is to stop a process that’s gone on long enough. You can also use the string 'INT' in place of the 2 there, because signal number 2 is SIGINT. If the process no longer exists,[25] you’ll get a false return value, so you can also use this technique to see whether a process is still alive. A special signal number of 0 says “just check to see whether I could send a signal if I wanted to, but I don’t want to, so don’t actually send anything.” So a process probe might look like:

unless (kill 0, $pid) {
  warn "$pid has gone away!";
}

Perhaps a little more interesting than sending signals is catching signals. Why might you want to do this? Well, suppose you have a program that creates files in /tmp, and you normally delete those files at the end of the program. If someone presses Control-C during the execution, that leaves trash in /tmp, a very unpolite thing to do. To fix this, create a signal handler that takes care of the cleanup:

my $temp_directory = "/tmp/myprog.$$"; # create files below here
mkdir $temp_directory, 0700 or die "Cannot create $temp_directory: $!";

sub clean_up {
  unlink glob "$temp_directory/*";
  rmdir $temp_directory;
}

sub my_int_handler {
  &clean_up;
  die "interrupted, exiting...
";
}

$SIG{'INT'} = 'my_int_handler';
.
.   # Time passes, the program runs, creates some temporary
.   # files in the temp directory, maybe someone presses Control-C
.
# Now it's the end of normal execution
&clean_up;

The assignment into the special %SIG hash activates the handler (until revoked). The key is the name of the signal (without the constant SIG prefix), and the value is a string[26] naming the subroutine, without the ampersand. From then on, if a SIGINT comes along, Perl stops whatever it’s doing and jumps immediately to the subroutine. Our subroutine cleans up the temp files and then exits. (And if nobody presses Control-C, we’ll still call &clean_up at the end of normal execution.)

If the subroutine returns rather than exiting, execution resumes right where it was interrupted. This can be useful if the interrupt needs to actually interrupt something rather than causing it to stop. For example, suppose processing each line of a file takes a few seconds, which is pretty slow, and you want to abort the overall processing when an interrupt is processed, but not in the middle of processing a line. Just set a flag in the interrupt procedure, and check it at the end of each line’s processing:

my $int_count;
sub my_int_handler { $int_count++ }
$SIG{'INT'} = 'my_int_handler';
...
$int_count = 0;
while (<SOMEFILE>) {
  ... some processing that takes a few seconds ...
  if ($int_count) {
    # interrupt was seen!
    print "[processing interrupted...]
";
    last;
  }
}

Now as each line is processed, the value of $int_count will be 0 if no one has pressed Control-C, and so the loop continues to the next item. However, if an interrupt comes in, the interrupt handler increments the $int_count flag, breaking out of the loop when checked at the end.

So, you can either set a flag or break out of the program, and that covers most of what you’ll need from catching signals. The current implementation of signal handlers is not entirely without faults,[27] however, so keep the stuff you’re doing in there to an absolute minimum, or your program may end up blowing up sometime when you least expect it.

Exercises

See Section A.13 for answers to the following exercises:

  1. [6] Write a program that changes to some particular (hardcoded) directory, like the system’s root directory, then executes the ls -l command to get a long-format directory listing in that directory. (If you use a non-Unix system, use your own system’s command to get a detailed directory listing.)

  2. [10] Modify the previous program to send the output of the command to a file called ls.out in the current directory. The error output should go to a file called ls.err. (You don’t need to do anything special about the fact that either of these files may end up being empty.)

  3. [8] Write a program to parse the output of the date command to determine the current day of the week. If the day of the week is a weekday, print get to work, otherwise print go play. The output of the date command begins with Mon on a Monday.[28] If you don’t have a date command on your non-Unix system, make a fake little program that simply prints a string like date might print. We’ll even give you this two-line program if you promise not to ask us how it works :

    #!/usr/bin/perl
    print localtime( ) . "
    ";


[1] Child processes, that is.

[2] As far as we know, no one has made a date command that works like this.

[3] See what we mean about this depending upon your system? The Unix shell (/bin/sh) lets you use the ampersand on this kind of command to make a background process. If your non-Unix system doesn’t support this way to launch a background process, then you can’t do it this way, that’s all.

[4] The PATH can be changed by adjusting $ENV{'PATH'} at any time. Initially, this is the environment variable inherited from the parent process (usually the shell). Changing this value affects new child processes, but cannot affect any preceding parent processes. The PATH is the list of directories where executable programs (commands) are found, even on some non-Unix systems.

[5] Or whatever was determined when Perl was built. Practically always, this is just /bin/sh on Unix-like systems.

[6] Of course, if you set $i = '$i', then it would work anyway, until a maintenance programmer came along and “fixed” that line out of existence.

[7] Or with a parameter in the indirect-object slot, like system { 'fred' } 'barney';, which runs the program barney, but lies to it so it thinks that it’s called 'fred'. See the perlfunc manpage.

[8] Unless you’re using taint checking and have done all the right things to prescan your data to ensure that the user isn’t trying to pull a fast one on you.

[9] That’s /bin/sh, or whatever your Unix system has installed as the most Bourne-like shell. If you don’t have a One True Shell, Perl figures out how to invoke some other command-line interpreter, with notable consequences—noted, that is, in the documentation for that Perl port.

[10] It’s actually the “wait” status, which is the child exit code times 256, plus 128 if core was dumped, plus the signal number triggering termination, if any. But we rarely check the specifics of that, and a simple true/false value suffices for nearly all applications.

[11] Actually, it’s the same process, having performed the Unix exec(2) system call (or equivalent). The process ID remains the same.

[12] Or exit. Or if it’s at the end of a block. This may change in a new release of Perl, too.

[13] That is, it’s also always interpreted by the One True Shell (/bin/sh) or alternative, as with system.

[14] So, if you want to pass a real backslash to the shell, you’ll need to use two. If you need to pass two (which happens frequently on Windows systems), you’ll need to use four.

[15] For a couple of harder ways, you can place your string inside qx'...'delimiters, or you can put it all in a variable using a single-quoted string, then interpolate that string into a backquoted string, since the interpolation will be only one level.

[16] This is called a “void” context.

[17] Such as IPC::Open3 in the standard Perl library, or writing your own forking code, as we will see later.

[18] Now you can see why dot doesn’t match newline by default. It makes it easy to write patterns like this one, in which we don’t have to worry about a newline at the end of the string.

[19] Via pipes, or whatever your operating system provides for simple interprocess communication.

[20] If the Perl process exits before the command is complete, a command that’s been reading will see end-of-file, while a command that’s been writing will get a “broken pipe” error signal on the next write, by default.

[21] Or you’re not running on a system that has support for forking. But the Perl developers are working hard to add forking even on systems whose underlying process model is very different than the one in Unix.

[22] Well, not exactly these situations, but analogous Unix-like ones. For these, the signals are SIGHUP, SIGCONT, SIGINT, and the fake SIGZERO (signal number zero).

[23] And you thought that pressing Control-C stopped your program. Actually, it simply sends the SIGINT signal, and that stops the program by default. As we’ll see later in this chapter, you can make a program that does something different when SIGINT comes in, rather than stopping at once.

[24] Usually you have the process ID because it’s a child process you produced with fork, or you found it in a file or from an external program. Using an external program can be difficult and problematic, which is why many long-running programs save their own current process ID into a file, usually described in the program’s documentation.

[25] Sending a signal will also fail if you’re not the superuser and it’s someone else’s process. It would be rude to send SIGINT to someone else’s programs, anyway.

[26] The value can also be a subroutine reference, but we’re not doing those here.

[27] This is one of the top items on the Perl developers’ list of things to be fixed, so we expect reliable signal handling to be one of the first items on the new feature list for Perl 6. The problem is that a signal may come in at any time, even when Perl isn’t ready for one. If Perl is (for example) in the middle of allocating some memory when a signal comes in, the signal handler can accidentally try to allocate some memory and—your program is dead. You can’t control when your Perl code will allocate memory, but XSUB code (usually written in C) can safely handle signals. See the Perl documentation for more information about this advanced topic.

[28] At least when the days of the week are being given in English. You might have to adjust accordingly if that’s not the case on your system.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset