gawk - 抑制匹配行的输出

gawk - suppress output of matched lines

我 运行 遇到 gawk 打印不需要的输出的问题。我想在文件中找到与表达式匹配的行,测试行中的信息是否与特定条件匹配,然后打印如果匹配的行。我得到了我想要的输出,但 gawk 还打印了与表达式匹配的每一行,而不仅仅是满足条件的行。

我正在尝试搜索包含要执行的某些操作的日期和时间的文件。我只想显示包含未来时间的行。日期格式如下:

text... 2016-01-22 10:03:41 more text...

我尝试使用 sed 来打印所有以当前时间开始的行,但不能保证文件包含那个时间的行,(而且不能保证行都有任何特定的年、月、日等)所以我需要更强大的东西。我决定尝试将时间转换为纪元以来的秒数,并将其与当前 systime 进行比较。如果转换产生的数字大于 systime,我想打印该行。

现在看来 gawkmktime() 功能是关键。不幸的是,它需要按以下格式输入:

yyyy mm dd hh mm ss

我目前正在一个测试文件(名为 timecomp)中搜索与日期格式匹配的正则表达式。

编辑:测试文件每行只包含一个日期和时间,没有其他文本。

我使用 sed 将日期分隔符(即 /、- 和 :) 替换为 space,然后使用管道将输出传输到名为 stime 的 gawk 脚本以下语句:

sed -e 's/[-://_]/ /g' timecomp | gawk -f stime

这是脚本

# stime
BEGIN { tsec=systime();  } /.*20[1-9][0-9] [0-1][1-9] [0-3][0-9] [0-2][0-9][0-6][0-9] [0-6][0-9]/ { 
    if (tsec < mktime([=13=]))
        print "\t" [=13=]    # the tab is just to differentiate the desired output from the other lines that are being printed.
} 

现在这是在获取我想要的基本信息,但它也在打印每个与原始表达式匹配的点赞,而不仅仅是包含未来时间的行。示例输出:

2016 01 22 13 23 20
2016 01 22 14 56 57
2016 01 22 15 46 46
2016 01 22 16 32 30
    2016 01 22 18 56 23
2016 01 22 18 56 23
    2016 01 22 22 22 28
2016 01 22 22 22 28
    2016 01 22 23 41 06
2016 01 22 23 41 06
    2016 01 22 20 32 33

以后如何只打印行?

注意:我在 Mac 上执行此操作,但我希望它可以移植到 Linux,因为我最终会为工作中必须执行的某些任务执行此操作。

我想尝试在一个脚本中完成此任务,而不是要求 sed 语句重新格式化日期,但我 运行 正在处理可能需要不同问题的其他问题,所以我现在坚持这样做。

如有任何帮助,我们将不胜感激!谢谢!


回答: 我在脚本的最后一行有一个 ,这就是额外输出的原因。

而不是 awk,这是一个(几乎)纯粹的 Bash 解决方案:

#!/bin/bash

# Regex for time string
re='[0-9]{4}-[0-9]{2}-[0-9]{2} ([0-9]{2}:){2}[0-9]{2}'

# Current time, in seconds since epoch
now=$(date +%s)

while IFS= read -r line; do

    # Match time string
    [[ $line =~ $re ]]
    time_string="${BASH_REMATCH[0]}"

    # Convert time string to seconds since epoch
    time_secs=$(date -d "$time_string" +%s)

    # If time is in the future, print line
    if (( time_secs > now )); then
        echo "$line"
    fi

done < <(grep 'pattern' "")

这利用 Coreutils date 格式将日期转换为纪元以来的秒数,以便于比较两个日期:

$ date
Fri, Jan 22, 2016 11:23:59 PM
$ date +%s
1453523046

-d参数以字符串作为输入:

$ date -d '2016-01-22 10:03:41' +%s
1453475021

该脚本执行以下操作:

  • 使用 grep 过滤输入文件(对于包含通用 pattern 的行,但可以是任何内容)
  • 循环包含pattern
  • 的行
  • 用匹配 date/time 字符串 yyyy-mm-dd hh:mm:ss 的正则表达式匹配该行并提取匹配项
  • 将时间字符串转换为纪元以来的秒数
  • 将该值与 $now 中的时间进行比较,这是自纪元
  • 以来的当前 date/time 秒数
  • 如果日志文件中的时间是未来的时间,则打印行

对于这样的示例输入文件

text 2016-01-22 10:03:41 with time in the past
more text 2016-01-22 10:03:41 matching pattern but in the past
other text 2017-01-22 10:03:41 in the future matching pattern
some text 2017-01-23 10:03:41 in the future but not matching
blahblah 2022-02-22 22:22:22 pattern and also in the future

结果是

$ date
Fri, Jan 22, 2016 11:36:54 PM
$ ./future_time logfile
other text 2017-01-22 10:03:41 in the future matching pattern
blahblah 2022-02-22 22:22:22 pattern and also in the future

这就是我现在的工作。它适用于几种不同的日期格式以及不仅仅具有日期和时间的实际文件。它适用的默认格式是 yyyy/mm/dd,但如果需要,它需要一个参数来指定 mm/dd/yyyy 格式。

BEGIN { tsec=systime(); dtstr=""; dt[1]="" } /.*[0-9][0-9]:[0-9][0-9]:[0-9][0-9]/ { 
cur=[=10=]

if ( fm=="mdy" ) {
    match([=10=],/[0-1][1-9][-_\/][0-3][0-9][-_\/]20[1-9][0-9]/)        # mm dd yyyy
    section=substr([=10=],RSTART,RLENGTH)
    split(section, dt, "[-_//]")
    dtstr=dt[3] " " dt[1] " " dt[2]
    gsub(/[0-1][1-9][-\/][0-3][0-9][-\/]20[1-9][0-9]/, dtstr, cur)
}

gsub(/[-_:/,]/, " ", cur)
match(cur,/20[1-9][0-9] [0-1][1-9] [0-3][0-9][[:space:] ]*[0-2][0-9] [0-6][0-9] [0-6][0-9]/)
arr=mktime(substr(cur,RSTART,RLENGTH))

if ( tsec < arr)
    print [=10=]
}

我会在找到更多格式后添加更多格式选项,但这适用于我目前测试过的所有不同文件。如果他们有 mm/dd/yyyy 格式,你可以用:

来调用它
gawk -f stime fm=mdy filename

我计划添加一个选项来指定您想要查看的时间 window,但这是一个很好的开始。再次感谢你们,这将大大简化工作中的一些任务(我基本上必须检索大量数据,通常在时间压力下视情况而定)。