I am trying to find dates with in a certain format, I have done it before with perl(strftime), but that time I mentioned a static time, this time i need a variable $CURRENT_DAY here.
Below is the issue, when equate the CURRENT_DAY by using DAYHOUR=86400 and an integer, the variable is giving the right time, but once I put $CURRENT_DAY variable in the statement, the date would not decrease.
$ DAYHOUR=86400
$ DAY=1
$ CURRENT_DAY=`echo $(($DAYHOUR*$DAY))`
$ DD=`perl -e 'use POSIX; print strftime "%d", localtime time - $CURRENT_DAY;'`
$ echo $DD
20
$ DAY=`echo $(($DAY+1))`
$ CURRENT_DAY=`echo $(($DAYHOUR*$DAY))`
$ DD=`perl -e 'use POSIX; print strftime "%d", localtime time - $CURRENT_DAY;'`
$ echo $DD
20
$ DAY=`echo $(($DAY+1))`
$ echo $DAY
3
$ CURRENT_DAY=`echo $(($DAYHOUR*$DAY))`
$ echo $CURRENT_DAY
259200
$ echo `perl -e 'use POSIX; print strftime "%d", localtime time - 259200;'`
17
Your principal problem is that $CURRENT_DAY is a Perl script variable. By enclosing your Perl script in single quotes, there is no visibility to the shell's variable of the same name. Had you enabled warnings (e.g. with -w) you would have seen this.
There are a couple of ways to circumvent your problem. One is to use double quotes to encapsulate your Perl thus allowing the shell to first expand its variable's value before the Perl is run:
CURRENT_DAY=3
perl -MPOSIX -wle "print strftime qq(%d),localtime time-(86400*$CURRENT_DAY);print $CURRENT_DAY"
17
Another is:
export CURRENT_DAY=3
perl -MPOSIX -wle 'print strftime qq(%d),localtime time-(86400*$ENV{CURRENT_DAY})'
Be advised that adding or subtracting 24-hours from a time to calculate yesterday or tomorrow will not correctly account for daylight saving changes. See this faq
You may pass them as arguments in #ARGV:
$ dd_seconds_ago () { perl -MPOSIX -e 'print strftime q(%d), localtime(time - shift)' "$#"; }
$ DD=$(dd_seconds_ago 86400)
Without an argument and in the above context, shift shifts #ARGV, which is handy for shell one-liners like this
Like Perl, sh doesn't interpolate in single quoted strings, so Perl sees $CURRENT_DAY instead of the actual number, and you've never assigned anything to that Perl variable. You could switch to a double-quoted string.
perl -MPOSIX -e"print strftime '%d', localtime time-$CURRENT_DAY;"
That's fine since $CURRENT_DAY is a number, but if you wanted to pass an arbitrary string, you'd use an env var or an argument.
export CURRENT_DAY
perl -MPOSIX -e'print strftime "%d", localtime time-$ENV{CURRENT_DAY};'
or
perl -MPOSIX -e'print strftime "%d", localtime time-$ARGV[0];' -- "$CURRENT_DAY"
Note that your code is buggy, though. There are two hours every year for which your code will give the wrong answer because not all days have 86400 seconds. Some have 82800, and others have 90000. (And that's assuming leap seconds don't factor in.) A Perl solution that doesn't suffer from that problem follows:
perl -MDateTime -e'print
DateTime->today(time_zone=>"local")
->subtract(days=>$ARGV[0])
->strftime("%d")' -- "$DAY"
Or you could use date.
date -d "$DAY days ago" +%d
I am assuming you want to pass the number of days in the past in the shell variable DAY and that you want the answer in the shell variable DD
So if it is the 20th of the month and the DAY is 1, then DD should be set to 19
You could modify your Perl command as follows:
DD=`perl -e 'use POSIX; print strftime "%d", localtime( time - ($ENV{DAY}* 86400))';
Alternatively, you could use the GNU date command that is widely available
DD=`date -d "$DAY days ago" +%d`
Using date is probably better at dealing with leap days, etc
Related
The following has been tested with perl 5.24 on OS X 10.11.5.
I wrote a short program (perl-embed.pl) to determine whether perl escapes shell metacharacters when interpolating strings into backticks (it doesn't).
use strict;
use warnings;
my $bar = '" ; echo 45 ; "';
printf "%s\n", `echo "hi${bar}ls"`;
I was very surprised to see that this generated a warning and only executed part of the command.
$ perl perl-embed.pl
Redundant argument in printf at perl-embed.pl line 6.
hi
For comparison the following program (perl-embed2.pl) with print instead of printf runs without warnings.
use strict;
use warnings;
my $bar = '" ; echo 45 ; "';
print `echo "hi${bar}ls"`;
I then ran it.
$ perl perl-embed2.pl
hi
45
<contents of current working directory>
perl-embed.pl's behavior is totally unexpected. printf interpolates the contents of strings just fine in other contexts, even if the string contains weird characters.
$ perl -Mstrict -Mwarnings -e 'printf "%s\n", q[5]'
5
$ perl -Mstrict -Mwarnings -e 'printf "%s\n", q["]'
"
The system perl (version 5.18) does not emit this warning, but seems not to execute ls or echo 45 like we would expect
$ /usr/bin/perl perl-embed.pl
hi
$ /usr/bin/perl perl-embed2.pl
hi
45
<contents of current directory>
Why is perl behaving this way? Note that in every case perl is exiting normally.
You are using backticks in list context, so the expression
`echo "hi${bar}ls`
will run the command
`echo "hi"; echo 45; ls`
and return each line of output in a separate element, for example
( "hi",
"45",
"foo",
... # other files in current directory
)
But the template in printf ("%s\n") only has one placeholder, so printf gets confused and issues the warning, just as if you said
perl -we 'printf "%d\n", 1, 2, 3, 4'
I am writing the below command for 1 hour ago in Solaris format.
date +%Y-%m-%d-%H:%M:%S -M "1 hour ago"
But I need half hour (30 minutes) ago format in Linux.
If you are running on Linux, this should work if run in bash:
date --date=#$((`date +%s`-1800))
(I tested it on Solaris 11 using gdate instead of date. I don't have a Linux box booted right now.)
Solaris 10 date command doesn't support the -M flag which is a GNU date extension.
An alternative is to use perl.
Here is how to get one hour ago:
perl -e 'my ($y,$m,$d,$H,$M,$S)=(localtime(time-3600))[5,4,3,2,1,0];
printf "%04d-%02d-%02d-%02d:%02d:%02d\n",$y+1900,$m+1,$d,$H,$M,$S;'
and half-an-hour ago:
perl -e 'my ($y,$m,$d,$H,$M,$S)=(localtime(time-1800))[5,4,3,2,1,0];
printf "%04d-%02d-%02d-%02d:%02d:%02d\n",$y+1900,$m+1,$d,$H,$M,$S;'
Edit: Using the POSIX Perl module, you can also do it this simpler way:
perl -e 'use POSIX qw(strftime);
print strftime("%Y-%m-%d-%H:%M:%S\n", localtime(time-1800));'
I have an utility to generate code documentation every night. I would like to add a timestamp in order to be aware how old the generated documentation is. I would like to use perl.
I've seen that with the following sentence I can change a joker (%1) by any value I want
perl -pi.bak -e 's/%1/date/g' footer.html
And with this other one I can get the system timestamp:
perl -MPOSIX -we "print POSIX::strftime('%d/%m/%Y %H:%M:%S', localtime)"
My question is whether there is any way to merge both instructions in just one sentence.
Thank you very much
Try doing this :
perl -MPOSIX -pi.bak -e 'BEGIN{$date = strftime("%d/%m/%Y %H:%M:%S", localtime);} s/%1/$date/g' file.html
sh command:
perl -i.bak -MPOSIX -pe's/%1/strftime("%d/%m/%Y %H:%M:%S", localtime)/eg'
cmd command:
perl -i.bak -MPOSIX -pe"s/%1/strftime('%d/%m/%Y %H:%M:%S', localtime)/eg"
/e cause the replacement expression to be treated as Perl code to execute, the result of which is the replacement text.
I've got a script that calls Perl's Time::HiRes module to calculate elapsed time. Basically the script gets the time by passing the following one-liner:
use Time::HiRes qw(time); print time
to the Perl interpreter via backticks and gets back the results.
#/bin/sh
START_TIME=`perl -e 'use Time::HiRes qw(time); print time'`
END_TIME=`perl -e 'use Time::HiRes qw(time); print time'`
ELAPSED_TIME=$(echo "($END_TIME - $START_TIME)" | bc)
echo $ELAPSED_TIME
I tried to rewrite it in a more modular way, but I'm stumped by the quoting rules of the Bash shell.
#/bin/sh
CALCULATE='bc'
NOW="perl -e 'use Time::HiRes qw(time); print time'"
START_TIME=`$NOW`
[Some long running task ...]
ELAPSED_TIME=$(echo "($NOW - $START_TIME)" | $CALCULATE)
echo $ELAPSED_TIME
Bash complains that something is not quoted properly. Why doesn't Bash just expand the command in $NOW and pass it to the backtick to be executed?
I tried various ways to embed Perl code in a shell script variable, but I can't seem to get it right.
How can I quote Perl code inside a shell script correctly?
Using a function is the most straightforward way to do this, I think:
#! /bin/bash
now() {
perl -e 'use Time::HiRes qw(time); print time';
}
calc=bc
time1=$(now)
time2=$(now)
elapsed=$(echo $time2 - $time1 | $calc)
echo $elapsed $time1 $time2
Essentially no quoting is required.
Your problem is that $NOW is just a string with some Perl code in it. You need to tell Bash to execute it, with backticks or $():
ELAPSED_TIME=$(echo "($($NOW) - $START_TIME)" | $CALCULATE)
Also, Bash can do arithmetic natively:
ELAPSED_TIME=$(( $($NOW) - $START_TIME))
There isn't any need to invoke bc.
Finally, starting and stopping perl is likely to take a lot of time, which will add noise to your results. I'd recommend running perl only once, and having perl itself execute the long-running task. You'd then do all the computation within Perl itself as well:
#!/usr/bin/perl
use Time::HiRes qw(time);
my $start = time;
system(#ARGV);
my $end = time;
print "Elapsed: ", ($end - $start), "\n"
Or you could just use the Bash builtin time (or /usr/bin/time) to just do all the timing directly.
If $NOW is outside of quotes, it gets split on whitespace.
$ perl -E'say 0+#ARGV; say for #ARGV' $NOW
7
perl
-e
'use
Time::HiRes
qw(time);
print
time'
You can surround the variable by double-quotes to avoid this:
$ perl -E'say 0+#ARGV; say for #ARGV' "$NOW"
1
perl -e 'use Time::HiRes qw(time); print time'
But you want to execute that string as a shell command. For that, use eval.
$ eval "$NOW"
1335602750.57325
Finally, to assign it, we use the backticks (or equivalent $( ... )).
$ START_TIME=$(eval "$NOW")
$ echo $START_TIME
1335602898.78472
The previously posted function is obviously cleaner, but you said you wanted help with quoting.
By the way,
perl -e 'use Time::HiRes qw(time); print time'
can be shortened to
perl -MTime::HiRes=time -e'print time'
and even to the following (since the trailing new line is perfectly fine):
perl -MTime::HiRes=time -E'say time'
Or if you really wanted to golf:
perl -MTime::HiRes=time -Esay+time
Below is a modified version of your script. You basically need to understand that some applications have their standard output towards standard error (stderr), so when you don't see their output put in a variable, you just need to redirect it to standard output (stdout):
#/bin/sh
CALCULATE='bc'
echo 'starting'
NOW=$(perl -e 'use Time::HiRes qw(time); print time' 2>&1)
sleep 3
echo 'ending'
END_TIME=$(perl -e 'use Time::HiRes qw(time); print time' 2>&1)
ELAPSED_TIME=$(echo "($NOW - $START_TIME)")
echo $ELAPSED_TIME
I think the benefit of HiRes time is negated by the fact that perl is a relatively heavy external process and it is separately invoked two times.
If you don't need that many decimal places for the value. you can use the time builtin in Bash like
task() {
[Some long running task ...]
}
TIMEFORMAT=%R
elapse=$({ time task > task.out 2>&1; } 2>&1)
echo $elapse
It's easy to get Epoch-Seconds (timestamp) in perl:
time
But what's with milliseconds? The most effective way seems to be time*1000, but that's not as accurate as I want it to be. Any good hints except for the long terms documented #perldoc?
The Time::HiRes module has a drop-in replacement for time
$ perl -E 'say time'
1298827929
$ perl -MTime::HiRes=time -E 'say time'
1298827932.67446
You can read more in the perl FAQ
perldoc -q "How can I measure time under a second"
A real world example would be:
use Time::HiRes qw(gettimeofday);
print gettimeofday;
perl -MTime::HiRes=time -e 'print time;'
For Perl: v5.8.4 built for SunOS (sun4-solaris-64int), oylenshpeegul's answer needs to be modified.