Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 6 years ago.
Improve this question
I have written a Perl subroutine that is supposed to take the name of a month and return a number if it matches, otherwise returning the original argument if not.
sub convert_month_to_number {
my ($input) = #_;
my #month_names = qw/January February March April May June July August September October November December/;
my #month_names_short = qw/Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec/;
for (my $i = 0; $i < scalar(#month_names); $i = $i + 1){
print "$month_names[$i]\n";
if ($input == $month_names[$i]{
return $i;
}
}
return $input;
}
}
However, when compiling this in Padre I receive the error:
syntax error at additionalscripts.pl line 16, near "}"
Global symbol "$input" requires explicit package name at additionalscripts.pl li
ne 19.
syntax error at additionalscripts.pl line 20, near "}"
Execution of additionalscripts.pl aborted due to compilation errors.
I'm not exactly sure why this is occurring, considering how I've already declared $input as a global variable up top. Any help would be greatly appreciated. I'm currently using Perl 5.010 on Windows.
You had a few errors in your code, which I've cleaned up.
my #test = qw(Foo January Bar);
say convert_month_to_number($_) foreach #test;
sub convert_month_to_number {
my ($input) = shift;
my #month_names = qw/January February March April May June July August September October November December/;
my #month_names_short = qw/Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec/;
for (my $i = 0; $i < #month_names; $i++){
if ($month_names[$i] eq $input){
return $i;
}
}
return $input;
}
Related
I found the below one-liner, that creates 50 text files
perl -MPath::Tiny -wE'
path("dir/s".$_.".txt")->spew("some data...$_\n") for 1..50
but it creates all the files with mtime as current time.
Is it possible to have the mtime between a date range?. like Jan 2016 to Dec 2018 for the text files?.
I understand that I have to use Time::Piece module, but I don't see Time::Piece module in my RHEL Perl which is 5.010.
There is the touch method in Path::Tiny, and the rest is about splitting up the desired range of dates. Note that the script below creates files (10 by default) with very simple-minded names, so please run in a brand new directory so to not overwrite something
perl -MPath::Tiny -MTime::Piece -wE'
$n = shift // 10;
($beg, $end) = map {
Time::Piece->strptime($_, "%Y%m%d")->epoch
} (20160101, 20181201);
$step = int ($end-$beg)/$n;
for (1..$n) {
$f = path("s$_.txt");
$f->spew("data..$_\n");
$f->touch($beg += $s)
' 5
Chaining calls to the desired path(..)->spew(..)->touch(..) doesn't work since spew returns true/false and not an object. (The touch does return the object and so it can have methods chained to it, but here it has to go after spew since spew changes the modification time.)
The above creates files
-rw-rw-r--. 1 xxx 8 Jul 31 2016 s1.txt
-rw-rw-r--. 1 xxx 8 Mar 1 2017 s2.txt
-rw-rw-r--. 1 xxx 8 Sep 30 2017 s3.txt
-rw-rw-r--. 1 xxx 8 May 1 2018 s4.txt
-rw-rw-r--. 1 xxx 8 Nov 30 2018 s5.txt
You'll probably want to fiddle with the step size and first/last time stamps.
Apparently there is a problem with Time::Piece, likely indicating a problem with that system† since the module has been in core since 5.9.5. Then I'd use Time::Local::timelocal
use Time::Local;
my $seconds_epoch = timelocal $sec, $min, $hour, $mday, $mon, $year;
where $mday goes from 1 while all other start at 0. Note that while it is traditional to provide the year subject to +1900 (so 116 for 2016) the normal years are accepted as well. So
$beg = timelocal 0, 0, 0, 1, 0, 2016;
$end = timelocal 0, 0, 0, 31, 11, 2018;
or, for more flexibility with dates
($beg, $end) = map {
($yr, $m, $d) = unpack "A4A2A2";
timelocal 0, 0, 0, $d, $m-1, $yr
} (20160101, 20181231)
† The problem of the missing Time::Piece is described in this post in Dave Cross's blog. Also see comments under the question and a chat.
Trying to understand a piece of code in Perl as follows
my #a = qw(A B C D E F F A K O N D);
my #b = qw(S F S T F F S);
$s=sprintf "%s %s %d %d:%d:%d %d: %s" , sub { ($a[$_[6]], $b[$_[4]], $_[3], $_[2], $_[1], $_[0], $_[5]+1900) }->(localtime), $s;
This code is in a method in a package which has been called with three arguments, arrays a and b have been defined in the method above in this code and s is a defined string.
All I can understand is that it is an anonymous subroutine which is expected to return multiple values due to localtime function but I just cannot understand the mechanism that this code follows. I assume I have given enough information to ask this question if not let me know.
The syntax is an unusual way of encapsulating the modification of several values at once inside of one statement.
This code is probably used at the end of a subroutine, as it returns a list of values. The real code might look like this.
sub foo {
my #a = qw(Sun Mon Tue Wed Thu Fri Sat);
my #b = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
my $s = q{foobar};
# wday month mday hour min sec year
return sub { ( $a[ $_[6] ], $b[ $_[4] ], $_[3], $_[2], $_[1], $_[0], $_[5] + 1900 ) }
->(localtime), $s;
}
Let's go through that. I added the return to make it more clear what's happening.
There is an anonymous subroutine sub { ... }, which is defined and directly being called sub { ... }->(). This is like a lambda function. The arrow operator -> in this form is used for calling a code reference. It differs from the direct object syntax (e.g. $foo->frobnicate()) in that there is no method name after the arrow.
my $code = sub { print "#_" };
$code->(1, 2, 3);
This will output:
1, 2, 3
The localtime return value in list context is passed. That's a bunch of values for the current time, as mentioned in the documentation.
# 0 1 2 3 4 5 6 7 8
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
localtime(time);
Without arguments, localtime implicitly uses time as its argument. Inside of the anonymous subroutine, those values end up in #_, which is the argument list of the sub. So $_[0] is the seconds part of the current local date.
Then there is #a and #b, which are really badly chosen names. If it's supposed to be short, I'd at least have called them #d and #m for weekdays and months.
The anonymous sub returns a list of values. It's the current weekday, as looked up in the #a array, where Sunday is index 0, the current month as looked in #b, the day, hour, minute and second, and the year as a four-digit number (hence the + 1900).
The overall function foo returns this list, and another value $s as the last element of its return value list.
If you were to print all of that, you would get:
print join q{ }, foo();
__END__
Mon Sep 25 16 10 33 2017 foobar
In the context of the sprintf, this becomes more clear. It's likely for adding a timestamp to a log entry.
sub log {
my $s = shift;
my #a = qw(Sun Mon Tue Wed Thu Fri Sat);
my #b = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
# 0 1 2 3 4 5 6 7
$s = sprintf "%s %s %d %d:%d:%d %d: %s",
# wday month mday hour min sec year
# 0 1 2 3 4 5 6
sub { ( $a[ $_[6] ], $b[ $_[4] ], $_[3], $_[2], $_[1], $_[0], $_[5] + 1900 ) }
# 7
->(localtime), $s;
return $s;
}
print &log('stuff happened'); # we need the & because CORE::log
I've marked each value with the position of the argument inside the sprintf pattern. $s seems to be the log message, which is replaced by the new string with the timestamp, and itself as the last argument.
However, this is a very unusual implementation, given that localtime will return the exact same thing in scalar context.
print &log("stuff happened\n");
print ''.localtime . ": stuff happened\n";
__END__
Mon Sep 25 16:24:40 2017: stuff happened
Mon Sep 25 16:24:40 2017: stuff happened
It's the return value of ctime(3). I first thought maybe it's a reimplementation because on the target system that kind of timestamp does not exist, but the localtime docs also say that this is not system-dependent.
The format of this scalar value is not locale-dependent but built into
Perl. For GMT instead of local time use the gmtime builtin. See also
the Time::Local module (for converting seconds, minutes, hours, and
such back to the integer value returned by time), and the POSIX
module's strftime and mktime functions.
The implementation with the sub is elegant, though not very Perl-ish. However, it's completely useless because Perl already brings this exact same format.
You could easily replace my log function with this.
sub log {
my $s = shift;
$s = localtime . " $s";
return $s;
}
But then again, if #a and #b are maybe localized, this makes makes sense. You give them as letter, which is probably not your real code, but I could see this being used as for example translating it to German like this.
use utf8;
sub log {
my $s = shift;
my #a = qw(So Mo Di Mi Do Fr Sa);
my #b = qw(Jan Feb Mär Apr Mai Jun Jul Aug Sep Okt Nov Dez);
# 0 1 2 3 4 5 6 7
$s = sprintf "%s %s %d %d:%d:%d %d: %s",
# wday month mday hour min sec year
# 0 1 2 3 4 5 6
sub { ( $a[ $_[6] ], $b[ $_[4] ], $_[3], $_[2], $_[1], $_[0], $_[5] + 1900 ) }
# 7
->(localtime), $s;
return $s;
}
print &log("stuff happened\n");
Now it makes a lot more sense.
how do i get the current year in 4 digit this is what i have tried
#!/usr/local/bin/perl
#months = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );
#days = qw(Sun Mon Tue Wed Thu Fri Sat Sun);
$year = $year+1900;
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
print "DBR_ $year\\$months[$mon]\\Failures_input\\Failures$mday$months[$mon].csv \n";
This prints DBR_ 114\Apr\Failures_input\Failures27Apr.csv
How do I get 2014?
I am using version 5.8.8 build 820.
use Time::Piece;
my $t = Time::Piece->new();
print $t->year;
Move the line:
$year = $year+1900;
To after that call to localtime() and to become:
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
$year = $year+1900;
The best way is to use the core library Time::Piece. It overrides localtime so that the result in scalar context is a Time::Piece object, you can use the many methods that the module supplies on it. (localtime in list context, as you have used it in your own code, continues to provide the same nine-element list.)
The strftime method allows you to format a date/time as you wish.
This very brief program produces the file path that I think you want (I doubt if there should be a space after DBR_?) Note that there is no need to double up backslashes inside a single-quoted string unless it is the last character of the string.
use strict
use warnings;
use Time::Piece;
my $path = localtime->strftime('DBR_%Y\%b\Failures_input\Failures%m%d.csv');
print $path;
output
DBR_2014\Apr\Failures_input\Failures27Apr.csv
One option to get the 4 digit year:
#!/usr/bin/perl
use POSIX qw(strftime);
$year = strftime "%Y", localtime;
printf("year %02d", $year);
You can also use
my ($y,$m,$d) = Date::Calc::Today();
$y variable will contain 2019
$m variable will contain 8
$d variable will contain 9
at the time of writing this answer ( 9th August 2019 )
The simplest way, I find, to get the year is:
my $this_year = (localtime)[5] + 1900;
I am trying to understand the logic of the following script specially in terms of storing content within the hash and time scan, also any suggestion on the improvement to make it more short.
#!perl
use strict;
use warnings;
my $A = 60; # minutes
my #mth = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
my #f = localtime();
my $TODAY = sprintf "%02d/%s/%4d",$f[3],$mth[$f[4]],$f[5]+1900;
my $START_MINUTE = $f[2]*60+$f[1] - $MAX_AGE;
##
my %users;
my %conn;
while (<DATA>) {
if( /\bAT\b/ ) {
my( $conn, $uid ) = /conn=(\d+).*uid=(.*?),/;
$conn{$conn} = $uid;
}
if( /ABB/ ) {
my ($timestamp, $conn) = /\[(.*?)\] conn=(\d+)/;
my ($date,$h,$m,undef) = split ':',$timestamp,4;
next unless ($date eq $TODAY);
my $minutes = $h*60 + $m;
if ($minutes >= $START_MINUTE){
my $uid = $conn{$conn};
++$users{$uid};
}
}
}
for my $uid (keys %users) {
my $count = $users{$uid};
print "$count\n" if $count > 6;
}
_DATA_
[04/Jun/2013:13:06:13 -0600] conn=13570 op=14 msgId=13 - AT dn="conn=ad1222,o=xyz.com" method=128 version=3
[04/Jun/2013:15:06:13 -0600] conn=13570 op=14 msgId=15 - RESULT ABB
There are two places where data is put in a hash
my( $conn, $uid ) = /conn=(\d+).*uid=(.*?),/;
$conn{$conn} = $uid;
}
This is straight forward, the regexp extracts the $uid and $conn and sets a hash entry with $conn as the key and $uid as the value. In this statement
$conn{$conn}
^^^^^^ ^ this is a hash
^^^^^ this is a completely different scalar
Overall the expression $conn{$conn} refers to a single element of the hash %conn with the scalar key $conn. There are two different variables here with basically the same name!
If you are looking for improvements, stylistically the hash should be called %uid as it's values are uids
if ($minutes >= $START_MINUTE){
my $uid = $conn{$conn};
++$users{$uid};
This is a bit more "that crazy perl" stuff, although really it is straight forward and is widely used in code. All it it does is increment the hash entry for the key $uid. If there is no entry for $user{$uid} already then the statement automatically makes it and sets the value to 1
update to discuss the "time scan"
my $A = 60; # minutes
my #mth = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
my #f = localtime();
my $TODAY = sprintf "%02d/%s/%4d",$f[3],$mth[$f[4]],$f[5]+1900;
my $START_MINUTE = $f[2]*60+$f[1] - $MAX_AGE;
This makes "$TODAY" which is the date today in a format that matches dates in the files and $START_MINUTE which is the number of minutes since midnight at the time the script is run
Later in the script the time of day is extracted and the minutes since midnight are found in a similar way (hour * 60 + minutes)
To "improve" this part of the script strftime could be used instead of the #mth array and the sprintf line
The calculations for the minutes could be moved to a sub called something like sub minutes_since_midnight
Bit difficult to say about improving the use of the hashes as it's not clear what they are used for out of the context of the program segment shown
Hope that more or less answers your question!!
any suggestion on the improvement to make it more short
You don't want to make it shorter to improve it. You need make it easier to understand in order to improve it. You already had problems understanding the logic, making it shorter isn't going to help there.
Let's take this:
my $A = 60; # minutes
my #mth = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
my #f = localtime();
my $TODAY = sprintf "%02d/%s/%4d",$f[3],$mth[$f[4]],$f[5]+1900;
my $START_MINUTE = $f[2]*60+$f[1] - $MAX_AGE;
I could go through the logic and attempt to figure it out, but more likely, I'll be making the same logical assumptions of the original author. Instead, we can improve readability by using good variable names, and by expanding the logic:
my #month_list = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
my ( $sec, $min, $hour, $month, $mday, $year ) = localtime; #Whoops!
my $full_year += 1900;
my $text_month = $month_list[$month];
my $today = sprintf "%02d/%s/%4d", $mday, $text_month, $full_year;
This is longer to type, but efficiency wise, it's just as efficient. Just because you can cram a bunch of operations on a single line doesn't make it faster to execute. However, mine is much easier to read and easier to maintain which will save you many hours of work. For example, my parsing of localtime is taken directly from the Perldoc on localtime. If you find an issue, and you think it could be due to my parsing of localtime, you could quickly compare my code to the Perldoc.
In fact, there is an error. Take a look of the localtime documentation and compare it to what I have, and you'll see I have $month and $mday mixed up.
Even better would be to use Time::Piece. In fact, Time::Piece would have made parsing the timestamp much cleaner too.
So, please understand that shorter code isn't better if it's just harder to understand, and it is usually not any more efficient in execution.
I need to read the system clock (time and date) and display it in a human-readable format in Perl.
Currently, I'm using the following method (which I found here):
#!/usr/local/bin/perl
#months = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
#weekDays = qw(Sun Mon Tue Wed Thu Fri Sat Sun);
($second, $minute, $hour, $dayOfMonth, $month, $yearOffset, $dayOfWeek, $dayOfYear, $daylightSavings) = localtime();
$year = 1900 + $yearOffset;
$theTime = "$hour:$minute:$second, $weekDays[$dayOfWeek] $months[$month] $dayOfMonth, $year";
print $theTime;
When you run the program, you should see a much more readable date and time like this:
9:14:42, Wed Dec 28, 2005
This seems like it's more for illustration than for actual production code. Is there a more canonical way?
Use localtime function:
In scalar context, localtime() returns
the ctime(3) value:
$now_string = localtime; # e.g., "Thu Oct 13 04:54:34 1994"
You can use localtime to get the time and the POSIX module's strftime to format it.
While it'd be nice to use Date::Format's and its strftime because it uses less overhead, the POSIX module is distributed with Perl, and is thus pretty much guaranteed to be on a given system.
use POSIX;
print POSIX::strftime( "%A, %B %d, %Y", localtime());
# Should print something like Wednesday, January 28, 2009
# ...if you're using an English locale, that is.
# Note that this and Date::Format's strftime are pretty much identical
As everyone else said "localtime" is how you tame date, in an easy and straight forward way.
But just to give you one more option. The DateTime module. This module has become a bit of a favorite of mine.
use DateTime;
my $dt = DateTime->now;
my $dow = $dt->day_name;
my $dom = $dt->mday;
my $month = $dt->month_abbr;
my $chr_era = $dt->year_with_christian_era;
print "Today is $dow, $month $dom $chr_era\n";
This would print "Today is Wednesday, Jan 28 2009AD". Just to show off a few of the many things it can do.
use DateTime;
print DateTime->now->ymd;
It prints out "2009-01-28"
Like someone else mentioned, you can use localtime, but I would parse it with Date::Format. It'll give you the timestamp formatted in pretty much any way you need it.
The simplest one-liner print statement to print localtime in clear, readable format is:
print scalar localtime (); #Output: Fri Nov 22 14:25:58 2019