How do I get a file's last modified time in Perl? - perl

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?

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);

Use the builtin stat function. Or more specifically:
my $modtime = (stat($fh))[9]

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.

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.

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.

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

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];

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";

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.

Related

Zsh completion caching policy explained

I'm writting some zsh functions using the powerful completion feature. The computation of my completions take some times and I want to make use of the completion caching policy. From the zsh manual (https://zsh.sourceforge.io/Doc/Release/Completion-System.html) I found this code snippet
example_caching_policy () {
# rebuild if cache is more than a week old
local -a oldp
oldp=( "$1"(Nm+7) )
(( $#oldp ))
}
I couldn't find any explanation on the (Nm+7) syntax, what does Nm means ? With try and error I could find out that for example Nms+1 would change the cache policy to 1 second, while Nmh+1 to 1 hour. But where can I find the general (NmX+N) construct explanation ?
Same what does exactly means the line (( $#oldp )) ?
I can explain the (Nm+7)
man zshexpn, search for Glob Qualifiers
a[Mwhms][-|+]n
files accessed exactly n days ago. Files accessed within the last n days are selected using a negative
value for n (-n). Files accessed more than n days ago are selected by a positive n value (+n). Op‐
tional unit specifiers `M', `w', `h', `m' or `s' (e.g. `ah5') cause the check to be performed with
months (of 30 days), weeks, hours, minutes or seconds instead of days, respectively. An explicit `d'
for days is also allowed.
Any fractional part of the difference between the access time and the current part in the appropriate
units is ignored in the comparison. For instance, `echo *(ah-5)' would echo files accessed within the
last five hours, while `echo *(ah+5)' would echo files accessed at least six hours ago, as times
strictly between five and six hours are treated as five hours.
m[Mwhms][-|+]n
like the file access qualifier, except that it uses the file modification time.
N stands for NULL_GLOB, if zsh matches nothing, it will remove the pattern.
Without this N option, if it matches nothing it will print an error.
Example with 4 files
$ touch lal # = updates file modification date to now
$ ls
lal lil lol tut
$ ls l*(m+7)
lil lol
# files older than 7 days starting with l
$ ls l*(m-7)
lal
# files younger than 7 days starting with l
$ ls l*(m+200)
zsh: no match
# no files older than 200 days
$ ls l*(Nm+200)
lal lil lol tut
# N = NULL_GLOB made disappear the non-matching pattern so it's just ls

why does printing $( gives a number series

When I started with variables to print a variable $foo in a string
I used print ${foo}s but when I used print "$(foo)s"
it gave me
1000 4 24 27 30 46 108 125 1000)s . When I printed $( or $) it gives me the sequence 1000 4 24 27 30 46 108 125 1000 . Also
$[ gave 5.014002 and $] gave 0 . What do they mean?
$( = The real gid of this process.
$[ = This variable stores the index of the first element in an array, and of the first character in a substring. The default is 0.
$) = The effective gid of this process.
$] = Perlversion
seen here: http://perldoc.perl.org/perlvar.html
See perldoc perlvar (in fact, see it any time you have a question about $ followed by a symbol).
$( and $) give the real and effective gids of the process.
You get a space separated list of gids if the system supports membership in multiple groups simultaneously.
$[ is the index considered to be the first element of an array.
$] is an old way to get the version of Perl being used to run the program.

Missing either data or entire lines on serial port read

I am trying to read streaming serial data at 115200b and cant seem to pick it all up.
using the input method ($data = $Port -> input), I get the first 14 or 15 characters of every line. I need the whole line.
using the read method ($data= $Port -> read(4096)) and by adjusting the read_interval I can either get partials of every line using
$Port->read_interval(1);
or fully every third line using
$Port->read_interval(2);
I need all of every line.
here is the code
my $App_Main_Port = Win32::SerialPort->start ($Test_cfgfile);
$App_Main_Port->read_interval(1);
$App_Main_Port->read_char_time(1);
for ($i=0;;) {
# $data = $App_Main_Port -> input;
$data= $App_Main_Port -> read(4096);
print "$data\n";
}
by adjusting the read interval I get the results mentioned above. I started with the default values of 100 in the interval and char_time parameters, but only get every third line.
Tx for any insight!
Chris
Serial port reads often just return what's buffered in the serial port at that moment. A 16550 UART only buffers 16 bytes total, and that's what most PCs emulate.
From the Win32::SerialPort page, it appears you need to call ->read() in a list context, so you can find out how many bytes actually got read. If you really do want to block until you've received all 4K characters, try a loop like this:
# read in 4K bytes
my ($data, $temp, $count_in, $total_in);
$total_in = 0;
while ($total_in < 4096) {
( $count_in, $temp ) = $App_Main_Port->read( 4096 - $total_in );
$data .= $temp;
$total_in += $count_in;
}
That said, your real problem sounds like it could be a flow control issue. Even with such a loop, you might still get dropped characters if you don't have proper flow control. You might experiment with handshake("dtr") or handshake("rts").

Perl Strange -M Flag in 'If' statement

What is this flag?
if (-M ..filepath..)
what is the '-M' flag?
perldoc -f -M will answer your question...
This is the modification "age" of the file, in fractional days. That is, it is the number of days since the file was modified, as of the script start time (or as of some other time, if you explicitly set the $^T variable).
I sure hope that the actual code is along the lines of -M filepath > ...; just testing -M's result for truth is pointless.
Script start time minus file modification time (aka file modification age), in days.
In other words, it returns the age of OPERAND in days when the program started.
Also see a full list of file test operators in perldoc perlfunc (-X section)
Modification age (measured in days)
from http://www.devshed.com/c/a/Perl/File-Tests-in-Perl/
if we have something like this:
$age = -M FILE;
$age will contain the days since the file was modified.

Perl: Use of uninitialized value in numeric lt (<) at /Date/Manip.pm

This has me puzzled. This code worked on another server, but it's failing on Perl v5.8.8 with Date::Manip loaded from CPAN today.
Warning:
Use of uninitialized value in numeric lt (<) at /home/downside/lib/Date/Manip.pm line 3327.
at dailyupdate.pl line 13
main::__ANON__('Use of uninitialized value in numeric lt (<) at
/home/downsid...') called at
/home/downside/lib/Date/Manip.pm line 3327
Date::Manip::Date_SecsSince1970GMT(09, 16, 2008, 00, 21, 22) called at
/home/downside/lib/Date/Manip.pm line 1905
Date::Manip::UnixDate('today', '%Y-%m-%d') called at
TICKER/SYMBOLS/updatesymbols.pm line 122
TICKER::SYMBOLS::updatesymbols::getdate() called at
TICKER/SYMBOLS/updatesymbols.pm line 439
TICKER::SYMBOLS::updatesymbols::updatesymbol('DBI::db=HASH(0x87fcc34)',
'TICKER::SYMBOLS::symbol=HASH(0x8a43540)') called at
TICKER/SYMBOLS/updatesymbols.pm line 565
TICKER::SYMBOLS::updatesymbols::updatesymbols('DBI::db=HASH(0x87fcc34)', 1, 0, -1) called at
dailyupdate.pl line 149
EDGAR::updatesymbols('DBI::db=HASH(0x87fcc34)', 1, 0, -1) called at
dailyupdate.pl line 180
EDGAR::dailyupdate() called at dailyupdate.pl line 193
The code that's failing is simply:
sub getdate()
{ my $err; ## today
&Date::Manip::Date_Init('TZ=EST5EDT');
my $today = Date::Manip::UnixDate('today','%Y-%m-%d'); ## today's date
####print "Today is ",$today,"\n"; ## ***TEMP***
return($today);
}
That's right; Date::Manip is failing for "today".
The line in Date::Manip that is failing is:
my($tz)=$Cnf{"ConvTZ"};
$tz=$Cnf{"TZ"} if (! $tz);
$tz=$Zone{"n2o"}{lc($tz)} if ($tz !~ /^[+-]\d{4}$/);
my($tzs)=1;
$tzs=-1 if ($tz<0); ### ERROR OCCURS HERE
So Date::Manip is assuming that $Cnf has been initialized with elements "ConvTZ" or "TZ". Those are initialized in Date_Init, so that should have been taken care of.
It's only failing in my large program. If I just extract "getdate()" above
and run it standalone, there's no error. So there's something about the
global environment that affects this.
This seems to be a known, but not understood problem. If you search Google for
"Use of uninitialized value date manip" there are about 2400 hits.
This error has been reported with MythTV and grepmail.
It is a bug in Date::Manip version 5.48-5.54 for Win32. I've had difficulty with using standard/daylight variants of a timezones, e.g. 'EST5EDT', 'US/Eastern'. The only timezones that appear to work are those without daylight savings, e.g. 'EST'.
It is possible to turn off timezone conversion processing in the Date::Manip module:
Date::Manip::Date_Init("ConvTZ=IGNORE");
This will have undesired side-effects if you treat dates correctly. I would not use this workaround unless you are confident you will be never be processing dates from different timezones.
It's almost certain that your host doesn't have a definition for the timezone you're specifying, which is what's causing a value to be undefined.
Have you checked to make sure a TZ definition file of the same name actually exists on the host?
Date::Manip is supposed to be self-contained. It has a list of all its time zones in its own source, following "$zonesrfc=".
Can you try single-stepping through the debugger to see what exactly is going wrong? It could easily be %Zone that is wrong - %tz may be set correctly on line 1 or 2, but then the lookup on line 3 fails, ending up with undef.
Edit: %Date::Manip::Cnf and %Date::Manip::Zone are global variables, so you should be able to take a dump of them before and after the call to Date::Manip::Date_Init. If I read the source correctly %Cnf should contain a basic skeleton of configuration options before the call to Date_Init, and %Zone should be empty; after Date_Init, TZ should have your chosen value, and %Zone should be populated by a lookup table of time zones.
I see a reference to .DateManip.cnf in %Cnf, which might be something to look at - is it possible that you have such a file in your home directory, or the current working directory, which is overriding the default settings?