我正在尝试使用 awk 过滤日志文件。现在过滤是基于时间的,超出时间范围的日志文件条目将被删除,而在该范围内的条目将被保留。现在,一旦我遇到一个在时间范围内的条目,我就知道所有后续条目也将在该时间范围内。
因此,不需要再进行任何检查,那么 awk 有没有办法干净地完成此操作?我的意思是我可以使用一个标志变量来表示不需要更多检查并打印每一行。但是有没有办法说“只处理所有剩余的行”?
答案1
awk 'flag == 0 || some_test { flag = 1 } flag == 1 { processing }'
这将使用布尔/二进制“标志”来跟踪处理何时可以继续到文件末尾。
第一个块测试数据中可以开始处理的点。事情some_test
应该是你已经存在的测试。它将执行多久flag == 0
。一旦您的测试为真,该标志就会切换为1
,这会禁用您的测试并启用该processing
块。
最后一个块将为从触发您的第一行some_test
到文件末尾的所有行运行。
答案2
我假设您的日志上的时间是按升序排序的。
你的条件“一旦我遇到一个在时间范围内的条目,我知道接下来的所有条目也将在该时间范围内”可以写成:
1
time >= start_of_range , 0 { print }
在哪里:
time
是从正在处理的行中提取时间的字段或表达式。start_of_range
是处理时间范围的最小值。,
以 Awk 理解范围的方式表达 arange
,它将在 the 的左侧第一次,
为 true 时开始,并在 the 的右侧,
为 true 时结束。在这种情况下,从不 (0),这会将右侧的命令应用于所有后续行,直到最后print
。
将其作为 awk 脚本的第一行:
awk '$7 >= "2015-08-12" , 0 { print }'
甚至打印也可以被删除,因为它是真实模式(匹配范围)的默认操作。
awk '$7 >= "2015-08-12" , 0'
2
另一种方法是交换测试并执行以下操作:
awk '$7 < "2015-08-12" {next}
{print}
' file
可以简单地写成:
awk '$7 < "2015-08-12" {next} 1' file
但这将继续评估所有线路的测试。
答案3
1.是的,使用范围带有0
or ""
(= false,从不匹配)结束条件:
awk '<is_within_the_range>, 0'
其中<is_within_the_range>
是你的条件,可以是任何表达式,除了另一个范围。
启动条件将不是第一场比赛后再次评估:
$ seq 1 6 | awk '
function check(){ print "checking", $0; return $1 == 3 }
check(), 0
'
checking 1
checking 2
checking 3
3
4
5
6
2.如果你不喜欢范围,你当然可以像 C 那样不尴尬地完成整个事情,只要条件匹配就显式打印所有行:
seq 1 6 | awk '$1==3 { do print; while (getline > 0) }'
3.另一种解决方案是,根据 POSIX 标准,应该使用常规的、可查找的文件(不是用管道!), 但实际上不起作用对于大多数 awk 实现,将依赖 awk 在退出时将文件指针设置为最后一条记录的末尾,因为所有 POSIX 实用程序都是被要求:
seq 1 6 > file
{ awk '$1 == 3 { print; exit }'; cat; } < file
IMLE 这只适用于Solaris 中的awk
/ nawk
,不适用于gawk
,mawk
或 *BSD 中的“one true awk”。
4.最后,您可以编写自己的状态机(例如,通过设置一个标志然后检查它)——用一种缓慢的高级语言,已经为其提供了一个漂亮的简化界面——但这太愚蠢了,不值得深思。
答案4
一旦文件行包含 PATTERN,该行和所有下一行都会被打印:
awk 'flag || /PATTERN/{flag=1} flag{print $0}' file
如果需要更多处理,您可以用不同的代码替换“print $0”。