3.10. Named Unary and File Test OperatorsSome of the "functions" described in Chapter 29, "Functions" are really unary operators. Table 3-2 lists all the named unary operators. Table 3.2. Named Unary Operators
Unary operators have a higher precedence than some of the binary operators. For example: 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:(sleep 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.print (4 | 3); 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
So a typical boo-boo is: 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; 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.next if length() < 80; next if (length) < 80; next if 80 > length; next unless length >= 80; Table 3.4. File Test Operators
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 (\0) 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: 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.)next unless -f $file && -T $file; Here are a couple of examples: print "Can do.\n" if -r $a || -w _ || -x _; stat($filename); print "Readable\n" if -r _; print "Writable\n" if -w _; print "Executable\n" if -x _; print "Setuid\n" if -u _; print "Setgid\n" if -g _; print "Sticky\n" if -k _; print "Text\n" if -T _; print "Binary\n" 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: To reset the script's start time to the current time, say this: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 $^T = time; Copyright © 2002 O'Reilly & Associates. All rights reserved. |
|