如何按日期时间范围进行grep?

3

我有一个日志文件,看起来像这样:

2011-03-21 00:01 xxxx
2011-03-22 04:05 xxxx
....
2011-03-25 10:12 xxxx
....
2011-04-04 12:23 xxxx

我希望有一个脚本,需要两个日期范围作为参数,例如:
grep-date-range.sh 2011-03-25 2011-04-02

它将在[2011-03-25,2011-04-02]中查找所有日志。我知道对于特定情况,我可以使用通配符,但在我看来,这不是通用的。有人能给我一个解决方案吗?
编辑: Python脚本也可以接受。

您可以可能将一些超复杂的 bash/cut/sed/awk 脚本链接在一起,以实现您想要的功能。但如果您了解 Python 或 Perl,编写一个小脚本来解析每行中的前三个数字并将其与参数中给定的范围进行比较可能更容易。 - Philip
我对作为shell脚本来完成这个任务的难度感到非常惊讶。最大的障碍似乎是没有办法使日期接受秒数自1970年以来的输入。真是让人郁闷。 - drysdam
5个回答

2

这是一个可能更适合编写简短Python脚本的情况。该语言中的高级日期操作功能可能很方便。

下面的脚本非常简单 - 经过更多的工作,它可以处理本地时间差异、夏令时等问题。

#! /usr/bin/python
import sys
from datetime import datetime
d_format = "%Y-%m-%d"

try:
    start = datetime.strptime(sys.argv[1], d_format)
    end = datetime.strptime(sys.argv[2], d_format) 
except (TypeError, IndexError):
    sys.stderr.write("Example: grep-date-range.py 2011-03-25 2011-04-02 \n")

for line in sys.stdin:
    try:
        date = datetime.strptime(line.split()[0], d_format)
        # suit the <=, <, comparisons bellow to your needs:
        if start <= date < end:
            sys.stdout.write(line)
    except (ValueError, IndexError):
        pass 

datetime.strptime 在我的 Python 中找不到。我可以使用 time.strptime。 - Dagang
Todd:你是否导入了datetime import datetime?datetime恰好是datetime模块内的一个工厂函数。如果你只是导入了datetime,那么你必须使用datetime.datetime.strptime——这非常标准,并且在你的Python中肯定可行。 - jsbueno
抱歉 - 刚刚检查了文档,这在Python 2.5中添加(已经有6或7年了,如果您的系统上有比那更旧的软件,您应该认真考虑升级)。无论如何,对于较旧的Python版本,解决方法是:datetime.datetime(*(time.strptime(date_string, format)[0:6])) - jsbueno

2
sed -n "/$1/,/$2/p" $3

称之为:

fromTo "2011-03-25" "2011-04-02" foo.log

sed

  • -n: 不输出
  • /from/,/to/: 匹配模式
  • p: 打印

日期必须存在于文件中,如果你只是在文件中有2011-03-24和2011-03-26作为日期,它将无法工作。这是字符串匹配,而不是日期匹配。 你不需要引用,但我碰巧有另一种日期格式,所以我进行了测试("Mar 23"等)。


只是一个小提示,如果日期没有排序,这个方法将无法正常工作。 - kurumi
1
是的,但示例日志文件看起来已经排序,因为大多数日志文件避免时间混乱。 - user unknown
这就是为什么我说这只是一个小注释 :) - kurumi

1

好的,我终于搞定了。基本思路是使用sort -m合并给定的日期,然后使用sed提取那些已知的行(感谢“user unknown”的建议)。如果数据文件尚未排序,请先对其进行排序。这里的假设是YYYY-MM-DD是一个常量,否则这将无法工作。

您可以通过使用mktemp而不是/tmp/startstop以及比“START”和“END”更独特的字符串来使其更加健壮。

/tmp/data显然是您的数据文件。

#!/bin/bash

START=$1
END=$2

echo $START START > /tmp/startstop
echo $END END >> /tmp/startstop

sort -m /tmp/data /tmp/startstop | sed -n '/START/,/END/p'

0

嗯,既然你的日期已经是“可排序”的了,

#!/bin/bash

a=2011-03-25
b=2011-06-02
a=${a//-/} # you can remove the dashes or not, up to you
b=${b//-/} # you can remove the dashes or not, up to you
awk -va=$a -vb=$b '{
    # save the first field if going to remove dash, 
    old=$1 
    # you can remove the dashes or not, up to you. Because your date is sortable
    # the dash will not matter.
    gsub(/-/,"",old) # for removing dash
    if( old >= a && old <=b ){ 
        # or use if ($1 >=a && $1 <=b ) (if not removing dash)
        print
    }
}' file

去掉破折号会增加很多不必要的复杂性:awk -v a=2011-03-25 -v b=2011-04-02 'a <= $1 && $1 <= b' - glenn jackman
@glenn,在他的情况下,没有必要,因为我也已经指出了。但是,如果日期不可搜索,那么是的。 - kurumi

0
同时,如果您的日志由journalctl管理,您只需要
 journalctl -S 2011-03-25 -U 2011-04-02 (...)

man journalctl中:

-S, --since=, -U, --until=

从指定日期开始显示条目,或者从指定日期或之前的日期开始显示条目。日期格式应为"2012-10-30 18:17:16"。如果省略了时间部分,则默认为"00:00:00"。如果只省略了秒数部分,则默认为":00"。如果省略了日期部分,则默认为当前日期。另外,字符串"yesterday"、"today"、"tomorrow"分别表示前一天的00:00:00、当前日期的00:00:00和后一天的00:00:00。"now"表示当前时间。最后,可以指定相对时间,以"-"或"+"为前缀,分别表示当前时间之前或之后的时间。有关完整的时间和日期规范,请参阅systemd.time(7)。请注意,--output=short-full打印的时间戳完全遵循此格式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接