使用Awk删除最早重复行并保留最新重复行,同时删除被删重复行的上一行
嗨,我来帮你解决这个问题!你需要保留每个命令最后出现的条目(包括对应的时间戳行),删除更早的重复项,这和常见的保留最早重复项的需求正好相反,我们可以用 Awk 来实现这个逻辑。
情况1:每行格式为 #时间戳 命令(如你的输入示例)
如果你的输入每行是时间戳加命令的组合,比如:
#1655636921 cd #1655636926 history #1655637510 history #1655637934 ls #1655637934 ls #1655638524 cd #1655638927 ls #1655638928 history
可以用这个 Awk 脚本,它会保留每个命令最后出现的行,并且保持最后出现的顺序:
awk ' { # 提取命令部分(时间戳后的内容) cmd = substr($0, index($0, " ") + 1) # 如果命令已存在,从顺序列表中移除旧条目 if (cmd in latest) { for (i = 1; i <= count; i++) { if (order[i] == cmd) { for (j = i; j < count; j++) { order[j] = order[j+1] } count-- break } } } # 将命令添加到顺序末尾,更新最新行记录 order[++count] = cmd latest[cmd] = $0 } END { # 按最后出现的顺序输出结果 for (i = 1; i <= count; i++) { print latest[order[i]] } } ' your_input_file.txt
脚本解释:
substr($0, index($0, " ") + 1):从每行中提取命令部分(比如从#1655636921 cd中取出cd)latest[cmd] = $0:用数组记录每个命令对应的最新行,遇到重复命令就覆盖旧值order数组用来维护命令最后出现的顺序:如果命令已经存在,就把它从原来的位置移到数组末尾,确保输出顺序和最后出现的先后一致- END 块遍历顺序数组,输出对应的最新行,正好得到你想要的结果。
情况2:输入是成对行(奇数行是时间戳,偶数行是命令)
如果你的输入是奇数行仅为时间戳(#xxxxxx),偶数行是命令,比如:
#1655636921 cd #1655636926 history #1655637510 history #1655637934 ls #1655637934 ls #1655638524 cd #1655638927 ls #1655638928 history
可以用这个脚本,它会成对删除旧的重复项:
awk ' # 处理奇数行(时间戳行) NR % 2 == 1 { timestamp = $0 next } # 处理偶数行(命令行) { cmd = $0 # 如果命令已存在,删除旧的时间戳和命令行记录 if (cmd in cmd_map) { delete lines[cmd_map[cmd] - 1] delete lines[cmd_map[cmd]] } # 更新命令对应的最新行号 cmd_map[cmd] = NR # 保存当前时间戳和命令行 lines[NR - 1] = timestamp lines[NR] = cmd } END { # 按行号顺序输出保留的行 for (i = 1; i <= NR; i++) { if (i in lines) { print lines[i] } } } ' your_input_file.txt
脚本解释:
NR % 2 == 1:识别奇数行,保存时间戳后直接处理下一行(命令行)- 遇到重复命令时,删除之前保存的对应时间戳行和命令行
cmd_map记录每个命令对应的最新命令行行号,lines数组保存所有需要保留的行- END 块按行号顺序输出,保证结果的顺序正确
和你提到的 awk '!visited[$0]++' 的区别
你说的这个脚本是通过标记每行是否已访问,第一次出现时保留,后续重复行跳过,所以它保留的是最早的重复行。而我们的脚本是通过覆盖最新记录或删除旧记录的方式,实现保留最新的重复行,正好满足你的需求。
内容的提问来源于stack exchange,提问作者AISK
相关产品推荐
相关产品推荐

