我在 CSV 文件中有一些测试结果。第一个字段有时间戳,以毫秒为单位
以下是 CSV 文件中的示例记录:
1628689875326,327,3.1 HTTP Request /api/StaffPortal,200,OK,Concurrency Thread Group SignIn-ThreadStarter 1-5,text,true,,73018,17,17
我想删除加速期间(从开始时间起 5 分钟)和减速期间(最后 5 分钟)的测试结果,并将过滤后的测试结果写入 CSV 文件。
我尝试使用硬编码值
#!/usr/bin/awk -f
BEGIN {
endTime=1628689875326
startTime=1628689875326-300*1000
offset=1628689875326-3900*1000
FS=","
rowCount=0
}
{
if ($1> startTime && $1<offset){
rowCount++
print $0
}
}
答案1
不要使用 shebang 调用 awk,而是使用 shebang 调用 shell,然后调用 awk。看https://stackoverflow.com/a/61002754/1745001为什么。
我会这样做你想做的事:
#!/usr/bin/env bash
awk -F',' '
NR==FNR {
if (NR==2) {
fiveMins = 5*60*1000*1000
begTime = $1 + fiveMins
}
endTime = $1 - fiveMins
next
}
(FNR==1) || ( (begTime <= $1) && ($1 <= endTime) ) {
print
rowCount++
}
END {
print rowCount+0 | "cat>&2"
}
' "$1" "$1"
或者您可以通过使用从最后一行获取时间戳tail
(灵感来自@格林在线的回答) 而不是在 awk 中读取文件两次:
#!/usr/bin/env bash
awk -v lastTime="$(tail -n 1 "$1" | cut -d',' -f1)" -F',' '
NR==2 {
fiveMins = 5*60*1000*1000
begTime = $1 + fiveMins
endTime = lastTime - fiveMins
}
(NR==1) || ( (begTime <= $1) && ($1 <= endTime) ) {
print
rowCount++
}
END {
print rowCount+0 | "cat>&2"
}
' "$1"
无论哪种方式,你都可以将其称为:
scriptname input.csv > output.csv
> output.csv
或者如果您不介意对输出文件名进行硬编码,则在 shell 脚本中的 awk 脚本后面添加。
上面假设您的 CSV 有一个您想要在输出中打印的标题行。
请注意,通过不使用 shebang 调用 awk,我可以使用 shell 来做它最擅长的事情,即调用其他命令并多次使用脚本参数,然后调用 awk 来做它最擅长的事情,即处理文本、引导比其他方式更简洁、更健壮、更高效的脚本。
答案2
硬编码解决方案
在您的硬编码时间脚本中,这是问题所在:
offset
小于startTime
,因此if
条件$1> startTime && $1<offset
能绝不是真实的。
原因是startTime
距离结束还有 5 分钟(5*60
= 300 秒),但offset
距离结束还有 65 分钟(65*60
= 3900 秒)。
解决方案是将初始化程序更改为
startTime=1628689875326-3900*1000*1000
offset=1628689875326-300*1000*1000
这会将相关时间设置为
endTime = 1628689875326
startTime = endTime - (3900*1000*1000) = 1624789875326
5 minutes before end = endTime - (300*1000*1000) = 1628389875326
所以,现在,给定一个像这样的日志文件
1624789875326,327,3.1 HTTP Request /api/StaffPortal,200,OK,Concurrency Thread Group SignIn-ThreadStarter 1-5,text,true,,73018,17,17
1624789875327,327,3.1 HTTP Request /api/StaffPortal,200,OK,Concurrency Thread Group SignIn-ThreadStarter 1-5,text,true,,73018,17,17
1628389875325,327,3.1 HTTP Request /api/StaffPortal,200,OK,Concurrency Thread Group SignIn-ThreadStarter 1-5,text,true,,73018,17,17
1628389875326,327,3.1 HTTP Request /api/StaffPortal,200,OK,Concurrency Thread Group SignIn-ThreadStarter 1-5,text,true,,73018,17,17
1628389875327,327,3.1 HTTP Request /api/StaffPortal,200,OK,Concurrency Thread Group SignIn-ThreadStarter 1-5,text,true,,73018,17,17
1628689875326,327,3.1 HTTP Request /api/StaffPortal,200,OK,Concurrency Thread Group SignIn-ThreadStarter 1-5,text,true,,73018,17,17
跑步
more test.log | ./filter.awk
给出所需的结果
1624789875327,327,3.1 HTTP Request /api/StaffPortal,200,OK,Concurrency Thread Group SignIn-ThreadStarter 1-5,text,true,,73018,17,17
1628389875325,327,3.1 HTTP Request /api/StaffPortal,200,OK,Concurrency Thread Group SignIn-ThreadStarter 1-5,text,true,,73018,17,17
更好的解决方案 - 获取开始时间
然而,进一步检查你的问题表明,有一些事情定义不明确:
在您的脚本中,您的
startTime
可能是过滤器的开始,而不是实际日志的开始。日志的开始时间将提前五分钟,即1624789875326 - 300*1000*1000 = 1624489875326
你说时间以毫秒为单位,但你使用
*1000*1000
,这意味着微秒。
尽管如此,这里有一个解决方案,它将自动查找启动时间(从日志文件的第一行开始)并计算斜坡时间,从而计算过滤器的启动时间。
它还将计算过滤时间的结束(与硬编码脚本相同的方式) -尽管您仍然需要输入结束时间,即日志中的最后一个时间戳。
#!/usr/bin/awk -f
BEGIN {
rampTimeMins=5
rampTimeMillis=rampTimeMins*60*1000
rampTimeMicros=rampTimeMins*60*1000*1000
# Enter the start time
# (not required, if NR conditional is used)
startTime=0
# Calculate Start filter time in microseconds
# (not required, if NR conditional is used)
filterStartTime=startTime+rampTimeMicros
# Enter the end time (required)
endTime=1628689875326
# Calculate End filter time in microseconds
filterEndTime=endTime-rampTimeMicros
FS=","
rowCount=0
}
{
if (NR==1){
startTime=$1
filterStartTime=startTime+rampTimeMicros
}
if ($1> filterStartTime && $1<filterEndTime){
rowCount++
print $0
}
}
END {
print rowCount
}
注1:如果您的 CSV 第一行是标题,则更NR==1
改为NR==2
笔记2:如果你想使用毫秒而不是微秒,只需更改rampTimeMicros
为rampTimeMillis
。
这是一个示例日志文件,其中包含额外的Early time,即日志的实际开始时间(而不是过滤器的开始时间)
1624489875326,327,3.1 HTTP Request /api/StaffPortal,200,OK,Concurrency Thread Group SignIn-ThreadStarter 1-5,text,true,,73018,17,17
1624489875327,327,3.1 HTTP Request /api/StaffPortal,200,OK,Concurrency Thread Group SignIn-ThreadStarter 1-5,text,true,,73018,17,17
1624789875326,327,3.1 HTTP Request /api/StaffPortal,200,OK,Concurrency Thread Group SignIn-ThreadStarter 1-5,text,true,,73018,17,17
1624789875327,327,3.1 HTTP Request /api/StaffPortal,200,OK,Concurrency Thread Group SignIn-ThreadStarter 1-5,text,true,,73018,17,17
1628389875325,327,3.1 HTTP Request /api/StaffPortal,200,OK,Concurrency Thread Group SignIn-ThreadStarter 1-5,text,true,,73018,17,17
1628389875326,327,3.1 HTTP Request /api/StaffPortal,200,OK,Concurrency Thread Group SignIn-ThreadStarter 1-5,text,true,,73018,17,17
1628389875327,327,3.1 HTTP Request /api/StaffPortal,200,OK,Concurrency Thread Group SignIn-ThreadStarter 1-5,text,true,,73018,17,17
1628689875326,327,3.1 HTTP Request /api/StaffPortal,200,OK,Concurrency Thread Group SignIn-ThreadStarter 1-5,text,true,,73018,17,17
与哪个运行
more test.log | ./filter.awk
给出与之前相同的结果
1624789875327,327,3.1 HTTP Request /api/StaffPortal,200,OK,Concurrency Thread Group SignIn-ThreadStarter 1-5,text,true,,73018,17,17
1628389875325,327,3.1 HTTP Request /api/StaffPortal,200,OK,Concurrency Thread Group SignIn-ThreadStarter 1-5,text,true,,73018,17,17
2
但有额外的行数。
最终解决方案 - 获取结束时间
如果你改变线路
endTime=1628689875326
到
"tail -1 test.log | cut -d, -f1" | getline endTime
然后脚本不需要进一步编辑,因为获得了最后的时间戳前脚本运行。制作是通用用途
"tail -1 "ARGV[1]" | cut -d, -f1" | getline endTime
或者更好的是(为了安全起见)将带引号的文件名传递给生成的子 shell,按照艾德的评论:
更改
"tail -1 "ARGV[1]"
为"tail -1 \047"ARGV[1]"\047
,这样您就不会将文件名传递给不带引号的生成的子shell(所有这些都意味着通配,分词,安全等)
其中\047
是单引号(FWIW:\042
是双引号)),所以:
"tail -1 \047"ARGV[1]"\047 | cut -d, -f1" | getline endTime
所以最终的脚本就变成了
#!/usr/bin/awk -f
BEGIN {
rampTimeMins=5
rampTimeMillis=rampTimeMins*60*1000
rampTimeMicros=rampTimeMins*60*1000*1000
# Enter the start time
# (not required, if NR conditional is used)
startTime=0
# Calculate Start filter time in microseconds
# (not required, if NR conditional is used)
filterStartTime=startTime+rampTimeMicros
# Derive the end time
"tail -1 \047"ARGV[1]"\047 | cut -d, -f1" | getline endTime
# Calculate End filter time in microseconds
filterEndTime=endTime-rampTimeMicros
FS=","
rowCount=0
}
{
if (NR==1){
startTime=$1
filterStartTime=startTime+rampTimeMicros
}
if ($1> filterStartTime && $1<filterEndTime){
rowCount++
print $0
}
}
END {
print rowCount
}
注1:如果您的 CSV 第一行是标题,则更NR==1
改为NR==2
使用以下命令运行脚本 ( filter.awk
)
./filter.awk test.log
不是
more test.log | ./filter.awk
参考:
答案3
通过以下修改更新了给定的示例脚本。
- 根据输入文件第二行中的开始时间计算
filterStartTime
和filterEndTime
- 将输出写入文件以供进一步分析
#!/usr/bin/awk -f
BEGIN {
testDurationMins=60
rampTimeMins=5
outputFileName="test-result-filtered-without-rampup.jtl"
rampTimeMillis=rampTimeMins*60*1000
# Enter the start time
# (not required, if NR conditional is used)
startTime=0
# Calculate Start filter time in milliseconds
# (not required, if NR conditional is used)
filterStartTime=startTime+rampTimeMillis
# Filter end time
filterEndTime=0
# Calculate the test duration in milliseconds
testDurationMillis=testDurationMins*60*1000
FS=","
rowCount=0
}
{
if (NR==1){
#Write header to the output file
print > outputFileName
}
if (NR==2){
#Set the filter start and end times
startTime=$1
filterStartTime=startTime+rampTimeMillis
filterEndTime=filterStartTime+testDurationMillis
}
if (NR>2 && $1> filterStartTime && $1<filterEndTime){
rowCount++
print >> outputFileName
}
}
END {
print "" rowCount
}