ЭЛЕКТРОННАЯ БИБЛИОТЕКА КОАПП
Сборники Художественной, Технической, Справочной, Английской, Нормативной, Исторической, и др. литературы.



10.5 The -x File Tests

Now you know how to open a filehandle for output, overwriting any existing file with the same name. Suppose you wanted to make sure that there wasn't a file by that name (to keep you from accidentally blowing away your spreadsheet data or that important birthday calendar). If you were writing a shell script, you'd use something like -e filename to test if the file exists. Similarly, Perl uses -e $filevar to test for the existence of the file named by the scalar value in $filevar. If this file exists, the result is true; otherwise it is false.[4] For example:

$name = "index.html";
if (-e $name) {
    print "I see you already have a file named $name\n";
} else {
    print "Perhaps you'd like to make a file called $name\n";
}

[4] This isn't good enough if you are managing lock files, or if files are appearing and disappearing quickly. In that case, you need to look into the sysopen and flock functions described in Programming Perl or see the examples in Chapter 19, CGI Programming.

The operand of the -e operator is really just any scalar expression that evaluates to some string, including a string literal. Here's an example that checks to see whether both index.html and index.cgi exist in the current directory:

if (-e "index.html" && -e "index.cgi") {
    print "You have both styles of index files here.\n";
}

Other operators are defined as well. For example, -r $filevar returns true if the file named in $filevar exists and is readable. Similarly, -w $filevar tests whether it is writable. Here's an example that tests a user-specified filename for both readability and writability:

print "where? ";
$filename = <STDIN>;
chomp $filename; # toss pesky newline
if (-r $filename && -w $filename) {
    # file exists, and I can read and write it
    ...
}

Many more file tests are available. Table 10.1 gives the complete list.


Table 10.1: File Tests and Their Meanings

File Test

Meaning

-r

File or directory is readable

-w

File or directory is writable

-x

File or directory is executable

-o

File or directory is owned by user

-R

File or directory is readable by real user, not effective user (differs from -r for setuid programs)

-W

File or directory is writable by real user, not effective user (differs from -w for setuid programs)

-X

File or directory is executable by real user, not effective user (differs from -x for setuid programs)

-O

File or directory is owned by real user, not effective user (differs from -o for setuid programs)

-e

File or directory exists

-z

File exists and has zero size (directories are never empty)

-s

File or directory exists and has nonzero size (the value is the size in bytes)

-f

Entry is a plain file

-d

Entry is a directory

-l

Entry is a symlink

-S

Entry is a socket

-p

Entry is a named pipe (a "fifo")

-b

Entry is a block-special file (like a mountable disk)

-c

Entry is a character-special file (like an I/O device)

-u

File or directory is setuid

-g

File or directory is setgid

-k

File or directory has the sticky bit set

-t

isatty() on the filehandle is true

-T

File is "text"

-B

File is "binary"

-M

Modification age in days

-A

Access age in days

-C

Inode-modification age in days

Most of these tests return a simple true-false condition. A few don't, so let's talk about them.

The -s operator does return true if the file is nonempty, but it's a particular kind of true. It's the length in bytes of the file, which evaluates as true for a nonzero number.

The age operators -M, -A, and -C (yes, they're uppercase) return the number of days since the file was last modified, accessed, or had its inode changed.[5] (The inode contains all of the information about the file except for its contents: see the stat system call manpage for details.) This age value is fractional with a resolution of one second: 36 hours is returned as 1.5 days. If you compare the age with a whole number (say three), you'll get only the files that were changed exactly that many days ago, not one second more or less. This means you'll probably want a range comparison[6] rather than an exact comparison to get files that are between three and four days old.

[5] The age is measured relative to the time the program started, as captured in system time format in the $^T variable. It's possible to get negative numbers for these ages if the queried value refers to an event that happened after the program began.

[6] Or the int operator.

These operators can operate on filehandles as well as filenames. Giving a filehandle for the operand is all it takes. So to test whether the file opened as SOMEFILE is executable, you can use:

if (-x SOMEFILE) {
    # file open on SOMEFILE is executable
}

If you leave the filename or filehandle parameter off (that is, you have just -r or -s), the default operand is the file named in the $_ variable (there it is again!). So, to test a list of filenames to see which ones are readable, it's as simple as this:

foreach (@some_list_of_filenames) {
    print "$_ is readable\n" if -r; # same as -r $_
}