如何在awk中选择日期范围

我们正在制作一个实用程序,用于SSH到不同的服务器并收集所有错误日志,并将其发送给相关团队,该实用程序将使用awk收集日志文件并进行过滤。例如

cat /app1/apache/tomcat7/logs/catalina.out | awk '$0>=from&&$0<=to' from="2019-02-01 12:00" to="2019-11-19 04:50"

我们正在将上次加载的日期保存在数据库中,并使用该日期作为下一次运行的日期。

问题

指定的

awk日期范围似乎仅适用于yyyy-mm-dd HH:MM日期格式。我们的日志文件具有不同的日期格式。例如

EEE MMM dd yy HH:mm
EEE MMM dd HH:mm
yyyy-MM-dd hh:mm
dd MMM yyyy HH:mm:ss
dd MMM yyyy HH:mm:ss

问题

如何编写awk日期过滤器以使用日志文件中使用的任何日期格式?

我们无法在服务器上使用perl / python。要求为此仅使用cat / awk / grep。

样本输入:

Sat Nov 02 13:07:48.005 2019 NA for id 536870914 in form Request
Tue Nov 05 13:07:48.009 2019 NA for id 536870914 in form Request
Sun Nov 10 16:29:22.122 2019 ERROR (1587): Unknown field ;  at position 177 (category)
Mon Nov 11 16:29:22.125 2019 ERROR (1587): Unknown field ;  at position 174 (category)
Tue Nov 12 07:59:48.751 2019 ERROR (1587): Unknown field ;  at position 177 (category)
Thu Nov 14 10:07:41.792 2019 ERROR (1587): Unknown field ;  at position 177 (category)
Sun Nov 17 08:45:22.210 2019 ERROR (1587): Unknown field ;  at position 174 (category)

命令和过滤器:

cat error.log |awk '$0>=from&&$0<=to' from="Nov 16 10:58" to="Nov 19 04:50"

预期输出:

Sun Nov 17 08:45:22.210 2019 ERROR (1587): Unknown field ;  at position 174 (category)
jiulongrushui 回答:如何在awk中选择日期范围

答案是awk不知道日期是什么。 Awk知道数字和字符串,只能比较它们。因此,当您要选择日期和时间时,必须确保比较的日期格式可以可排序,并且其中有很多格式:

| type       | example                   | sortable |
|------------+---------------------------+----------|
| ISO-8601   | 2019-11-19T10:05:15       | string   |
| RFC-2822   | Tue,19 Nov 2019 10:05:15 | not      |
| RFC-3339   | 2019-11-19 10:05:15       | string   |
| Unix epoch | 1574157915                | numeric  |
| AM/PM      | 2019-11-19 10:05:15 am    | not      |
| MM/DD/YYYY | 11/19/2019 10:05:15       | not      |
| DD/MM/YYYY | 19/11/2019 10:05:15       | not      |

因此,您必须主要使用字符串操作将不可排序的格式转换为可排序的格式。一个可以实现您想要的功能的awk模板程序记录在这里:

# function to convert a string into a sortable format
function convert_date(str) {
    return sortable_date
}
# function to extract the date from the record
function extract_date(str) {
    return extracted_date
}
# convert the range
(FNR==1) { t1 = convert_date(begin); t2 = convert_date(end) }
# extract the date from the record
{ date_string = extract_date($0) }
# convert the date of the record
{ t = convert_date(date_string) }
# make the selection
(t1 <= t && t < t2) { print }

在大多数情况下,可以大大减少此程序。如果以上内容存储在extract_date_range.awk中,则可以将其运行为:

$ awk -f extract_date_range.awk begin="date-in-know-format" end="date-in-known-format" logfile

注意:以上假设单行登录。稍作改动,您就可以处理多行日志条目。


在原始问题中,提出了以下格式:

EEE MMM dd yy HH:mm         # not sortable
EEE MMM dd HH:mm            # not sortable
yyyy-MM-dd hh:mm            # sortable
dd MMM yyyy HH:mm:ss        # not sortable

从以上所述,除第二种格式外,其他所有格式都可以轻松转换为可排序格式。第二种格式错过了Year,因此我们必须利用星期几来进行详尽的检查。这是极其困难的,而且永远不会100%证明其安全性。

除了第二种格式,我们可以编写以下函数:

BEGIN {
    datefmt1="^[a-Z][a-Z][a-Z] [a-Z][a-Z][a-Z] [0-9][0-9] [0-9][0-9] [0-9][0-9]:[0-9][0-9]"
    datefmt3="^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:[0-9][0-9]"
    datefmt4="^[0-9][0-9] [a-Z][a-Z][a-Z] [0-9][0-9][0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9]"
}
# convert the range
(FNR==1) { t1 = convert_date(begin); t2 = convert_date(end) }
# extract the date from the record
{ date_string = extract_date($0) }
# skip if date string is empty
(date_string == "") { next }
# convert the date of the record
{ t = convert_date(date_string) }
# make the selection
(t1 <= t && t < t2) { print }

# function to extract the date from the record
function extract_date(str,date_string) {
    date_string=""
    if (match(datefmt1,str)) { date_string=substr(str,RSTART,RLENGTH) }
    else if (match(datefmt3,RLENGTH) }
    else if (match(datefmt4,RLENGTH) }
    return date_string
}
# function to convert a string into a sortable format
# converts it in the format YYYYMMDDhhmmss
function convert_date(str,a,fmt,YYYY,MM,DD,T,sortable_date) {
    sortable_date=""
    if (match(datefmt1,str)) { 
        split(str,"[ ]")
        YYYY=(a[4] < 70 ? "19" : "20")a[4]
        MM=get_month(a[2]); DD=a[3]
        T=a[5]; gsub(/[^0-9]/,T)"00"
        sortable_date = YYYY MM DD T
    }
    else if (match(datefmt3,str)) { 
        sortable_date = str"00"
        gsub(/[^0-9]/,sortable_date)
    }
    else if (match(datefmt4,"[ ]")
        YYYY=a[3]
        MM=get_month(a[2]); DD=a[1]
        T=a[4]; gsub(/[^0-9]/,T)"00"
        sortable_date = YYYY MM DD T
    }
    return sortable_date
}
# function to convert Jan->01,Feb->02,Mar->03 ... Dec->12
function get_month(str) {
   return sprintf("%02d",(match("JanFebMarAprMayJunJulAugSepOctNovDec",str)+2)/3)
}

ISO 8601 was published on 06/05/88 and most recently amended on 12/01/04.

,

从技术上讲,您可以从awk调用date,但是这种方法的帮助有限:

  • awk调用日期(或其他程序)非常昂贵(启动进程等)。如果日志文件很大,处理将会很慢
  • 看起来您正在寻找可以在远程服务器上执行的“单一代码”。处理多种格式将需要多种格式。

考虑解除这些限制-以下一项(或多项):

  • 将完整的日志文件传输到能够在本地运行过滤器并支持多个日期的计算机。
  • 发送更复杂的脚本以在每个远程服务器上执行扫描。这将需要更多的设置,但是将不需要通过ssh传输完整的日志文件。
  • 自定义日志文件-catalina,Apache等,可让您控制日期格式。使它们全部产生YYYY-MM-DD HH:MM或类似的结果。
本文链接:https://www.f2er.com/3076628.html

大家都在问