我有一个日志文件,名称为listlog,内容为:
2021-08-12 16:09:17 textsp sdgg
reponse:success
prams:invalid
line 3
2021-08-12 16:10:17 textdfdfdlfs sfdfs
reponse: failed
prams:valid
line 3
我想过滤 t1 和 t2 之间记录的日志并打印连续 2 行的日志行。我使用 awk 和 if 条件和 grep 编写。但我得到了错误:
这是我想要过滤并写入新文件的内容,假设 t1 < (16:09:17, 16:19:17) < t2 :
2021-08-12 16:09:17 textsp sdgg
reponse:success
prams:invalid
2021-08-12 16:10:17 textdfdfdlfs sfdfs
reponse: failed
prams:valid
这是我写的 awk 脚本:
t1=$(date -d "$min_change minutes ago" +"%F %T")
t2=$(date +"%F %T")
cat $listlog | awk -v t1="$t1" -v t2="$t2" -v listlog="$listlog" '{
if (match($0 ~ /^[0-9]{4}(-[0-9]{2}){2}))
{
if ( $0 > t1 && $0 < t2 | $0 ~ t2 )
{
grep -A 2 $0 $listlog >> /tmp/temp.txt
}
}
}'
这是我得到的错误:
awk: cmd. line:2: if (match($0 ~ /^[0-9]{4}(-[0-9]{2}){2}))
awk: cmd. line:2: ^ unterminated regexp
awk: cmd. line:3: if (match($0 ~ /^[0-9]{4}(-[0-9]{2}){2}))
awk: cmd. line:3: ^ unexpected newline or end of string
awk: cmd. line:4: if ( $0 > t1 && $0 < t2 | $0 ~ t2 )
awk: cmd. line:4: ^ syntax error.
awk: cmd. line:5: if ( $0 > t1 && $0 < t2 | $0 ~ t2 )
awk: cmd. line:5: ^ unexpected newline or end of string
awk: cmd. line:6: grep -A 2 $0 $listlog >> /temp/temp.txt
awk: cmd. line:6: ^ syntax error
awk: cmd. line:7: grep -A 2 $0 $listlog >> /temp/temp.txt.
awk: cmd. line:7: ^ unexpected newline or end of string
答案1
甚至很难开始描述脚本的问题所在,但这里总结了最重要的几点:
你写的不是
awk
剧本。它甚至不是awk
嵌入 shell 脚本中的脚本(通常完全没问题)。这是一些 shell 代码的奇怪混合,并尝试在awk
其中包含 shell 代码(awk
不解释或运行 shell 命令)。无论如何,你不能
grep
在内部像这样运行awk
,除非将其分叉为外部进程。并且不需要这样做,因为awk
可以进行正则表达式模式匹配。您的任何代码中都没有缩进,这使得阅读起来非常困难。
更新:我刚刚编辑了你的问题以重新格式化它,结果发现你的代码中确实有一些缩进。但是您使用 HTML 标签对其进行格式化,
<br/>
而不是使用 Markdown 编辑器的内置文本格式化功能。第 1 点和第 2 点可能是您发布问题后 7 小时内没有得到答案甚至评论的原因。任何读者(至少是我的)的最初反应都是“WTF?这毫无意义。我什至不知道从哪里开始......通过”。
您无法在 shell 中或
awk
尝试执行日期比较的方式进行日期比较。要进行大于或小于等数字比较,您需要将日期转换为数字(即unixtime_t
格式,自“纪元”以来的秒数,即UTC 1970年1月1日午夜)。可以在 中执行您想要的操作
awk
,但会比必要的更困难,因为awk
没有任何内置的日期转换函数。在 中会容易得多perl
,它确实有很好的日期和时间库函数。这对您来说是一件好事,因为您编写的脚本perl
在风格上比代码更接近于代码awk
。
这是一种相当简单、直接的方法,可以在 Perl 中完成您想要的操作。它需要命令行参数(使用 perl 的获取选择::标准库模块)。 Getopt::Std
是一个核心 Perl 库模块,包含在 Perl 中。
参数可以按任何顺序提供。所有选项参数( 、 和 )都不-m
是-n
强制性-s
的,如果命令行上未提供,它们都有默认值。任何未处理的参数Getopt::Std
都被视为输入文件名。该脚本可以处理标准输入以及任何输入文件名,因此您可以将日志文件通过管道传输到其中。
该脚本还使用日期::解析来自 perl 的模块时间::日期将日期字符串转换为自纪元以来的秒数的集合。
Date::Parse
perl 不包含它,需要安装。如果您使用 Debian(或 Ubuntu 或 Mint 或其他 Debian 相关发行版),您可以使用sudo apt-get install libtimedate-perl
.大多数其他发行版也会将其打包。 Centos 包是perl-TimeDate-2.30-2对于 Centos 7,或perl-TimeDate-2.30-15对于 Centos 8。否则,您可以使用cpan
.
#!/usr/bin/perl
use strict;
use Getopt::Std;
use Date::Parse;
my %opts;
getopt('m:s:n:', \%opts); # -m minutes, -s start time, -n number of lines
my $st = $opts{s} || -1; # start time, default now.
my $min = $opts{m} || 5; # number of minutes after $st to match, default 5.
my $nl = $opts{n} || 2; # No. of extra lines to print after match, default 2.
if ($st == -1) {
$st = time(); # now
} else {
$st = str2time($st); # convert time string to seconds since epoch
};
my $et = $st + $min * 60; # calculate ending time
my $p = -1; # input lines are printed only when $p > -1
# main loop, read and process all input file(s).
while (<>) {
if (m/^(\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d)/) {
my $t = str2time($1);
$p = $nl if ($t >= $st && $t <= $et);
};
if ($p > -1) { print; $p-- };
};
将其另存为,例如extract.pl
,使其可执行chmod +x extract.pl
并像这样运行它:
$ ./extract.pl -s "2021-08-12 16:09:00" -n 2 -m 5 listlog
2021-08-12 16:09:17 textsp sdgg
reponse:success
prams:invalid
2021-08-12 16:10:17 textdfdfdlfs sfdfs
reponse: failed
prams:valid
如果需要,您可以重定向输出,例如通过附加>>/tmp/temp.txt
到命令行末尾。