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 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.
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.
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.
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.)
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.
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.
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.
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.
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.
See Section A.13 for answers to the following exercises:
[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.)
[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.)
[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.