使用 awk 转换时间戳时日期无效

使用 awk 转换时间戳时日期无效

我正在尝试找到一种将日志文件中的时间戳转换为 Unix 时间戳的方法。到目前为止我想出的命令如下:

awk -F'[' '{ print $2}' | awk -F']' '{cmd ="date \"+%s\" -d \""$1"\""; cmd | getline var; print var $2; close(cmd)}'

当原始时间戳包含 UTC 作为时区或未指定时区时,该命令有效。但是,当时区是其他值时,它会失败。例如,这有效:

$ entry="[08-May-2020 15:40:32 UTC] PHP Warning:  Illegal string offset 'ID' in /home/example/public_html/wp-content/themes/example/functions.php on line 1290"
$ echo "$entry" | awk -F'[' '{ print $2}' | awk -F']' '{cmd="date \"+%s\" -d \""$1"\""; cmd | getline var; print var $2; close(cmd)}'
1588952432 PHP Warning:  Illegal string offset 'ID' in /home/example/public_html/wp-content/themes/example/functions.php on line 1290

该命令在未指定时区时也有效:

$ entry="[08-May-2020 15:40:32] PHP Warning:  Illegal string offset 'ID' in /home/example/public_html/wp-content/themes/example/functions.php on line 1290"
$ echo "$entry" | awk -F'[' '{ print $2}' | awk -F']' '{cmd="date \"+%s\" -d \""$1"\""; cmd | getline var; print var $2; close(cmd)}'
1588948832 PHP Warning:  Illegal string offset 'ID' in /home/example/public_html/wp-content/themes/example/functions.php on line 1290

但是,当时区是时,Europe/London它会失败:

$ entry="[08-May-2020 15:40:32 Europe/London] PHP Warning:  Illegal string offset 'ID' in /home/example/public_html/wp-content/themes/example/functions.php on line 1290"
$ echo "$entry" | awk -F'[' '{ print $2}' | awk -F']' '{cmd="date \"+%s\" -d \""$1"\""; cmd | getline var; print var $2; close(cmd)}'
date: invalid date ‘08-May-2020 15:40:32 Europe/London’
 PHP Warning:  Illegal string offset 'ID' in /home/example/public_html/wp-content/themes/example/functions.php on line 1290

我真的不知道如何调试 awk 命令。我怀疑它可能不喜欢时区中的斜线,但这只是一个猜测。

答案1

date 接收 TZ 变量(并理解它)的方式非常棘手。该命令的工作原理:

 $ date -d 'TZ="UTC" 08-May-2020 15:40:32' +"%s"
1588952432

以及来自 Olson 数据库的 TZ:

$ date -d 'TZ="Europe/London" 08-May-2020 15:40:32' +"%s" 
1588948832

并注意纪元时间不同15889524321588948832。由于伦敦-1和 UTC 存在一 (1) 小时的差异0

了解格式非常严格,首先是 TZ,所有内容都在单引号内,TZ 值也在双引号内。而且,如此严格,也相当脆弱。

因此,在数组中设置值(假设是 bash、ksh 或 zsh):

entry=( 
        "[08-May-2020 15:40:32 UTC] PHP Warning:  Illegal string offset 'ID' in /home/example/public_html/wp-content/themes/example/functions.php on line 1290"
        "[08-May-2020 15:40:32] PHP Warning:  Illegal string offset 'ID' in /home/example/public_html/wp-content/themes/example/functions.php on line 1290"
        "[08-May-2020 15:40:32 Europe/London] PHP Warning:  Illegal string offset 'ID' in /home/example/public_html/wp-content/themes/example/functions.php on line 1290"
      )

然后,我们可以使用 awk 获取日期中的所有值(请注意,一次调用 awk 时的 FS 不同(从技术上讲:GNU awk 或 nawk),并且由于时间字符串的元素数量不同而使用 split):

printf '%s\n' "${entry[@]}" | awk -F '[][]' '{
    n=split($2, val, / /, sep);
    cmd=sprintf("date +\"%%s\" -d '\''TZ=\"%s\" %s %s'\''",val[3],val[1],val[2]);
    cmd | getline var; close(cmd);
    print "["var"]"$3;
}'
[1588952432] PHP Warning:  Illegal string offset 'ID' in /home/example/public_html/wp-content/themes/example/functions.php on line 1290
[1588952432] PHP Warning:  Illegal string offset 'ID' in /home/example/public_html/wp-content/themes/example/functions.php on line 1290
[1588948832] PHP Warning:  Illegal string offset 'ID' in /home/example/public_html/wp-content/themes/example/functions.php on line 1290

请注意,从技术上讲,一行如下:

if ( (rc=(cmd | getline var)) != 1){ 
     print "error on calling the command date ",rc; exit 
};

会捕获 getline 的一些错误(基本上 getline 无法从命令中获取输出),但 awk 无法报告也无法对命令中的错误号采取行动。如果需要的话,命令有责任中断执行。 awk 唯一做的事情(无论是否有错误)就是将 cmd 的 stderr 输出直接传递到它的 stderr。因此,您将在 awk 的 stderr 上看到该命令引发的任何注释(或错误)。如果需要,请务必检查并处理这些内容。如果没有,输出文件将悄无声息地损坏。你被警告了!。这似乎就是你所要求的。

不,awkdatetime()无法理解 TZ 时间,更不能理解 Olson 数据库中的值。

答案2

@Isaac 走在正确的轨道上,请务必将他的答案保留为已接受的答案,因为它可能适合您的输入,但我会这样做以捕获输入不以预期字符串开头的情况[date+time],调用date失败或失败,因此如果消息文本中时间戳后面getline有 a 则调用成功,因此如果执行的任何部分失败,它会以失败退出状态退出:]

$ cat tst.awk
match($0,/\[[^]]+] /) {
    dt  = substr($0,RSTART+1,20)
    tz  = substr($0,RSTART+22,RLENGTH-24)
    msg = substr($0,RSTART+RLENGTH)

    cmd = sprintf("date -d \047TZ=\"%s\" %s\047 +\047%%s\047", tz, dt)
    cmd | getline secs
    close(cmd)
}
secs == "" {
    printf "%s[%d]: failed to convert: \"%s\"\n", FILENAME, NR, $0 | "cat>&2"
    exit 1
}
{ print secs, msg; secs="" }

例如,给定此输入文件(请注意最后两行 - 第一行在]消息部分中有一个,第二行有一个无效日期):

$ cat file
[08-May-2020 15:40:32 UTC] PHP Warning:  Illegal string offset 'ID' in /home/example/public_html/wp-content/themes/example/functions.php on line 1290"
[08-May-2020 15:40:32] PHP Warning:  Illegal string offset 'ID' in /home/example/public_html/wp-content/themes/example/functions.php on line 1290
[08-May-2020 15:40:32 Europe/London] PHP Warning:  Illegal string offset 'ID' in /home/example/public_html/wp-content/themes/example/functions.php on line 290
[08-May-2020 15:40:32 Europe/London] PHP Warning:  array foo[] is bad, the sky is falling
[08-Bob-2020 15:40:32 Europe/London] PHP Warning:  Illegal string offset 'ID' in /home/example/public_html/wp-content/themes/example/functions.php on line 290

$ awk -f tst.awk file
1588952432 PHP Warning:  Illegal string offset 'ID' in /home/example/public_html/wp-content/themes/example/functions.php on line 1290"
1588952432 PHP Warning:  Illegal string offset 'ID' in /home/example/public_html/wp-content/themes/example/functions.php on line 1290
1588948832 PHP Warning:  Illegal string offset 'ID' in /home/example/public_html/wp-content/themes/example/functions.php on line 290
1588948832 PHP Warning:  array foo[] is bad, the sky is falling
date: invalid date ‘TZ="Europe/London" 08-Bob-2020 15:40:32’
file[5]: failed to convert: "[08-Bob-2020 15:40:32 Europe/London] PHP Warning:  Illegal string offset 'ID' in /home/example/public_html/wp-content/themes/example/functions.php on line 290"

$ echo "$?"
1

以失败状态退出的最后一部分很重要,因此您可以编写这样的脚本,awk 'script' file > tmp && mv tmp file而不必担心 awk 输出会覆盖您的输入文件,即使它失败,或者您可以以其他方式测试结果awk 'script' file

相关内容