Replace date in a txt file having xml - perl

I have a text file in which i have a xml data. Now i want to replace the date from current date plus 2 days in dd/mm/yyyy format . I tried it will sed command in Unix but i am unable to do that .Below command i tried to change the date .But it is not changing the text.
sed -i 's/\<Date\>*\<Date\>/\<Date\>date +"%m/%d/%y"<Date\>/g' avltest.xml
<Args>
<Date>01/10/2017</Date>
<\Args>
In the Date field i want to change the date whenever i run my command or i can use that command from a script.

Here's a perl version:
perl -MPOSIX=strftime -i -pe 's{<Date>.*?</Date>}{"<Date>" . strftime("%m/%d/%Y", localtime(time + 2 * 24 * 60 * 60)) . "</Date>"}e' avltest.xml
I removed the redundant backslashes before < and >, added the missing / in </Date>, and fixed the * part.

Don't use regular expressions to modify XML. It's dirty, hacky, brittle and unnecessary:
#!/usr/bin/perl
use warnings;
use strict;
use XML::Twig;
use Time::Piece;
#load your file
my $twig = XML::Twig->new->parsefile('avltest.xml');
#iterate all <Date> elements.
foreach my $date_elt ( $twig->get_xpath('//Date') ) {
my $date = localtime;
#increment it by two days.
$date += 2 * 60 * 60 * 24; #two days.
#replace it.
$date_elt -> set_text($date -> strftime("%m/%d/%Y"));
}
#print XML to STDOUT.
$twig -> set_pretty_print('indented_a');
$twig -> print;
This will do what you want as described, and not be tripped up by 'end of year/end of month'.
You can use parsefile_inplace in XML twig if you prefer - that requires a slightly different code setup.

Related

Perl using Date::Parse module unable to print <STDIN> date in different format

I want to accept a user date on the command line in format
dd/mm/yyyy
then print the date out to the user in
yyyy/mm/dd
I am trying to use the Date::Parse module to parse into a date to be reprinted.
The Date:Parse docs show that I should be able to get $day, $month and $year from user input.
use Date::Parse;
$time = str2time($date);
($ss,$mm,$hh,$day,$month,$year,$zone) = strptime($date);
This is my current code:
use strict;
use Date::Parse;
print "Enter a date in dd/mm/yyy format: ";
my $user_date = <STDIN>;
my #date = strptime($user_date);
# ( $day, $month, $year ) = strptime($user_date);
# my $user_day = ( ($day) = strptime($user_date) );
print "%Y/%m/%d", #date;
However the print fails and it appears from output that entered 10 of 10 is 9 in output.
Output
Enter a date in dd/mm/yyy format: 16/10/1952
%Y/%m/%d1952916s
What should I do?
The documentation for Date::Parse isn't clear, but it looks like you get the values back in the format that localtime() would expect. The year, for example, seems to be the year minus 1900. This means that the month number will be 0 to 11 rather than 1 to 12.
Date::Parse hasn't been updated for over five years. I'd suggest that it should best be avoided these days. There are much better options to choose from. These include Time::Piece that has been included as a standard part of the Perl distribution since version 5.10.0. You can use its strptime() (string parse time) method to parse your string and its strftime() (string format time) method to format the date object as you like.
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
use Time::Piece;
print "Enter a date in dd/mm/yyy format: ";
chomp(my $user_date = <STDIN>);
my $tp = Time::Piece->strptime($user_date, '%d/%m/%Y');
say $tp->strftime('%Y/%m/%d');
Update:
Also, it's really not clear what this line is supposed to do:
print "%Y/%m/%d", #date;
I think you were thinking of using the strftime() method from POSIX.pm.
print strftime "%Y/%m/%d", #date;
But with use warnings this generates warnings because of all the undefined values in #data (that's a rather bizarre design decision in that module and, in my opinion, another reason to avoid it). You can fix that by replacing:
my #date = strptime($user_date);
With:
my #date = map { $_ // 0 } strptime($user_date);

Increment date in shell script on AIX [duplicate]

This question already has answers here:
What is the optimal way to loop between two dates in Perl?
(5 answers)
Closed 6 years ago.
I have to create a shell script file that accepts a string, start date and end date and generates the output file in the below format.
Note : Dates are in MM/DD/YYYY
string,start date
string,start date + 1
...
string,end date
I am not able to increment the date. I tried using date -d option but it is not available on AIX.
Could someone please help with a built in shell script command or perl command to increment the date ?.
Given a constraint of 'uses core':
#!/usr/bin/env perl
use strict;
use warnings;
use Time::Piece;
use Time::Seconds;
my $FORMAT = '%m/%d/%Y';
my $start = '01/22/2016';
my $end = '01/31/2016';
my $start_t = Time::Piece->strptime( $start, $FORMAT );
my $end_t = Time::Piece->strptime( $end, $FORMAT );
while ( $start_t <= $end_t ) {
print $start_t ->strftime($FORMAT), "\n";
$start_t += ONE_DAY;
}
Both Time::Piece and Time::Seconds are core as of perl 5.9.5. The latter is only needed for ONE_DAY - otherwise you can just add 60 * 60 * 24 instead.
I'm sure you can figure out how to print the strings etc. yourself.
(I'd also note - this is a horrible time format, and should be avoided)
You can make a shellscript using awk like this:
str="some string"
startd="12/25/2016"
endd="1/17/2017"
maxloop=10
echo "${maxloop}" |
awk -v string="${str}" -v startdate="${startd}" -v enddate="${endd}" '{
split(startdate,A,"[/]");
T1=mktime(A[3] " " A[1] " " A[2] " 0 0 0");
split(enddate,B,"[/]");
T2=mktime(B[3] " " B[1] " " B[2] " 23 59 59");
linenr=1;
while (T1 < T2) {
printf("%s,%s\n",string,
strftime("%m-%d-%Y",T1));
T1+=3600*24;
if (linenr++ > $1) break;
}
}'
The linenr/maxloop is not adding something to the solution but will help against
mistakes in the input values or maintenance of the script.

convert perl timestamp to human readable

I am reading a log file which contains time stamps which I want to convert to human readable.
In this command, $1 contains a time stamp (like this 1403457192.663): $temp = localtime->mon($1) but instead of storing the month, $temp contains the same timestamp that was input. What am I doing wrong?
You're close. The time should be passed to the localtime function, not the mon method.:
$temp = localtime($1)->mon; # 6
You can use strftime with this to turn it into any arbitrary format
localtime($1)->strftime("%b %d %a"); # Jun 22 Sun
Or if you're not picky about the format you can just stringify it:
$temp = localtime($1);
print "$temp\n"; # Sun Jun 22 13:13:12 2014
This assumes that Time::Piece is loaded.
I'd simply go with
$ perl -E'
use POSIX qw( strftime );
say strftime("%Y/%m/%d %H:%M:%S", localtime(1403457192.663));
'
2014/06/22 13:13:12
But you're using Time::localtime. That module overrides the localtime builtin, so you need a slight modification if you use that.
Either avoid using Time::localtime's localtime
$ perl -E'
use POSIX qw( strftime );
use Time::localtime qw( localtime );
say strftime("%Y/%m/%d %H:%M:%S", CORE::localtime(1403457192.663));
'
2014/06/22 13:13:12
or flatten an existing Time::localtime object.
$ perl -E'
use POSIX qw( strftime );
use Time::localtime qw( localtime );
my $tm = localtime(1403457192.663);
say strftime("%Y/%m/%d %H:%M:%S", #$tm);
'
2014/06/22 13:13:12
All of these solutions lose the millisecond precision. If it's relevant, you'll have to extract it from the original input and reinsert it in the output.
For formatting dates most system strftime manual pages will list a few "shortcuts" to get you certain "standard" formats.
e.g. %F is equivalent to ā€œ%Y-%m-%dā€.
~/% perl -MPOSIX -E'say strftime"%D",localtime'
06/25/14
~/% perl -MPOSIX -E'say strftime"%F",localtime'
2014-06-25
These can make using "ye olde" strftime easier ;-)
Perl since 5.10 now contains Time::Piece. This makes it the official way to handle time in Perl. Or, about as official as something gets in Perl. Since it's always available, you might as well learn to use that:
use strict;
use warnings;
use Time::Piece;
use Time::Seconds; # More time fun!
my $time = Time::Piece->new; # Gets the current timestamp
my $month = $time->mon(); # Month from 1 to 12
my $month = $time->month(); # Abbreviation of the name of month
my $month = $time->fullmonth(); # Full name of the month
my $time = $time + (ONE_DAY * 30) # Add thirty days to the time
my $date = $time->mdy # The date 30 days from now.

Optimize perl script to filter rows based on date in the file

I am a beginner with programming not just perl !
Please let me know what needs to change or how else this can be done.
Need to optimize the perl code to run faster.
For a test run, with around a 500MB file with 3 million rows in it, runtime is 28 minutes.
I know a tool which processes the 39 million rows in 15 mins, but i want to acheive this running on the command prompt without resorting to the tool.
Earlier I used Date::Manip and Date::Parse and moved on to DateTime, thinking it should be faster.
My approach was If the dates are ISO-8601 (ie, YYYY-MM-DD) and we do not need to validate them,
we can compare lexicographically (ie, the lt and gt operators.)
Input File Date Format is 07/18/2013 13:45:49
Input File Size 42GB.
Number of Rows 39 Million.
Column Delimiter : |~|
Platform : GNU/Linux
I have tried ">" and "gt" and did not find any difference in runtime.
Code snippet:
use DateTime::Format::Strptime;
my $idate = "07/17/2013 00:00:00";
my $Strp = DateTime::Format::Strptime->new(
pattern => '%m/%d/%Y %H:%M:%S',
);
my $inputdt = $Strp->parse_datetime($idate);
open (FILE,"myinputfile.dat") or die "could not input File\n";
while (defined(my $line = <FILE>)) {
my #chunks = split '[|]~[|]', $line;
my $fdate = $Strp->parse_datetime($chunks[6]);
if ( $fdate > $inputdt) {
open(FILEOUT, ">>myoutputfile.dat") or die "Could not write\n";
print FILEOUT "$line";
}
}
close(FILE);
close (FILEOUT);
There are two and a half big performance problems here:
You open the output file in every iteration. Just open it once, before the loop.
The parse_datetime returns a DateTime object. Object orientation with Perl implies a significant overhead. Because your pattern is well defined, we can do the parsing ourself, and remove all object orientation.
Reading a file in the GB range just takes some time. To speed this up, upgrade your hardware (e.g. to a SSD).
To parse the date string into a sortable representation, we just reorder the various parts to a string:
# %m/%d/%Y %H:%M:%S ā†’ %Y/%m/%d %H:%M:%S
$fdate =~ s{^ ([0-9]{2} / [0-9]{2}) / ([0-9]{4}) }{$2/$1}x;
if ($fdate gt $inputdate) { ... }
This would lead to the code
use strict; use warnings;
use constant DATE_FIELD => shift #ARGV;
my $inputdate = shift #ARGV;
$inputdate =~ s{^ ([0-9]{2} / [0-9]{2}) / ([0-9]{4}) }{$2/$1}x;
<>; # remove the header line
while (<>) {
my $filedate = (split /\|~\|/, $_, DATE_FIELD + 2)[DATE_FIELD];
$filedate =~ s{^ ([0-9]{2} / [0-9]{2}) / ([0-9]{4}) }{$2/$1}x;
print if $filedate gt $inputdate;
}
The in- and output, as well as the start date, are specified on the command line, e.g.
./script 6 '07/17/2013 00:00:00' myinputfile.dat >>myoutputfile.dat

How can I remove the timestamp from a filename in Perl?

I have a file which has a line in it as:
/hosting/logs/U01-ecom-SIT01/CU01-DC05-IFIO_SIT01_NU01-nc3sz1ecmas11/waslogs/SystemOut_10.01.21_16.54.18.log`
I need a script which would read this line and remove the time stamp from it, that is:
10.01.21_16.54.18
The script should print the filename without the timestamp and holding the full path, that is:
/hosting/logs/U01-ecom-SIT01/CU01-DC05-IFIO_SIT01_NU01-nc3sz1ecmas11/waslogs/SystemOut.log`
Please help as I'm unable to pattern match and output the file path without the timestamp.
echo "/hosting/logs/U01-ecom-SIT01/CU01-DC05-IFIO_SIT01_NU01-nc3sz1ecmas11/waslogs/SystemOut_10.01.21_16.54.18.log" |
perl -pe "s/_\d\d\.\d\d\.\d\d_\d\d\.\d\d\.\d\d//;"
$ perl -e 's{_\d{2}\.\d{2}.\d{2}_\d{2}\.\d{2}.\d{2}}{} and print for #ARGV' /hosting/logs/U01-ecom-SIT01/CU01-DC05-IFIO_SIT01_NU01-nc3sz1ecmas11/waslogs/SystemOut_10.01.21_16.54.18.log
Path shortened to prevent scrolling:
$ cat paths
CU01-DC05-IFIO_SIT01_NU01-nc3sz1ecmas11/waslogs/SystemOut_10.01.21_16.54.18.log
$ perl -pe 's/(_(\d\d(\.\d\d){2})){2}\.log$/.log/' paths
CU01-DC05-IFIO_SIT01_NU01-nc3sz1ecmas11/waslogs/SystemOut.log
The timestamp is made up of 2 sequences that look like _##.##.##. The subsequences end with 2 sequences of .##. These are the roles of the {2} quantifiers.
while(<>){
#s = split /\// ;
$fullpath=join("/",splice #s , 0, $#s);
#a = split /[_.]/ ,$s[-1];
$newfile="$fullpath/$a[0].$a[-1]";
print $newfile."\n";
}
You can use the following coding
use strict;
use warnings;
my $var; $var=/hosting/logs/U01-ecom-SIT01/CU01-DC05-IFIO_SIT01_NU01-nc3sz1ecmas11/waslogs/SystemOut_10.01.21_16.54.18.log";
$var=~s/_\d\d\.\d\d\.\d\d//g;
# $var=~s/_10\.01\.21_16\.54\.18//g; # You can use this way also
print "$var\n";