如何让grep使用“从X到Y”的语法? (使用日期作为参数)

时间:2016-09-16 07:46:56

标签: perl grep

所以我想编写一个脚本来扫描订单和文件,并将这些文件的某些行粘贴到文件中。

如何让我的文件扫描指定范围而不是单数日期?

实际上,我需要更改的代码如下所示:

$curdir = "$scriptdir\\$folder";
opendir my $dir_b,  "$curdir" or die "Can't open directory: $!";
my @file = grep { /$timestamp/ } readdir $dir_b;
closedir $dir_b;

现在第3行需要像这样工作

my @file = grep { /$timestamp1 to $timestamp2/ } readdir $dir_b;

谁知道如何实现这一目标? timestamp1将为20160820,timestamp2为20160903或20160830

感谢您的帮助

3 个答案:

答案 0 :(得分:2)

您可以使用Regexp::Assemble在日期范围内的所有时间戳中构建一个模式。

use strict;
use warnings;
use Regexp::Assemble;

my $timestamp_late  = 20160830;
my $timestamp_early = 20160820;

my $ra = Regexp::Assemble->new;
$ra->add( $_ ) for $timestamp_early .. $timestamp_late;

print $ra->re;

该案例的模式是:(?^:201608(?:2\d|30))

您现在可以像这样使用它:

my $pattern = $ra->re;
my @files = grep { /$pattern/ } readdir $dir_b;

它通过将多个模式组合成一个模式来工作。

  

Regexp :: Assemble接受任意数量的正则表达式,并将它们组合成一个正则表达式(或RE),匹配单个RE匹配的所有表达式。

     

因此,只需要针对一个表达式测试目标字符串,而不是使用大型表达式列表进行循环。当你有几千种模式需要处理时,这很有趣。尽最大努力产生尽可能小的图案。

我们这里的模式相当简单(它们只是字符串),但它仍然有效。结果模式的工作方式如下:

(?^:                ) # non-capture group w/o non-default flags for the sub pattern
    201608            # literal 201608
          (?:      )  # non-capture group
             2\d      # literal 2 followed by a digit (0-9)
                |     # or
                 30   # literal 30

解释(?^:) in this part of perlre

如果传入更多数字,则生成的正则表达式会有所不同。当然这不适用于日期,所以使用我简单的1 .. 9表达式,我们可以得到所有数字。 ..range operator,并会针对上述情况返回列表(1, 2, 3, 4, 5, 6, 7, 8, 9)

因此,如果您想确保只获得有效日期,可以选择this approachthis approach。这是一个例子。

use strict;
use warnings;
use Regexp::Assemble;
use DateTime;

my $timestamp_late  = DateTime->new( year => 2016, month => 9, day => 1 );
my $timestamp_early = DateTime->new( year => 2016, month => 8, day => 19 );    # -1 day

my $ra = Regexp::Assemble->new;
while ( $timestamp_early->add( days => 1 ) <= $timestamp_late ) {
    $ra->add( $timestamp_early->ymd(q{}) );
}

print $ra->re;

这将持续到下个月并给出

(?^:20160(?:8(?:3[01]|2\d)|901))

,只匹配实际日期,而另一个更简单的解决方案将包括它们之间的所有数字,包括8月99日。

(?^:20160(?:8(?:2\d|3\d|4\d|5\d|6\d|7\d|8\d|9\d)|90[01]))

答案 1 :(得分:2)

Сухой27解决方案,发布为评论

my @file = grep { /$timestamp1/ .. /$timestamp2/ } readdir $dir_b;

使用range operator

的一个很好的例子

答案 2 :(得分:2)

我赞成一些易于理解的简单方法。触发器很酷,但几乎没有人知道它的作用。

您无需在一次操作中执行所有操作:

 my @file = grep { 
    my $this_date = ...;
    $lower_date <= $this_date and $this_date <= $higher_date; 
    } @inputs;