TranslateProject/translated/tech/20191028 How to remove duplicate lines from files with awk.md

6.7 KiB
Raw Blame History

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

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

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

这篇内容篇幅比较长,如果不想深入探讨或时间有限,可以看总结。

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

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

工作原理

这个脚本维持一个关联数组,index 总数为文件去重后的行数,每个 index 对应的 value 为某行出现的次数。对于文件的每一行,如果这行出现的次数为 0则 value 加 1 并打印这行,否则 value 加 1 不打印这行

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

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

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

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

awkpattern 表达式和一个与之关联的 action 组成

`<pattern/expression> { <action> }`

如果匹配到了 pattern就会执行后面的 action。如果没有 actionawk 默认会 print 输入。

省略 action 等于 {print $0}

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

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

等于这样写:

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

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

为什么不用 uniq 命令?

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

$ cat test.txt
A
A
A
B
B
B
A
A
C
C
C
B
B
A
$ uniq &lt; test.txt
A
B
A
C
B
A

其他方法

使用 sort 命令

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

`sort -u your_file > sorted_deduplicated_file`

使用 cat + sort + cut

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

`cat -n your_file | sort -uk2 | sort -nk1 | cut -f2-`
工作原理

假设我们有下面一个文件

abc
ghi
abc
def
xyz
def
ghi
klm

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

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

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

1       abc
4       def
2       ghi
8       klm
5       xyz

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

1       abc
2       ghi
4       def
5       xyz
8       klm

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

abc
ghi
def
xyz
klm

参考

以上为全文。附上猫照。

Duplicate cat


本文首发在 Lazarus Lazaridis 的 iridakos 博客,遵照 CC BY-NC 4.0 License ,转载已获得作者授权。


via: https://opensource.com/article/19/10/remove-duplicate-lines-files-awk

作者:Lazarus Lazaridis 选题:lujun9972 译者:lxbwolf 校对:校对者ID

本文由 LCTT 原创编译,Linux中国 荣誉推出