2

I have a file like this:

2014-12-31 531.252429 532.602384 525.802363 526.402397 1368200 526.402397
2014-12-30 528.092396 531.152424 527.132366 530.422394 876300 530.422394
2014-12-29 532.192446 535.482414 530.013375 530.332426 2278500 530.332426
2014-12-26 528.772422 534.252417 527.312364 534.032454 1036000 534.032454
2014-12-24 530.512424 531.761394 527.022384 528.772422 705900 528.772422
2014-12-23 527.00237 534.56241 526.292354 530.592416 2197600 530.592416

and want to echo each line between 2014-12-31 and 2014-12-23 in a bash script.

I tried this before, but it ended up showing me the lines I wanted but in a never-ending loop.

perl -ne 'if ( m/^([0-9-]+)/ ) { $date = $1; print if ( $date ge "2014-12-31" and $date le "2014-12-23" ) }' file.txt

What went wrong and how can I fix the code?

Simona
  • 63
  • 1
  • 1
  • 4

4 Answers4

2

If the lines are sorted by date, you could simply use the Perl's .. range operator:

perl -ne 'print if /2014-12-31/ .. /2014-12-23/' file.txt

To fix your one-liner, swap the le and ge operators to set the range correctly:

perl -ne 'if ( m/^([0-9-]+)/ ) { $date = $1; print if ( $date le "2014-12-31" and $date ge "2014-12-23" ) }' file.txt
Eugene Yarmash
  • 142,882
  • 41
  • 325
  • 378
  • Tried this, it prints the lines over and over (like in a loop) too. What could be causing this? – Simona Jan 25 '16 at 16:31
  • Your first solution doesn't work if there's 0 records with the later date, if there's 0 records with the earlier date, or if there are 2+ records with the earlier date. – ikegami Jan 25 '16 at 17:11
1

You're printing the records with dates before your start date and after your end date.
You should be printing the records with dates after your start date and before your end date.
To fix, just flip the dates around:

perl -ne'if ( m/^([0-9-]+)/ ) { $date = $1; print if ( $date ge "2014-12-23" and $date le "2014-12-31" ) }'

Shorter version:

perl -ane'print if $F[0] ge "2014-12-23" && $F[0] le "2014-12-31"'
ikegami
  • 367,544
  • 15
  • 269
  • 518
1

I have to say - I'd probably tackle it by parsing the time and date stamps into epoch times, then comparing those.

So something like:

#!/usr/bin/env perl
use strict;
use warnings;

use Time::Piece;
my $end = Time::Piece->strptime( "2014-12-31", "%Y-%m-%d" );
my $start = Time::Piece->strptime( "2014-12-23", "%Y-%m-%d" );

while ( <DATA> ) {
    my $t = Time::Piece->strptime( (split)[0], "%Y-%m-%d" );
    print if $t > $start and $t < $end;
}

__DATA__
2014-12-31 531.252429 532.602384 525.802363 526.402397 1368200 526.402397
2014-12-30 528.092396 531.152424 527.132366 530.422394 876300 530.422394
2014-12-29 532.192446 535.482414 530.013375 530.332426 2278500 530.332426
2014-12-26 528.772422 534.252417 527.312364 534.032454 1036000 534.032454
2014-12-24 530.512424 531.761394 527.022384 528.772422 705900 528.772422
2014-12-23 527.00237 534.56241 526.292354 530.592416 2197600 530.592416

It's converting your date stamp into an epoch (second since 1970) as you would get from time().

You could probably collapse this down to a one liner:

perl -MTime::Piece -ane 'BEGIN{@R=map { Time::Piece->strptime($_, "%Y-%m-%d") } qw ( 2014-12-23 2014-12-31 )} $t = Time::Piece->strptime($F[0],"%Y-%m-%d"); print if $t > $R[0] and $t < $R[1]'

Now, I know that's a bit heavyweight for your use case, but the advantage of doing this is that it can quite easily support different date formats.

Sobrique
  • 52,974
  • 7
  • 60
  • 101
1

Since your dates are ISO 8601, you can compare lexicographically.

You do not need a regex; just use awk like auto split.

Given:

$ echo "$dates"
2014-12-31 531.252429 532.602384 525.802363 526.402397 1368200 526.402397
2014-12-30 528.092396 531.152424 527.132366 530.422394 876300 530.422394
2014-12-29 532.192446 535.482414 530.013375 530.332426 2278500 530.332426
2014-12-26 528.772422 534.252417 527.312364 534.032454 1036000 534.032454
2014-12-24 530.512424 531.761394 527.022384 528.772422 705900 528.772422
2014-12-23 527.00237 534.56241 526.292354 530.592416 2197600 530.592416

You can do:

$ echo "$dates" | perl -lane 'print if $F[0] gt "2014-12-28" && $F[0] lt "2014-12-31"'
2014-12-30 528.092396 531.152424 527.132366 530.422394 876300 530.422394
2014-12-29 532.192446 535.482414 530.013375 530.332426 2278500 530.332426
dawg
  • 98,345
  • 23
  • 131
  • 206