72

Suppose I have a filehandle $fh. I can check its existence with -e $fh or its file size with -s $fh or a slew of additional information about the file. How can I get its last modified time stamp?

cowgod
  • 8,626
  • 5
  • 40
  • 57

10 Answers10

111

Calling the built-in function stat($fh) returns an array with the following information about the file handle passed in (from the perlfunc man page for stat):

  0 dev      device number of filesystem
  1 ino      inode number
  2 mode     file mode  (type and permissions)
  3 nlink    number of (hard) links to the file
  4 uid      numeric user ID of file's owner
  5 gid      numeric group ID of file's owner
  6 rdev     the device identifier (special files only)
  7 size     total size of file, in bytes
  8 atime    last access time since the epoch
  9 mtime    last modify time since the epoch
 10 ctime    inode change time (NOT creation time!) since the epoch
 11 blksize  preferred block size for file system I/O
 12 blocks   actual number of blocks allocated

Element number 9 in this array will give you the last modified time since the epoch (00:00 January 1, 1970 GMT). From that you can determine the local time:

my $epoch_timestamp = (stat($fh))[9];
my $timestamp       = localtime($epoch_timestamp);

Alternatively, you can use the built-in module File::stat (included as of Perl 5.004) for a more object-oriented interface.

And to avoid the magic number 9 needed in the previous example, additionally use Time::localtime, another built-in module (also included as of Perl 5.004). Together these lead to some (arguably) more legible code:

use File::stat;
use Time::localtime;
my $timestamp = ctime(stat($fh)->mtime);
user7761803
  • 209
  • 2
  • 9
cowgod
  • 8,626
  • 5
  • 40
  • 57
  • Is `localtime` supposed to correct for timezone changes? Ie: file created in PST, then read `mtime` from CET. I am getting confusing results. – Jonathan Cross Jan 09 '17 at 00:47
  • 1
    Mentioning the use of `File::stat` is misleading, as this will provide an object oriented way of accessing the stat information, and accessing array elements directly won't work. There is no need to use `File::stat` to access e.g. `(stat($fh))[9]` – Santrix Nov 21 '19 at 09:05
26

Use the builtin stat function. Or more specifically:

my $modtime = (stat($fh))[9]
Michael Carman
  • 30,628
  • 10
  • 74
  • 122
19
my @array = stat($filehandle);

The modification time is stored in Unix format in $array[9].

Or explicitly:

my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size,
    $atime, $mtime, $ctime, $blksize, $blocks) = stat($filepath);

  0 dev      Device number of filesystem
  1 ino      inode number
  2 mode     File mode  (type and permissions)
  3 nlink    Number of (hard) links to the file
  4 uid      Numeric user ID of file's owner
  5 gid      Numeric group ID of file's owner
  6 rdev     The device identifier (special files only)
  7 size     Total size of file, in bytes
  8 atime    Last access time in seconds since the epoch
  9 mtime    Last modify time in seconds since the epoch
 10 ctime    inode change time in seconds since the epoch
 11 blksize  Preferred block size for file system I/O
 12 blocks   Actual number of blocks allocated

The epoch was at 00:00 January 1, 1970 GMT.

More information is in stat.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
David Segonds
  • 83,345
  • 10
  • 45
  • 66
16

You need the stat call, and the file name:

my $last_mod_time = (stat ($file))[9];

Perl also has a different version:

my $last_mod_time = -M $file;

but that value is relative to when the program started. This is useful for things like sorting, but you probably want the first version.

Paul Beckingham
  • 14,495
  • 5
  • 33
  • 67
10

If you're just comparing two files to see which is newer then -C should work:

if (-C "file1.txt" > -C "file2.txt") {
{
    /* Update */
}

There's also -M, but I don't think it's what you want. Luckily, it's almost impossible to search for documentation on these file operators via Google.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • 2
    To google `-M`, add the quotes "-M", since `-X` removes results having `X`... By the way, '-M' is what the OP wants. – Déjà vu May 04 '12 at 12:38
  • 1
    It would be more natural to use "<" (to test if "file1.txt" is the newer one - as ***"-C" returns the age of the file, not the (creation) date)***. – Peter Mortensen Jan 08 '14 at 15:59
  • 1
    Explanation of the difference between -M (content modification) and -C (inode change): https://unix.stackexchange.com/questions/132660/whats-the-difference-between-modification-date-and-inodes-modification-date – David L. Nov 12 '17 at 20:05
3

You could use stat() or the File::Stat module.

perldoc -f stat
Chris Kloberdanz
  • 4,436
  • 4
  • 30
  • 31
3

I think you're looking for the stat function (perldoc -f stat)

In particular, the item found at index 9 of the returned list (i.e., the 10th field) is the last modify time of the file, in seconds since the epoch.

So:

my $last_modified = (stat($fh))[9];

bobbogo
  • 14,989
  • 3
  • 48
  • 57
Lee
  • 282
  • 2
  • 6
2

On my FreeBSD system, stat just returns a bless.

$VAR1 = bless( [
                 102,
                 8,
                 33188,
                 1,
                 0,
                 0,
                 661,
                 276,
                 1372816636,
                 1372755222,
                 1372755233,
                 32768,
                 8
               ], 'File::stat' );

You need to extract mtime like this:

my @ABC = (stat($my_file));

print "-----------$ABC['File::stat'][9] ------------------------\n";

or

print "-----------$ABC[0][9] ------------------------\n";
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
holly
  • 577
  • 1
  • 5
  • 12
1

This is very old thread, but I tried using the solution and could not get the information out of File::stat. (Perl 5.10.1)

I had to do the following:

my $f_stats = stat($fh);
my $timestamp_mod = localtime($f_stats->mtime);
print "MOD_TIME = $timestamp_mod \n";

Just thought I share in case anyone else had the same trouble.

ndmeiri
  • 4,979
  • 12
  • 37
  • 45
MBW
  • 73
  • 4
1

Another alternative is: $^T - (-M $fh)*3600*24

Given that the OP asked about the -X builtin functions I thought this might be along the lines of what he was looking for. I prefer it to stat() in some circumstances myself, tho I'm not sure I could defend that stylistic decision...

This uses the fact that Perl maintains the script start time in $^T (perldoc perlvar for more details), and that -M returns the delta of that vs the mod time of the file, as a floating point fraction of a day. It is cryptic but very perlish and fits nicely in situations where you don't have a file handle already (-X can operate on a file path or filehandle).

p.s. I used to have trouble finding details on the -X builtins, until realized you can find the details with perldoc -f -X in most perl-enabled command-line environments.

GargFunk
  • 11
  • 2
  • 1
    Remember that Stack Overflow isn't just intended to solve the immediate problem, but also to help future readers find solutions to similar problems, which requires understanding the underlying code. This is especially important for members of our community who are beginners, and not familiar with the syntax. Given that, **can you [edit] your answer to include an explanation of what you're doing** and why you believe it is the best approach? – Tyler2P Apr 25 '23 at 17:28
  • Thanks for the nudge! I was thinking of expounding on it when I posted, but figured it was an old question and nobody would care =) – GargFunk Apr 26 '23 at 18:41
  • Never know who it may help in the future. Most of stack overflows traffic come from people with the same issue. It is always important to ensure that answers provide enough detail that anyone, new to the language or not, can understand the solution. – Tyler2P Apr 26 '23 at 20:06