File tests and metadata
| The file test operators | |
| Size and emptiness | |
| stat | |
| How old |
The file test operators
A minus sign followed by a letter asks a question about a file and returns true or false. The letters are short but worth memorizing: they replace a whole library in other languages.
my $path = "/etc/hosts";
print "exists\n" if -e $path;
print "regular\n" if -f $path;
print "readable\n" if -r $path;
print "executable\n" if -x $path;
perl demo.pl
exists
regular
readable
The full set covers directories with -d, symbolic links with -l, sockets with -S and ownership with -o. Use -e first when the answer decides whether a later test even makes sense.
Size and emptiness
Test -s returns the size in bytes, which is true for any non-empty file, and -z is true for an empty one. Both are handy before reading a file that a previous run should have filled.
my $path = "/etc/hosts";
my $size = -s $path;
print "$size bytes\n" if $size;
print "empty\n" if -z $path;
perl demo.pl
172 bytes
A missing file makes -s return undef and raise no warning, so treat a false result as missing or empty and check -e when the difference matters. The count itself is the size of your own /etc/hosts, so it changes from machine to machine.
stat
stat returns a thirteen-element list with everything the filesystem knows: device, inode, mode, link count, owner, group, size, and three timestamps. Named fields are easier to read than positions.
my $path = "/etc/hosts";
my @st = stat $path;
print "size: $st[7]\n";
print "mode: ", sprintf("%04o\n", $st[2] & 07777);
perl demo.pl
size: 172
mode: 0644
The mode is printed in octal with the leading zero, the way chmod reads it. Field 7 is the size in bytes, the same number -s returns.
The File::stat module returns an object with named accessors instead: (stat $path)->size. Use it when the offsets look like line noise, which they will after a month away from the code.
How old
The -M operator returns the file age in days, measured from the start of the program. Together with -s it builds the classic cleanup filter.
The demo asks about a log file, so create a fresh app.log first:
printf 'INFO start\nERROR boom\n' > app.log
my $path = "app.log";
print "old log\n" if -M $path > 7;
printf "%s is %.1f days old\n", $path, -M $path;
perl demo.pl
app.log is 0.0 days old
The seven-day test stays quiet, because the file was written a moment ago and its age is a fraction of a day. A log last touched two weeks back would print old log and report 14.0 days, which is exactly the filter a cleanup script needs.
The related tests are -A for last access and -C for inode change time. All three are cheap, because the information comes from the same stat call the kernel already made.
Next: Directories and paths
Article author: Arthur Isaev