【问题标题】:AWK matching values in a column and performing calculationAWK 匹配列中的值并执行计算
【发布时间】:2015-04-12 05:56:36
【问题描述】:

我是 AWK 的新手,我正在努力为我的问题找出答案。我有一个包含以下值的平面文件:

403 | SanMateo   | f | 2015-04-09 18:50:24.38
403 | SanMateo   | t | 2015-04-09 18:45:24.36
403 | SanMateo   | t | 2015-04-09 18:40:24.383
403 | SanMateo   | f | 2015-04-09 18:35:24.357
403 | SanMateo   | t | 2015-04-09 18:30:24.355
404 | RedwoodCity| f | 2015-04-09 18:35:50.308
404 | RedwoodCity| t | 2015-04-09 18:30:50.242
404 | RedwoodCity| f | 2015-04-09 18:25:50.245
404 | RedwoodCity| t | 2015-04-09 18:20:50.242
404 | RedwoodCity| f | 2015-04-09 18:15:50.242

我想用 awk 比较当前行的 $1 和下一行的 $1,以及 $3 ~/f/.如果语句为真,则从当前行的 $4 中减去下一行的 $4,并将差值写入当前行的新列,如果为假,则不执行任何操作。我到目前为止是这样的:

awk 'BEGIN {FS="|";} {if (NR $1 ~ NR++ $1 && $3 ~ /f/) subtract = NR $4 - NR++ $4; {print subtract}}' allHealthRecords_Sorted

显然这行不通。有人可以帮忙吗?

【问题讨论】:

  • 不要想着想对下一行做点什么,而是想着想对上一行做点什么。你不能看下一行,因为你还没有读过它,但你已经读过上一行。这样想,解决方案就会来找你。
  • 感谢您的建议,我会牢记这一点。

标签: bash awk gawk mawk


【解决方案1】:

另存为time_diff.awk

BEGIN {FS = "[[:blank:]]*\\|[[:blank:]]*"}

# convert "YYYY-mm-dd HH:MM:SS.fff" to a number
function to_time(timestamp,       fraction) {
    fraction = timestamp
    sub(/\..*$/, "", timestamp)
    gsub(/[-:]/, " ", timestamp)
    sub(/.*\./, "0.", fraction)
    return mktime(timestamp) + fraction
}

# gawk has no builtin abs() function
function abs(val) { 
    return( val < 0 ? -1*val : val) 
}

# add the time diff if the condition is met
NR > 1 {
    diff = 0
    if ($1+0 == key && flag == "f") 
        diff = abs( to_time($4) - to_time(time) )
    print line (diff > 0 ? " | " diff : "")
} 

{
    # remember the previous line's values
    key = $1+0; flag = $3; time = $4; line = $0
}

END {print}

然后

$ gawk -f time_diff.awk file
        403 | SanMateo| f                | 2015-04-09 18:50:24.38 | 300.02
        403 | SanMateo| t                | 2015-04-09 18:45:24.36
        403 | SanMateo| t                | 2015-04-09 18:40:24.383
        403 | SanMateo| f                | 2015-04-09 18:35:24.357 | 300.002
        403 | SanMateo| t                | 2015-04-09 18:30:24.355
        404 | RedwoodCity| f                | 2015-04-09 18:35:50.308 | 300.066
        404 | RedwoodCity| t                | 2015-04-09 18:30:50.242
        404 | RedwoodCity| f                | 2015-04-09 18:25:50.245 | 300.003
        404 | RedwoodCity| t                | 2015-04-09 18:20:50.242
        404 | RedwoodCity| f                | 2015-04-09 18:15:50.242

【讨论】:

  • 我可以在 abs 函数中使用一元 - 运算符:return (val&lt;0 ? -val : val)
【解决方案2】:

您没有显示您的预期输出,因此我们无法对其进行测试,并且 $4 是一个日期,所以请理解您所说的“减去”是什么意思,但这基本上是正确的方法:

$ cat tst.awk         
BEGIN{ FS="[[:space:]]*[|][[:space:]]*"; OFS=" | " }
split(prev,p) { print prev ( ($1==p[1])&&(p[3]=="f") ? OFS p[4] - $4 : "") }
{ prev = $0 }
END { print prev ( ($1==p[1])&&(p[3]=="f") ? OFS p[4] - $4 : "") }

$ awk -f tst.awk file
403 | SanMateo   | f | 2015-04-09 18:50:24.38 | 0
403 | SanMateo   | t | 2015-04-09 18:45:24.36
403 | SanMateo   | t | 2015-04-09 18:40:24.383
403 | SanMateo   | f | 2015-04-09 18:35:24.357 | 0
403 | SanMateo   | t | 2015-04-09 18:30:24.355
404 | RedwoodCity| f | 2015-04-09 18:35:50.308 | 0
404 | RedwoodCity| t | 2015-04-09 18:30:50.242
404 | RedwoodCity| f | 2015-04-09 18:25:50.245 | 0
404 | RedwoodCity| t | 2015-04-09 18:20:50.242
404 | RedwoodCity| f | 2015-04-09 18:15:50.242

即您有 1 行的缓冲区,因此您始终在操作并输出您读取的上一行。

【讨论】:

  • 请原谅我没有显示预期的结果。我想要的是找出每次站点将其状态从 t 更改为 f 时的时间差异,反之亦然。从那里,我可以根据这些时间进行计算并生成报告。我想把信息分成几部分,以便报告的读者容易消化。问题是,平面文件包含每个站点,我想自动化计算并让自动化足够智能,知道什么时候不包含它正在读取的信息。我的空间不够了,我只想说 thnx 4 D hlp。
【解决方案3】:

在 BEGIN 操作中,使用 getline 读取第一行并保存 $1 和 $4 的值。

在此后的每一行中,将 $1 与前一行中保存的值进行比较。如果它们相同,并且$3 ~ /f/,请执行所需的过程。然后将 $1 和 $4 的值保存到下一行。

这应该足以让您入门。如果您在编写代码时遇到问题,请回来提出更多问题。

【讨论】:

  • 没有。不要遵循这个建议,因为在 BEGIN 中使用 getline 是完全没有必要的,并且充满了警告,请参阅awk.info/?tip/getline
  • 好吧,我不认为这是特别错误的,但是如果您愿意,您可以从第一行中读取并存储 NR == 1 模式中的值。算法的其余部分,在第一行之后的剩余行中,是正确的。
  • 告诉某人为此使用 getline 与告诉他们使用 head 获取变量中的第一行然后 tail 将管道中的其余部分用于 awk 一样错误,但是 getline有更多阴险的警告/陷阱。对于一些非常具体的问题,getline 是正确的解决方案,因为其他所有东西都不要使用它,而是让 awk 做它自然会做的事情。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多