首先使用awk处理最后一行


11

我有一个要awk基于最后一个数据点进行规范化的数据文件。因此,我想先访问最后一个数据点,以规范化数据,然后正常处理。

以下方法使用tac两次即可完成工作,但可能比必要的方法复杂。

$ cat file
0 5
1 2
2 3
3 4
$ tac file | awk 'NR==1{norm=$2} {print $1, $2/norm}' | tac
0 1.25
1 0.5
2 0.75
3 1

我的问题是:仅使用awk是否可以获得上述结果?

我认为答案是“不,awk逐行扫描文件”,但我愿意提出替代建议。

Answers:


5

您可以在awk中将其作为两阶段解决方案来完成:

awk 'FNR == NR { n = $2; next } { print $1, $2/n }' infile infile

如果您的awk版本支持ENDFILE块(例如GNU awk 4+),则可以这样做:

awk 'ENDFILE { n = $2 } FNR != NR { print $1, $2/n }' infile infile

请注意,到seek文件末尾先查看camh的答案效率更高。

说明

第一个示例通过记住前面的示例来工作$2,即仅当本地行计数器(FNR)等于全局行计数器(NR)时才进行评估。该next命令跳到下一行,在这种情况下,它确保仅在解析第二个参数时才评估最后一个块。

第二个示例具有类似的逻辑,但是利用了ENDFILE块,该块在到达输入文件的末尾时进行评估。


第一个示例可以正常工作,第二个则不能$ awk --version GNU Awk 3.1.8。您能否对如何处理两个输入文件以及如何处理添加一个非常小的解释next
Bernhard

1
@Bernhard:见编辑
托尔

6

如果您的数据源是可以多次读取的文件(即,它不是流),则应首先使用tail(1)来从最后一行获取所需的数据,然后将其传递给awk以对其进行顺序处理。tail将寻求到文件的末尾以读取最后一行,而无需先读取所有数据。

awk -v norm=$(tail -n 1 file | cut -d' ' -f2) '{print $1, $2/norm}' file

对于大型文件而言,这将是一个巨大的胜利,因为整个文件将无法容纳在缓冲区缓存中(这意味着需要从磁盘读取两次,每次通过一次),并且由于不需要扫描而在较小程度上有所帮助输入到最后一行。较小的文件可能与两次通过方法没有太大区别。


3

您可以将它们加载到数组中并向后读取:

awk '{x[i++]=$0} END{for (j=i-1; j>=0;) print x[j--] }'

您可以更有效地执行此操作,但是这种情况说明了为什么awk不是正确的工具。继续使用tac可用的工具,GNU tac通常是完成这项工作的各种工具中最快的。


我同意,使用for-loops awk不是解决方案。
Bernhard
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.