note
haukex
<ul>
<li>The code you posted does not compile as is, which makes it harder for those trying to help. In this case, I'm guessing that you may have edited out too much for posting - you could have instead replaced the user input with simple variable assignments, e.g. <c>my $sdate = '...';</c>. Please see [id://1177642|SSCCE].</li>
<li>You should in general try to call external programs as little as possible, and I wrote about some of the issues with doing so [id://1180578|here]. In general, Perl can do anything that <c>sed</c> and <c>awk</c> can, and a <c>grep</c> can be written in Perl as:
<c>
open my $fh, '<', $filename or die "$filename: $!";
while (<$fh>) {
if (/pattern/) {
# do something ...
print $_;
}
}
close $fh;
</c>
See [doc://perlintro#Files-and-I/O], [doc://perlop#I/O-Operators], [doc://perlrequick], and [doc://perlretut].
</li>
<li>The dots in an IP address will be interpreted as a special character in a regular expression: the dot matches anything, so if the user enters "12.34", that'll also match e.g. "12534". To avoid that, use [doc://quotemeta] or <c>\Q...\E</c>.</li>
</ul>
<c>
use warnings;
use strict;
use DateTime;
use DateTime::Format::Strptime;
use Path::Class qw/dir/;
my $LOGPATH = '.';
my $STARTD = '2017-12-08';
my $NUMDAYS = 3;
my $PATTERN = '1.2.3.4';
my $strp = DateTime::Format::Strptime->new(on_error=>'croak',
pattern => '%Y-%m-%d', time_zone=>'local');
my $dt = $strp->parse_datetime($STARTD);
my @files;
for (1..$NUMDAYS) {
my $date = $dt->strftime('%Y-%m-%d');
push @files, sort grep { $_->basename=~/\.log\z/i }
dir($LOGPATH,$date)->children;
$dt->add(days=>1);
}
{ # in a new block for "local"
local *ARGV; @ARGV = @files;
while (<>) {
chomp;
if (/\Q$PATTERN\E/) {
print "$ARGV:$.: $_\n";
}
} continue { close ARGV if eof }
}
</c>
<ul>
<li>It's very good you're using a module like [doc://Time::Piece] for your date handling. Personally I like [mod://DateTime] because it does a whole lot more, along with [mod://DateTime::Format::Strptime] for parsing, which is why I used those above.</li>
<li>I'm using [mod://Path::Class] for getting filenames, where <c>$_->basename=~/\.log\z/i</c> matches those files whose names end in <c>.log</c> (case-insensitively).</li>
<li>I used a trick and assigned the list of files to the special [doc://@ARGV] variable, which normally holds the command line arguments, so that I can make use of Perl's special <c>while (<>)</c> loop, described in [doc://perlop#I/O-Operators]. Inside that loop, the current filename is stored in [doc://$ARGV], and the line number in [doc://$.] - but see the documentation on [doc://eof] as for why I need the snippet of code <c>close ARGV if eof</c>. (If all that is too much magic for now, you can also wrap the grepping code I showed at the top in a <c>for my $filename (@files) { ... }</c> loop.)</li>
<li>As opposed to your description, I have taken the "number of days" to <i>include</i> the start day, since that makes more sense to me.</li>
</ul>
<p><b>Update:</b> Fixed [http://blogs.perl.org/users/book/2018/07/a-widespread-and-broken-perl-idiom.html|this potential issue] in the above example code.</p>
1206578
1206578