awk 加入 getline 后跳过记录,末尾重复旧值
getline 额外消费主输入,正常规则不会再次处理该记录;按逐行或两行分组需求重写,并区分 EOF 与读取错误。
输入有三行,awk 动作里加入 getline next_line 后,某一行没有进入正常规则,最后还重复打印了前一行的内容。这通常是把 getline 当成了“不移动输入位置的预览”:它实际上会消费下一条记录。
下面限定 GNU awk(命令名 gawk),使用默认换行记录分隔符、单个普通文本输入文件,不启用输入重试扩展。多行记录格式需要按实际 RS 判断,不能一律按物理行计数。
多读一次,主循环就少见一条记录
假设 ./records.txt 内容如下:
alpha
beta
gamma
这段是问题写法,只读文件并输出文本:
gawk '{ getline next_line; print next_line; print $0 }' ./records.txt
预期输出顺序是 beta、alpha、beta、gamma,而不是依次查看三个位置的“下一行”。
第一次执行动作时,主循环已经读到 alpha,它保存在 $0。getline next_line 从同一主输入再读走 beta,把它赋给变量,但不改当前 $0。动作结束后,主循环继续取得的记录便是 gamma;beta 不会再单独进入整套规则。
第二次执行动作时已经没有下一条记录,getline 返回 0。继续无条件打印 next_line,就会输出上次读到的旧值。要判断是否获得新记录,应检查返回状态,不能靠变量是否为空:空行也是一次成功读取。
NR、FNR 在成功读入主输入记录时增加,是读取结果的计数,不是控制文件当前位置的索引。把 NR 减一不会把已经消费的记录放回去。
只需要逐行处理时,去掉多余的 getline
如果每行都应经过同样的处理,直接使用 awk 自带的主循环:
gawk '{ print NR, $0 }' ./records.txt
样例预期得到 1 alpha、2 beta、3 gamma。把实际处理表达式放进这个动作即可,不需要先写一层 while (getline ...)。额外读取的记录不会从第一条规则重新开始;后面还有其他模式和动作时,这种遗漏更容易被隐藏。
需要相邻两行比较时,可以保留上一条记录,等主循环读到当前记录再比较。例如:
gawk '
NR > 1 { print previous, $0 }
{ previous = $0 }
' ./records.txt
这里只输出 alpha beta 和 beta gamma,最后一条没有虚构的后继。它表达的是相邻记录配对,不是交换两行;应先明确业务要求,不能把两种输出混用。
确实按两行一组时,区分成功、末尾和错误
如果目标就是交换每一对相邻行,才需要主动取走第二条。下面保留奇数条输入的最后一条,并在读取错误时返回非零状态:
gawk '
{
result = (getline next_line)
if (result == 1) {
print next_line
print $0
} else if (result == 0) {
print $0
} else {
print "getline failed: " ERRNO > "/dev/stderr"
exit 1
}
}' ./records.txt
样例预期输出 beta、alpha、gamma。普通 GNU awk 输入中,1 表示读到记录,0 表示输入结束,负数表示错误。不能写成 if (getline next_line) 就认为只有成功才进入分支,因为负数在条件中也是真。
这段只处理一个文件。把多个文件直接加到命令后面,主输入会按文件继续读取,可能把前一文件的末行与下一文件的首行配成一对;各文件必须独立配对时,应分别处理或另行设计文件边界逻辑。
getline next_line 不重新切分字段,当前 $0、$1 等字段和 NF 保持原记录。去掉变量改成裸 getline 则会替换当前记录并更新字段,不能作为等价修复。
用奇数条输入检查末尾处理
验证至少包括空文件、一条、两条、三条记录以及空行。逐行处理应没有漏项;两行交换应保留奇数末行,而且读取失败不能被当作正常结束。空行应按记录参与处理,不应因内容为空被跳过。
以上命令只向标准输出写结果。真正保存转换结果时,使用单独的新输出位置并检查命令状态,不要重定向覆盖输入文件。发生读取错误时,已输出的前缀不代表完整结果,应丢弃候选输出并保留原文件。上线前保存原脚本和输入;回退恢复原版本,不能靠调整计数器补回已经漏处理的业务记录。


