[Chapter 10] 10.5 The -x File Tests
Author: Randal Schwartz, Tom Christiansen & Larry Wall
Date: 1999-07-02T01:34:00Z

#

| Chapter 10 Filehandles and File Tests |

## 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 $_
    }

10.4 Using Filehandles |  | 10.6 The stat and lstat Functions