如何用awk获取文件中标识符对应的使用行范围?
嘿,针对你这种百万行级别的大文件处理需求,用逐个grep的方式确实效率太低了——毕竟每次grep都要扫一遍文件,反复IO开销巨大。我给你两个高效的awk方案,单遍扫描就能搞定,完美适配大文件场景:
情况1:相同标识符的行是连续的(你的示例就是这种)
这种情况最简单,只需要一次遍历就能记录每个标识符的起止行号:
awk '{ current_id = $4 if (current_id != prev_id) { # 输出上一个标识符的范围(如果不是第一个) if (prev_id != "") { print prev_id, start "-" (NR - 1) } # 初始化当前标识符的起始行号 start = NR prev_id = current_id } } END { # 处理最后一个标识符 print prev_id, start "-" NR }' file1.txt
运行效果(针对你的示例输入)
输出结果会是:
KK-LK 1-2 CK-LK,LK 3-3 JK@ 4-6 JK2 7-7
为什么高效?
这个脚本只扫描文件一次,不管你的文件有几百万行,都是从头到尾读一遍就完成,完全避免了多次grep重复扫文件的开销,速度快到飞起。
情况2:相同标识符的行不连续(如果你的文件里有分散的同标识符行)
如果你的文件中同一个标识符的行不是连续的,比如JK@出现在第4、6、8行,那上面的脚本就不适用了。这时候可以用下面的脚本,记录每个标识符的所有连续行区间:
awk '{ current_id = $4 if (current_id in id_ranges) { # 获取当前标识符的最后一个区间的结束行号 last_range_end = id_ranges[current_id][length(id_ranges[current_id])] # 如果当前行是上一行的连续行,更新区间结束行号 if (NR == last_range_end + 1) { id_ranges[current_id][length(id_ranges[current_id])] = NR } else { # 不连续的话,新增一个区间的起始行号 id_ranges[current_id][length(id_ranges[current_id]) + 1] = NR } } else { # 第一次遇到这个标识符,初始化第一个区间的起始行号 id_ranges[current_id][1] = NR } } END { # 遍历所有标识符,输出对应的行区间 for (id in id_ranges) { printf "%s ", id range_count = length(id_ranges[id]) for (i = 1; i <= range_count; i += 2) { start = id_ranges[id][i] # 如果是单个行,结束行等于起始行;否则取下一个元素作为结束行 end = (i+1 <= range_count) ? id_ranges[id][i+1] : start printf "%d-%d", start, end # 不是最后一个区间的话,加逗号分隔 if (i < range_count - 1) printf "," } print "" } }' file1.txt
比如如果你的文件里JK@出现在第4、6、8行,这个脚本会输出:JK@ 4-4,6-6,8-8
额外优化
如果需要对输出的标识符进行排序,只需要在脚本后面加个| sort就行,比如:
awk '...' file1.txt | sort
这样输出就会按标识符的字典序排列啦~
内容的提问来源于stack exchange,提问作者bapors
相关产品推荐
相关产品推荐

