怎样使用awk删掉文件中重复的行

系统 Linux
学习怎样使用 awk 的 !visited[$0]++ 在不重新排序或改变原排列顺序的前提下删掉重复的行。

[[285435]]

学习怎样使用 awk 的 !visited[$0]++ 在不重新排序或改变原排列顺序的前提下删掉重复的行。

假设你有一个文本文件,你需要删掉所有重复的行。

TL;DR

要保持原来的排列顺序删掉重复行,使用:

  1. awk '!visited[$0]++' your_file > deduplicated_file

工作原理

这个脚本维护一个关联数组,索引(键)为文件中去重后的行,每个索引对应的值为该行出现的次数。对于文件的每一行,如果这行(之前)出现的次数为 0,则值加 1,并打印这行,否则值加 1,不打印这行。

我之前不熟悉 awk,我想弄清楚这么短小的一个脚本是怎么实现的。我调研了下,下面是调研心得:

  • 这个 awk “脚本” !visited[$0]++ 对输入文件的每一行都执行。
  • visited[] 是一个关联数组(又名映射)类型的变量。awk 会在第一次执行时初始化它,因此我们不需要初始化。
  • $0 变量的值是当前正在被处理的行的内容。
  • visited[$0] 通过与 $0(正在被处理的行)相等的键来访问该映射中的值,即出现次数(我们在下面设置的)。
  • ! 对表示出现次数的值取反:
    • awk 中,任意非零的数或任意非空的字符串的值是 true
    • 变量默认的初始值为空字符串,如果被转换为数字,则为 0。
    • 也就是说:
      • 如果 visited[$0] 的值是一个比 0 大的数,取反后被解析成 false
      • 如果 visited[$0] 的值为等于 0 的数字或空字符串,取反后被解析成 true
    • ++ 表示变量 visited[$0] 的值加 1。
      • 如果该值为空,awk 自动把它转换为 0(数字) 后加 1。
      • 注意:加 1 操作是在我们取到了变量的值之后执行的。

总的来说,整个表达式的意思是:

  • true:如果表示出现次数为 0 或空字符串
  • false:如果出现的次数大于 0

awk模式或表达式和一个与之关联的动作 组成:

  1. <模式/表达式> { <动作> }

如果匹配到了模式,就会执行后面的动作。如果省略动作,awk 默认会打印(print)输入。

省略动作等价于 {print $0}

我们的脚本由一个 awk 表达式语句组成,省略了动作。因此这样写:

  1. awk '!visited[$0]++' your_file > deduplicated_file

等于这样写:

  1. awk '!visited[$0]++ { print $0 }' your_file > deduplicated_file

对于文件的每一行,如果表达式匹配到了,这行内容被打印到输出。否则,不执行动作,不打印任何东西。

为什么不用 uniq 命令?

uniq 命令仅能对相邻的行去重。这是一个示例:

  1. $ cat test.txt
  2. A
  3. A
  4. A
  5. B
  6. B
  7. B
  8. A
  9. A
  10. C
  11. C
  12. C
  13. B
  14. B
  15. A
  16. $ uniq < test.txt
  17. A
  18. B
  19. A
  20. C
  21. B
  22. A

其他方法

使用 sort 命令

我们也可以用下面的 sort 命令来去除重复的行,但是原来的行顺序没有被保留

  1. sort -u your_file > sorted_deduplicated_file

使用 cat + sort + cut

上面的方法会产出一个去重的文件,各行是基于内容进行排序的。通过管道连接命令可以解决这个问题。

  1. cat -n your_file | sort -uk2 | sort -nk1 | cut -f2-

工作原理

假设我们有下面一个文件:

  1. abc
  2. ghi
  3. abc
  4. def
  5. xyz
  6. def
  7. ghi
  8. klm

cat -n test.txt 在每行前面显示序号:

  1. 1       abc
  2. 2       ghi
  3. 3       abc
  4. 4       def
  5. 5       xyz
  6. 6       def
  7. 7       ghi
  8. 8       klm

sort -uk2 基于第二列(k2 选项)进行排序,对于第二列相同的值只保留一次(u 选项):

  1. 1       abc
  2. 4       def
  3. 2       ghi
  4. 8       klm
  5. 5       xyz

sort -nk1 基于第一列排序(k1 选项),把列的值作为数字来处理(-n 选项):

  1. 1       abc
  2. 2       ghi
  3. 4       def
  4. 5       xyz
  5. 8       klm

最后,cut -f2- 从第二列开始打印每一行,直到最后的内容(-f2- 选项:留意 - 后缀,它表示这行后面的内容都包含在内)。

  1. abc
  2. ghi
  3. def
  4. xyz
  5. klm

参考

以上为全文。 

责任编辑:庞桂玉 来源: Linux中国
相关推荐

2009-06-18 09:05:35

Unix文件管理

2010-09-01 16:47:18

SQL删除

2019-08-28 15:43:03

sed命令Linux

2019-12-03 10:00:19

awkLinux循环

2016-08-29 20:51:16

awkLinux开源

2009-04-20 15:54:04

SQL Server重复行

2010-06-28 12:46:09

SQL Server

2016-08-10 16:07:08

awkLinux开源

2016-08-10 11:19:11

awkLinux开源

2016-08-11 09:18:33

awkShellLinux

2016-10-08 20:58:50

awkLinux编写脚本

2010-07-12 09:52:24

删除 SQL Serv

2016-07-29 15:13:00

awk文本处理工具编程

2023-05-29 15:23:37

MySQL数据库函数

2013-03-06 09:41:29

2019-11-26 14:00:58

awkLinux命令

2018-03-28 17:51:24

LinuxUnix

2019-04-12 14:26:17

Linux命令文件

2022-03-20 10:40:11

Linuxawk 脚本

2020-10-15 12:30:37

Python编程语言
点赞
收藏

51CTO技术栈公众号