Contents

如何按列执行Uniq命令

1. 概述

我们知道*uniq *命令是一个方便的实用程序,可以从输入中删除重复的相邻行。

但是,当我们处理基于列的输入文件(例如 CSV 文件)时,我们可能希望删除包含重复列的行。

uniq命令在这种情况下无法帮助我们,因为它仅在整行相同时才删除行。

在本教程中,我们将学习如何对列而不是整行执行“ uniq ”操作。

2. 问题介绍

首先,我们以创建一个 CSV 文件price.csv 为例:

Product,Price,Update
Monitor,218,2019-01-01
Keybord,20,2019-02-02
Wireless Mouse,25,2019-02-02
Keybord,22,2019-03-02
Wireless Mouse,30,2019-03-02
Keybord,18,2019-04-02
Monitor,208,2019-02-01
Keybord,25,2019-05-02
Monitor,230,2019-03-01

在 price.csv文件中,我们有三列,每行记录每个产品的价格更新时间。

我们想删除包含重复Product的行。

因此,我们将对Product列进行“ uniq ”操作。

让我们看一下解决问题的两种方法并比较这两种方法:  sort和 awk

3. 使用sort

排序 命令可以按特定字段对行进行排序,并从排序结果中删除重复项。对于重复项,将仅保留第一个实例。

在本节中,我们将使用sort命令解决问题。

在 price.csv文件中,我们注意到第一行是标题行,我们在对数据进行排序时不希望标题参与排序。

为此,我们可以使用tail 命令剪切第一行并将其余部分通过管道传递给sort命令:

$ tail -n+2 price.csv | sort -u -t, -k1,1
Keybord,20,2019-02-02
Monitor,218,2019-01-01
Wireless Mouse,25,2019-02-02

让我们仔细看看上面的命令并理解它的每个部分:

  • tail -n+2:从第二行开始直到文件末尾
  • sort -u:排序并删除重复项
  • -t, : 定义逗号作为字段分隔符
  • -k1,1 : 按第一列排序

很有可能,我们仍然希望在输出中保留标题行。

为此,我们可以让head 命令先打印第一行,然后执行上面的命令:

$ head -n1 price.csv && tail -n+2 price.csv | sort -u -t, -k1,1 
Product,Price,Update
Keybord,20,2019-02-02
Monitor,218,2019-01-01
Wireless Mouse,25,2019-02-02

4. 使用awk

awk 是Unix 系统中非常强大的命令行文本处理工具。

它可以简洁地解决这个问题:

$ awk -F, 'NR==1 || !a[$1]++' price.csv 
Product,Price,Update
Monitor,218,2019-01-01
Keybord,20,2019-02-02
Wireless Mouse,25,2019-02-02

与使用sort命令的解决方案相比 ,awk命令要紧凑得多。让我们仔细看看。

*-F,*将逗号设置为字段分隔符

NR ==1表达式处理标题行:awk将在输出中打印第一行。

现在是棘手的部分:*!a[$1]++,*让我们了解它是如何工作的。

首先,在 awk 中,一个非零数字模式 将被评估为,一个模式将触发默认操作打印错误的模式将无济于事。

回到我们的例子:

  • 当带有“ Keyboard ”的行第一次出现在awk中时,awk创建一个关联数组 元素:a[“Keyboard”],默认值:0
  • a[“Keyboard”]++返回原始值,然后将其值递增 1,因此表达式返回0,然后我们有a[“Keyboard”]=1
  • !a[“Keyboard”]++将变为!0,正如我们所了解的,它被评估为*true,*因此触发默认操作:打印当前行
  • 再次出现“ Keyboard ”行时,数组元素已经存在,a[“Keyboard”] ++会返回1并保持2
  • !a[“Keyboard”]++这次会变成!1,因此我们有false:什么都不做。

这样,在打印出第一个“ Keyboard ”之后,数组元素a[“Keyboard”]就保存了一个正数。以下所有带有“ Keyboard ”的行将使!a[“Keyboard”]++评估为false。因此,删除了重复项。

5. awksort

到目前为止,我们已经看到了两种不同的方法来解决这个问题。如果我们比较它们的输出,我们会发现它们是不同的:

$ diff -y <(head -n1 price.csv && tail -n+2 price.csv | sort -u -t, -k1,1) <(awk -F, 'NR==1 || !a[$1]++' price.csv)
Product,Price,Update				 Product,Price,Update
Keybord,20,2019-02-02			<
Monitor,218,2019-01-01				 Monitor,218,2019-01-01
				            	>	Keybord,20,2019-02-02
Wireless Mouse,25,2019-02-02		 Wireless Mouse,25,2019-02-02

这是因为sort命令 首先按键列对行进行排序,这可能会改变行的原始顺序,但awk不会。

让我们回顾并比较这两种解决方案:

  • 如果输入中有标题行,awk可以比sort方法更容易地处理标题行
  • awk使用单个进程解决问题,而sort方法需要三个进程:headtailsort
  • awk会比sort方法更快,因为 awk解决方案不会对文件进行排序
  • 如果需要排序结果,sort方法将是正确的选择