Named Unary and File Test Operators

Some of the "functions" described in Chapter 29 are really unary operators. Table 3.2 lists all the named unary operators.

Table 3-2. Named Unary Operators

-X (file tests)gethostbynamelocaltimereturn
alarmgetnetbynamelockrmdir
callergetpgrplogscalar
chdirgetprotobynamelstatsin
chrootglobmysleep
cosgmtimeoctsqrt
definedgotoordsrand
deletehexquotemetastat
dointranduc
evallcreadlinkucfirst
existslcfirstrefumask
exitlengthrequireundef

Unary operators have a higher precedence than some of the binary operators. For example:

sleep 4 | 3;

does not sleep for 7 seconds; it sleeps for 4 seconds and then takes the return value of sleep (typically zero) and bitwise ORs that with 3, as if the expression were parenthesized as:

(sleep 4) | 3;

Compare this with:

print 4 | 3;

which does take the value of 4 ORed with 3 before printing it (7 in this case), as if it were written:

print (4 | 3);

This is because print is a list operator, not a simple unary operator. Once you've learned which operators are list operators, you'll have no trouble telling unary operators and list operators apart. When in doubt, you can always use parentheses to turn a named unary operator into a function. Remember, if it looks like a function, it is a function.

Another funny thing about named unary operators is that many of them default to $_ if you don't supply an argument. However, if you omit the argument but the token following the named unary operator looks like it might be the start of an argument, Perl will get confused because it's expecting a term. Whenever the Perl tokener gets to one of the characters listed in Table 3.3, the tokener returns different token types depending on whether it expects a term or operator.

Table 3-3. Ambiguous Characters

CharacterOperatorTerm
+AdditionUnary plus
-SubtractionUnary minus
*Multiplication*typeglob
/Division/pattern/
<Less than, left shift<HANDLE>, <<END
.Concatenation.3333
??:?pattern?
%Modulo%assoc
&&, &&&subroutine

So a typical boo-boo is:

next if length < 80;

in which the < looks to the parser like the beginning of the <> input symbol (a term) instead of the "less than" (an operator) you were thinking of. There's really no way to fix this and still keep Perl pathologically eclectic. If you're so incredibly lazy that you cannot bring yourself to type the two characters $_, then use one of these instead:

next if length() < 80;
next if (length) < 80;
next if 80 > length;
next unless length >= 80;

When a term is expected, a minus sign followed by a single letter will always be interpreted as a file test operator. A file test operator is a unary operator that takes one argument, either a filename or a filehandle, and tests the associated file to see whether something is true about it. If the argument is omitted, it tests $_, except for -t, which tests STDIN. Unless otherwise documented, it returns 1 for true and "" for false, or the undefined value if the file doesn't exist or is otherwise inaccessible. Currently implemented file test operators are listed in Table 3.4.

Table 3-4. File Test Operators

OperatorMeaning
-rFile is readable by effective UID/GID.
-wFile is writable by effective UID/GID.
-xFile is executable by effective UID/GID.
-oFile is owned by effective UID.
-RFile is readable by real UID/GID.
-WFile is writable by real UID/GID.
-XFile is executable by real UID/GID.
-OFile is owned by real UID.
-eFile exists.
-zFile has zero size.
-sFile has nonzero size (returns size).
-fFile is a plain file.
-dFile is a directory.
-lFile is a symbolic link.
-pFile is a named pipe (FIFO).
-SFile is a socket.
-bFile is a block special file.
-cFile is a character special file.
-tFilehandle is opened to a tty.
-uFile has setuid bit set.
-gFile has setgid bit set.
-kFile has sticky bit set.
-TFile is a text file.
-BFile is a binary file (opposite of -T).
-MAge of file (at startup) in days since modification.
-AAge of file (at startup) in days since last access.
-CAge of file (at startup) in days since inode change.

Note that -s/a/b/ does not do a negated substitution. Saying -exp($foo) still works as expected, however--only single letters following a minus are interpreted as file tests.

The interpretation of the file permission operators -r, -R, -w, -W, -x, and -X is based solely on the mode of the file and the user and group IDs of the user. There may be other reasons you can't actually read, write, or execute the file, such as Andrew File System (AFS) access control lists.[3] Also note that for the superuser, -r, -R, -w, and -W always return 1, and -x and -X return 1 if any execute bit is set in the mode. Thus, scripts run by the superuser may need to do a stat in order to determine the actual mode of the file or temporarily set the UID to something else.

The other file test operators don't care who you are. Anybody can use the test for "regular" files:

while (<>) {
    chomp;
    next unless -f $_;      # ignore "special" files
    …
}

The -T and -B switches work as follows. The first block or so of the file is examined for strange characters such as control codes or bytes with the high bit set (that don't look like UTF-8). If more than a third of the bytes appear to be strange, it's a binary file; otherwise, it's a text file. Also, any file containing ASCII NUL () in the first block is considered a binary file. If -T or -B is used on a filehandle, the current input (standard I/O or "stdio") buffer is examined rather than the first block of the file. Both -T and -B return true on an empty file, or on a file at EOF (end-of-file) when testing a filehandle. Because Perl has to read a file to do the -T test, you don't want to use -T on special files that might hang or give you other kinds of grief. So on most occasions you'll want to test with a -f first, as in:

next unless -f $file && -T $file;

If any of the file tests (or either the stat or lstat operator) are given the special filehandle consisting of a solitary underline, then the stat structure of the previous file test (or stat operator) is used, thereby saving a system call. (This doesn't work with -t, and you need to remember that lstat and -l will leave values in the stat structure for the symbolic link, not the real file. Likewise, -l _ will always be false after a normal stat.)

Here are a couple of examples:

print "Can do.
" if -r $a || -w _ || -x _;

stat($filename);
print "Readable
" if -r _;
print "Writable
" if -w _;
print "Executable
" if -x _;
print "Setuid
" if -u _;
print "Setgid
" if -g _;print "Sticky
" if -k _;
print "Text
" if -T _;
print "Binary
" if -B _;

File ages for -M, -A, and -C are returned in days (including fractional days) since the script started running. This time is stored in the special variable $^T ($BASETIME). Thus, if the file changed after the script started, you would get a negative time. Note that most time values (86,399 out of 86,400, on average) are fractional, so testing for equality with an integer without using the int function is usually futile. Examples:

next unless -M $file > .5;      # files are older than 12 hours
&newfile if -M $file < 0;       # file is newer than process
&mailwarning if int(-A) == 90;  # file ($_) was accessed 90 days ago today

To reset the script's start time to the current time, say this:

$^T = time;


[3] You may, however, override the built-in semantics with the use filetest pragma. See Glossary.

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

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