6. System Operators

Perl offers a number of operators that mimic or call UNIX system operators available through a shell. Consequently, the discussion here will assume familiarity with corresponding UNIX facilities and will be oriented toward accessing those facilities through Perl. For additional details on system functions, per se, see the appropriate man pages or other UNIX sources.

Perl system operators can be divided into two large categories: file/directory operators and process operators. However, while useful, the distinction does not always hold. For example, Perl provides a mechanism whereby operators can be accessed as if they were files, permitting a Perl script to "read" the data they produce or to "write" to them to supply input data.


6.1 File/directory operators

chdir

Allows a Perl process to change its location to a specified directory within the file system. The function takes a single argument, an expression that evaluates to the path for the desired directory, and returns a true/false value indicating success or failure.

Form:

chdir ("/path/ . . . /directory");

Example:

chdir ("/afs/unc.cs.edu/home/jbs/public_html/perl");

Note that the path is defined within the namespace of the UNIX file system, not the namespace as configured for a Web server. In the author's UNIX environment, users have individual home directories under the directory, /home. Under each user's home directory is a public_html directory, intended to be used for his or her Web-related materials.

When specifying a path for a Web server, ~login can be used to abbreviate the path to the user's home directory because of the way the server is configured. As a result, the Web server automatically inserts /public_html into the path following ~login. Consequently, for files and directories below public_html, one MUST NOT specify /public_html; otherwise, that directory will be duplicated in the Web path.

When specifying the path for a Perl program, /home/login is used instead of the tilde abbreviation and public_html MUST be included, if it lies along the path. One implication of this difference in the two name spaces is that Perl programs (as well as executables in another language) can reference files outside the subset of the filespace for which the server is configured.

opendir

Opens a directory so that subsequent operations can read the members of the directory, as described below. Takes two arguments: a filehandle that will be used with subsequent readdir operators and the path to the directory to be opened; the operator returns true/false indicating success/failure.

Form:

opendir (DIR_NAME, "/path/ . . . /directory");

Example:

opendir (DIR, "/afs/unc.cs.edu/home/jbs/public_html/perl");-

readdir

Once a directory is open (using opendir) and a file handle is established for it, the names of files and directories within it can be read into a Perl program. Like other read operators, readdir delivers either the name or all names, depending on whether it occurs within a scalar or array context.

Form:

readdir(DIR_NAME)

Example:

$name = readdir(DIR); # just one name @name = readdir(DIR); # all names

closedir

Closes a directory that has been opened with opendir. Directories are automatically closed at the end of execution of Perl program, but closedir provides an explicit operator for doing this and promotes "neatness."

Form:

closedir(DIR_NAME)

Example:

closedir(DIR);

symlink

UNIX links provide a mechanism whereby a file or directory that exists in one directory can be referenced in another directory. Two types of links exist. Symbolic links, also called soft links, are more flexible in that the file or directory that is pointed to does not have to exist at the time the link is created nor are there restrictions on where such a file or directory has to be stored. By contrast, hard links are not normally permitted for directories and UNIX requires that the file and its linked surrogate reside on the same physical volume. Because of these restrictions, symbolic links are likely to be more appropriate for most tasks.

Form:

symlink("path", "LINK_NAME");

Example:

symlink("/afs/unc.cs.edu/home/jbs/public_html/perl", DIR_PERL);

In this example, the separate directory I use for Perl scripts can be referenced directly from the context (directory) where a Perl script is run by a Web server, such as cgi-bin.

link

Hard links are created using the link operator. As already stated, hard links cannot be made to directories, and files linked to one another must reside on the same physical volume. Hard links constitute a form of reference by name. Consequently, there is no notion of a primary version of a file and its secondary aliases; all references to the underlying file are equal and no such reference is more fundamental than another.

Form:

link("path/file", "LINK_NAME");

Example:

link("DIR_PERL/hello.html", HELLO_HTML.PL);
Note that the path in this example assumes the symlink created in the prior step and that the hard link defined here is to a particular file, not to a directory.

readlink

Provides the same information for a symlink to a Perl program that is provided by the ls -l command.

Form:

readlink("LINK_NAME");

Example:

readlink("DIR_PERL");

unlink

Files are removed using the unlink operator. For hard linked files, the underlying file is removed only when the last link or reference to it is removed. Consequently, unlink deletes the specified file within the current directory but does not affect other possible references to the file in other file system contexts.

Form:

unlink("LINK_NAME");

Example:

unlink(HELLO_HTML.PL);

rename

Files are moved using rename. If the system crashes during a move/rename, the file may be lost. Consequently, many people first copy a file and then delete the original version instead of moving/renaming.

Form:

rename("old_file_name", "new_file_name");

Example:

rename("hello_html", "hello_html.pl");

mkdir

Directories can be created through a Perl program using the mkdir operator. It takes two arguments -- a name for the new directory and a mode -- and it returns a true/false success/failure code. The mode designates access permissions for the directory and conform to standard UNIX octal values for such. For example, 0777 gives read/write/execute permission to owner, group, and others, whereas 0666 gives read/write permissions to everyone, but not execute permission. In general a value of "4" indicates read, "2" write, and "1" execute. When the value occurs in the highest order position, it refers to the owner; in the middle position, it refers to the owner's group; and in the low-order position, it refers to others. Thus, 755 gives read and execute permission to everyone, but reserves write access for only the owner. See the man page for chmod for a list of access codes in octal form.

Form:

mkdir("new_dir_name", mode);

Example:

mkdir("perl_scripts", 0777);

rmdir

Removes a directory, but only if the directory is empty, i.e., all of its files have previously been deleted. It returns a true/false success/failure code.

Form:

rmdir("dir_name");

Example:

rmdir("perl_scripts");

chmod

Changes the access permissions on a file. It includes a mode and one or more files whose permissions are to be changed. It returns a true/false success/failure code.

Modes, again, are composable octal values that can be found in the man page for chmod.

Form:

chmod(mode, "file_name");

Example:

chmod(0775, "hello_html.pl");
The primary file and directory operators were discussed, above. Several additional one are available, such as commands to change timestamps and ownership of files. They will not be discussed here. See a standard text on Perl for descriptions of them.

The next group of operators are concerned with UNIX processes.


6.2 Process operators

Process operators range in functionality from the capability to execute a UNIX system operator as is normally done from a shell to the operators used to implement a client/server architecture using forked processes with multiple threads of execution. The discussion will proceed from the simple to the more complex.

system

The simplest form of Perl process operator is the system operator. Just as a UNIX shell launches a new process to carry out a command, so the system operator causes Perl to launch a new process to carry out the indicated operation. It takes a single argument, the name of the process or command to be executed, and it returns a success/failure code. However, unlike many other operators, system normally returns a zero if successful and a nonzero value if unsuccessful.

When the process executes, it inherits the files of the Perl process from which it is launched. Thus, output produced by the process normally goes to the standard files, such as STDOUT and STDERR, whereas input normally comes from the standard input file, STDIN. However, data can be redirected to/from other files.

In addition to inheriting the files of the parent process, the child also inherits its environment variables. These are available through the %ENV associative array. For a more through discussion of environment variables within a Web context, see the course Perl-CGI Tutorial.

Form:

system("process"):

Example:

system("pwd"); system("xpwd");
In the first example, Perl asks the system to execute the path process which, in turn, determines the path of the current location and writes that information to STDOUT, which is normally displayed on the terminal screen; it then returns a value of zero to the Perl Program from which it was launched, indicating success.

In the second example, perl asks the system to execute a nonexistent process. The system operator cannot oblige (fails) and returns a nonzero value (i.e., 65,280).

backquotes

Backquotes provide a means of returning to the Perl process the value generated by the child process that would have been written to STDOUT or another file, had that process been launched through the system operator. Thus, this form of system call is usually better suited for most Perl applications than the < CODE >system operator described above. For example, returning the output of a system function to the parent process, rather than its return value, is needed if the parent Perl program, run through the CGI by a Web server, wishes to return to a Web client the results of the system function. Otherwise, what would be available to the parent Perl program would be just the return code of the system operator.

Form:

`process`

Example:

$A = system("pwd"); $A = `pwd`;

In the first example, the pwd process is launched and $A is given the value 0 if it executes properly or some nonzero value (i.e., 62,580) if it does not.

In the second example, the pwd process is launched, but instead of writing its results to some file (e.g., STDOUT), the result is returned by the backquotes operator and assigned to $A. Thus, in this case, $A ends up with the character string that designates the path to the directory where the Perl script is executed (e.g., /afs/cs.unc.edu/home/jbs/public_html/perl).

filehandles

Since UNIX command processes normally write their output to a file, such as STDOUT, and/or receive their input from standard input (STDIN), they may be "opened" and assigned a filehandle so that subsequent I/O can come from or be directed to the Perl program through conventional read and write operators. Thus, processes can be launched and subsequently treated as if they were files.

The filehandle form of process interaction is based on underlying UNIX pipes; consequently, if the process is to be accessed through a read, a vertical bar (|) must be appended to the right side of the process name; conversely, if it is to be accessed through a write, the vertical bar goes on the left side.

Form:

open(PROC_HANDLE, "process|"); close(PROC_HANDLE); open(PROC_HANDLE, "|process"); close(PROC_HANDLE);

Example:

open (PWD_HANDLE, "pwd|"); # read and process pwd data close(PWD_HANDLE); open (MORE_HANDLE, "|more"); # generate and write data to more close(MORE_HANDLE);

In the first example, the process, pwd, will be opened for read access in the Perl program. In the second example, the process, more, will be opened for write access.

In both cases, the processes should be closed since they will continue running otherwise. Any I/O directed to or from the processes would, of course, be done between the open and close statements.

exec

The exec operator works much like the system operator except that when it launches another process, the Perl program from which the launch originated immediately terminates.

Form:

exec "process";

Example:

exec "pwd";
Note: exec causes problems for the Web server when used in the CGI context.

fork

The most sophisticated and most powerful of the process operators is fork. It enables a process to launch a duplicate of itself that can run concurrently with the parent process from which it was launched and is often used to implement the server part of client/server designs. The parent and child processes are virtually identical, sharing the same code, variables, and open files. They are differentiated from one another only by the return code generated by the fork operator. It returns a value of zero (0) to the child process and a one (1) to the parent process. Thus, the fork operator often appears within a conditional statement, such as an if/else or an unless construct.

Form:

(fork)

Example:

if (fork) { # parent process } else { # child process }

exit

exit causes a process to terminate immediately. Thus, when used with a launched process, it functions much like a return statement in a subprocedure. It can be used to kill a forked process that would continue running, otherwise.

Form:

exit;

Example:

unless (fork) { # zero(0) condition # child process exit; { # one(1) condition # parent process
In this example, the unless functions as an if not construct; hence the false condition in which the child process is defined comes first. Once the code in that condition is completed, if it were not stopped (i. e., by the exit), the child process would continue beyond the curly braces where the parent process code appears.

wait

wait causes the parent process to wait until the child process completes execution before continuing.

Form:

wait

Example:

unless (fork) { # zero(0) condition # child process exit; { # one(1) condition # parent process wait; # parent waits until child completes before continuing