Issue with Perl File::stat output, need to show date - perl

I am having an issue with getting File::stat to output the last modified date of the file. This is my code so far:
#!/usr/bin/perl
use Time::localtime;
use File::stat;
use warnings;
use File::Find;
my $dirloc = 'E:\tmp\testdir';
sub find_txt {
my $F = $File::Find::name;
if ( ! -d $F && $F =~ /.tar|.exe|.zip/ ) {
my #result = $F;
foreach my $result (#result){
my $timestamp;
$timestamp = (stat("$result"))->[9] or die "No $_: $!";
print "$result : $timestamp\n";
}
}
}
find({wanted => \&find_txt}, $dirloc);
It is outputing something like this:
C:/tmp/testdir/foo/bar/test.tar : 1415305933
I need it to output instead (date format doesn't have to be what is listed, i just want to see the date):
C:/tmp/testdir/foo/bar/test.tar : 11/07/2014
I know that the output it is giving me is the time since epoch but I thought stat was supposed to give the date. Am I doing something wrong? Thanks!
edit: I have tried localtime, and i get: Time::tm=ARRAY(0x245b220), not sure what is happening there

You can use the localtime (Note: not Time::localtime) function to convert the timestamp into something useful
my $date = localtime $timestamp
which will make it a human readable string like Fri Nov 7 15:33:00 2014
Or you can use it in a list context to spit it into individual fields:
my($sec, $min, $hour, $day, $month, $year, $weekday, $yearOfDay, $isDST) = localtime $timestamp

Related

Perl Get File Last Modified Date Time No Module

I'm creating a script where I need to get the Last Modified Date of the files
I checked this thread How do I get a file's last modified time in Perl?
So I used the script below to get the last modified, at first it was working but when I try to run it again, the timestamp returns 00:00 January 1, 1970.
Why is that happening and how can I get the correct last modified date and time?
my $dir = '/tmp';
opendir(DIR, $dir) or die $!;
#content=readdir(DIR);
foreach(#content)
{
next unless ($_ =~ m/\bfile.txt|file2.csv\b/);
my $epoch_timestamp = (stat($_))[9];
my $timestamp = localtime($epoch_timestamp);
$f_detail = $_ .' '.$timestamp;
print "$f_detail\n";
}
closedir(DIR);
exit 0;
When I tried to run the perl, I will get this result
file.txt Thu Jan 1 00:00:00 1970
file2.csv Thu Jan 1 00:00:00 1970
Ok, last update, it is working now, I try to run all of the scripts you've given to me, standalone script. I found what's causing the default time, see the script below, I remove that in my program and it works, didn't notice this at first, sorry. But still, it feels weird because I was sure that it is working when I first run it, but now it is working so yeah thank you guys!
if (($month = ((localtime)[4] + 1)) < 10)
{
$month = '0' . $month;
}
if (($day = ((localtime)[3])) < 10)
{
$day = '0' . $day;
}
if (($year = ((localtime)[5]+1900)) >= 2000)
{
if (($year = $year - 2000) < 10)
{
$year = '0' . $year;
}
}
else
{
$year = $year - 1900;
}
$date = $month . $day . $year;
readdir returns file names without the full path. You need to prepend the path manually:
for (#content) {
next unless /^(?:file\.txt|file2\.csv)\z/;
my $epoch_timestamp = (stat("$dir/$_"))[9];
# ~~~~~~~~~
Also note how I changed the regex to match the file names.
If you have a directory name, and you want to see if some files whose names you already know exist in that directory, there's really no need for opendir/readdir - that's more helpful if you don't know the filenames ahead of time. When you do, you can just build a path using both parts and use file test operators/stat/etc. on it.
#!/usr/bin/env perl
use strict;
use warnings;
use feature qw/say/;
my $dir = '/tmp';
my #files = qw/file.txt file2.csv/;
for my $file (#files) {
# Better to use File::Spec->catfile($dir, $file), but your question
# title said no modules...
my $name = "$dir/$file";
if (-e $name) { # Does the file exist?
# _ to re-use the results of the above file test operator's stat call
my $epoch_timestamp = (stat _)[9];
my $timestamp = localtime $epoch_timestamp;
say "$file $timestamp";
}
}
Example execution:
$ perl demo.pl
file.txt Tue Feb 8 07:26:07 2022
file2.csv Tue Feb 8 07:26:10 2022
Following demo code utilizes glob to obtain modification time for specified files in a directory.
use strict;
use warnings;
use feature 'say';
my $dir = '/tmp';
my #files = qw(file.txt file2.csv);
my $mask = join ' ', map { "$dir/$_" } #files;
say "$_\t" . localtime((stat($_))[9]) for glob($mask);

Perl Date Comparison Query

I'm trying to output "not ok" if the date provided within an input file is greater than one day from "today" using Perl version 5.8.8.
Initializing with "./code.sh < test.txt" works fine, when test.txt contains the following data:
07/02/2020 08/02/2020
When I rehash the code below to try an use "today's date" as a variable, and only have one date within the input file I get the following error:
Use of uninitialized value in concatenation (.) or string at code.sh line 27, <> line 1
Working code (with two dates in the input file):
#!/usr/bin/perl
use strict;
use warnings;
use Time::Piece;
#my $date = localtime->strftime('%d/%m/%Y');
#print "$date";
my $format = '%d/%m/%Y';
while (<>) {
chomp;
my ($str1, $str2) = split;
# my ($date, $str2) = split;
# my $dt1 = Time::Piece->strptime($date, $format);
my $dt1 = Time::Piece->strptime($str1, $format);
my $dt2 = Time::Piece->strptime($str2, $format);
# print "$date / $str2 ";
print "$str1 / $str2 ";
if ($dt2->julian_day - $dt1->julian_day ==1) {
print "ok\n";
} else {
print "not ok\n";
}
}
Broken code (with one date within the input file):
#!/usr/bin/perl
use strict;
use warnings;
use Time::Piece;
my $date = localtime->strftime('%d/%m/%Y');
print "$date";
my $format = '%d/%m/%Y';
while (<>) {
chomp;
# my ($str1, $str2) = split;
my ($date, $str2) = split;
my $dt1 = Time::Piece->strptime($date, $format);
# my $dt1 = Time::Piece->strptime($str1, $format);
my $dt2 = Time::Piece->strptime($str2, $format);
print "$date / $str2 ";
# print "$str1 / $str2 ";
if ($dt2->julian_day - $dt1->julian_day ==1) {
print "ok\n";
} else {
print "not ok\n";
}
}
Not quite sure what I'm doing wrong...
Any help is appreciated
Please pay more attention when you type your code, your sample had a few miss-types
#!/usr/bin/perl
#
# vim: ai:ts=4:sw=4
#
use strict;
use warnings;
use feature 'say';
use Time::Piece;
my $format = '%d/%m/%Y';
my $date = localtime->strftime($format);
say "Today is: $date\n";
my #str_dates = <DATA>;
chomp(#str_dates);
my $date1 = Time::Piece->strptime($str_dates[0], $format);
my $date2 = Time::Piece->strptime($str_dates[1], $format);
my $days_diff = $date2->julian_day - $date1->julian_day;
my $msg = ($days_diff == 1) ? "ok" :"not ok";
say "$date2 :: $date1 => $msg";
say "$date2 :: $date1 = $days_diff day(s) apart";
__DATA__
07/02/2020
08/02/2020
Output
Today is: 07/02/2020
Sat Feb 8 00:00:00 2020 :: Fri Feb 7 00:00:00 2020 => ok
Sat Feb 8 00:00:00 2020 :: Fri Feb 7 00:00:00 2020 = 1 day(s) apart
NOTE: I would recommend if you use:
Windows OS
notepad++
Linux OS vim
vim better use with options: syntax on, ai,ts=4,sw=4

Perl setting value of a variable in strict -T mode errors

I have a script running strict with the -T parameter or 'taint' mode. If I try to set the value of a variable already defined, it craps out. I didn't make much of it, I'm just trying to modify it and don't know why it's breaking. Here's the gist of what it looks like:
#!/usr/bin/perl -T
use 5.010;
use CGI;
use CGI::Carp qw/fatalsToBrowser/;
use strict;
use warnings;
use localtime;
my #months = qw(january february march april may june july august september october november december);
my $q = CGI->new();
say $q->header(), $q->start_html(-title=>'Calendar');
for my $param ($q->param()) {
my $safe_param = $q->escapeHTML($param);
for my $value ($q->param($param)) {
my $params = $q->escapeHTML($value);
{
local $ENV{"PATH"} = "/bin:/usr/local/bin:/usr/bin";
local $ENV{"BASH_ENV"}="";
my $date = "";
my $white = /^\s*$/;
my $singleyear = /^\d{2,4}$/;
my $nummonth = /^\d{1,2}\s\d{1,4}$/;
# If $params is empty or all white space, set $date to a single space
if ($params =~ $white) {
my($day, $month, $year)=(localtime)[3,4,5];
my $monthname = $months[$month];
$date = "$monthname $year"
}
# If $params is only a single 1-4 digit year, set $date to year
if ($params =~ $singleyear) {
$date = $params;
}
# If $params is a 1-2 digit month and a 1-4 digit year set $date to month and year
if ($params =~ $nummonth) {
my $monthnumber = $params =~ /^\d{1,2}/;
my $monthstring = $months[$monthnumber];
my $yearnumber = $params =~ /(\d{1,4})$/;
$date = "$monthstring $yearnumber";
}
if ($date eq "") {
say "<h1>Invalid Parameters: $params</h1>";
} else {
say "<h1>Parameters: $params</h1>";
}
my $cmds = "cal -h " . $date;
my #lines = `$cmds`;
say ("<pre>");
for my $line (#lines) {
print ("$line");
}
say ("</pre>");
}
}
say '</p>';
}
say $q->end_html();
At any point I set the value of $date it throws the error:
Insecure dependency in `` while running with -T switch
if I set my $date it throws no error, but doesn't actually update the value of date.
Also, I'm pretty sure all my regex is wrong, because I did some testing, and all of those cases evaluate to true for some reason, regardless of the user input. But that's not really the problem I'm facing now.
Example user input for $params
02 1999
$date = $params;
Such code assigns the tainted $params value to $date without untainting it.
Instead: Match the exact data you want with a regex, and use regex captures to extract an untainted value. For example:
if ($params =~ /^(\d{2,4})$/) {
$date = $1;
}
And so on for the other cases.
Note that assigning a regex $foo = /bar/ does not assign a regex object, but assigns the result of matching that regex against the $_ variable! If you need to create regex objects, use the qr/.../ operator. However, using regex objects seems unnecessary in your program – just write down the regex in each condition in order to avoid problems.

Saving output of a system call in perl to a text file

Im trying to make this script put the output files in results/ but i only get them in results/spool. How can I make them end up in the right place?
#!/usr/bin/perl -w
use POSIX;
my $date = strftime "%Y%m%d_%H%M.txt", localtime;
my #files = <spool/*.*>;
foreach $file (#files) {
my $date_file = $file;
system("$^X wfreq.pl $file >> results/${date_file}_$date");
unlink("$file");
}
Have you tested this ?
#!/usr/bin/perl -w
use POSIX;
my $date = strftime "%Y%m%d_%H%M.txt", localtime;
my #files = <spool/*.*>;
foreach $file (#files) {
(my $date_file = $file) =~ s!.*/!!;
system("$^X wfreq.pl $file >> results/${date_file}_$date");
unlink("$file");
}
The functions system(), qx() & exec() can handle variables without any problems.
Is this what your looking for?
This will give you a separate file for each date
#files = <spool/*.*>;
foreach $file (#files) {
system($^X, "wfreq.pl", $file);
$DATE = strftime "%Y%m%d";
open OUTPUT, '>results/$DATE.txt' or die;
print OUTPUT $DATE;
}
Or for a file with results all in one text file
#files = <spool/*.*>;
open OUTPUT, '>>results/results.txt' or die;
foreach $file (#files) {
system($^X, "wfreq.pl", $file);
$DATE = strftime "%Y%m%d";
print OUTPUT $DATE;
}

parse timestamp with millisecond in Perl

Assuming I have a bunch of timestamps like "11/05/2010 16:27:26.003", how do parse them with millisecond in Perl.
Essentially, I would like to compare the timestamp to see if they are before or after a specific time.
I tried using Time::Local, but it seems that Time::Local is only capable to parse up second. And Time::HiRes, on the other hand, isn't really made for parsing text.
Thanks,
Derek
use DateTime::Format::Strptime;
my $Strp = new DateTime::Format::Strptime(
pattern => '%m/%d/%Y %H:%M:%S.%3N',
time_zone => '-0800',
);
my $now = DateTime->now;
my $dt = $Strp->parse_datetime('11/05/2010 23:16:42.003');
my $delta = $now - $dt;
print DateTime->compare( $now, $dt );
print $delta->millisecond;
You can use Time::Local and just add the .003 to it:
#!/usr/bin/perl
use strict;
use warnings;
use Time::Local;
my $timestring = "11/05/2010 16:27:26.003";
my ($mon, $d, $y, $h, $min, $s, $fraction) =
$timestring =~ m{(..)/(..)/(....) (..):(..):(..)([.]...)};
$y -= 1900;
$mon--;
my $seconds = timelocal($s, $min, $h, $d, $mon, $y) + $fraction;
print "seconds: $seconds\n";
print "milliseconds: ", $seconds * 1_000, "\n";