如何按列执行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. awk与sort
到目前为止,我们已经看到了两种不同的方法来解决这个问题。如果我们比较它们的输出,我们会发现它们是不同的:
$ 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方法需要三个进程:head、tail和sort
- awk会比sort方法更快,因为 awk解决方案不会对文件进行排序
- 如果需要排序结果,sort方法将是正确的选择