如何在不对Unix进行排序的情况下删除文件中的重复行?


136

有没有办法在Unix中删除文件中的重复行?

我可以使用sort -uuniq命令来实现,但是我想使用sedawk。那可能吗?


11
如果您的意思是连续重复,那么uniq仅此一项就足够了。
Michael Krelin-黑客

否则,我相信可以使用awk,但在较大的文件上会消耗大量资源。
Michael Krelin-黑客2009年

重复的stackoverflow.com/q/24324350stackoverflow.com/q/11532157有有趣的答案,最好在这里迁移。
人间

Answers:


290
awk '!seen[$0]++' file.txt

seen是Awk会将文件的每一行传递到的关联数组。如果行不在数组中,seen[$0]则将评估为false。该!是逻辑NOT运算符和将反转假为真。Awk将打印表达式计算结果为true的行。的++增量seen,以便seen[$0] == 1在第一时间之后的线被发现,然后seen[$0] == 2,依此类推。
Awk会将除0""(空字符串)以外的所有内容都评估为true。如果放置重复的行,seen则结果!seen[$0]将为false,并且该行将不会写入输出。


5
要将其保存在文件中,我们可以这样做awk '!seen[$0]++' merge_all.txt > output.txt
Akash Kandpal,

5
这里有一个重要的警告:如果您需要对多个文件执行此操作,并且在命令末尾添加更多文件或使用通配符,则“可见”数组将填充所有文件中的重复行。如果你不是要独立处理每一个文件,你需要做的是这样for f in *.txt; do gawk -i inplace '!seen[$0]++' "$f"; done
尼克K9

@ NickK9可以在多个文件中累积重复数据消除本身很棒。
温馨

31

http://sed.sourceforge.net/sed1line.txt:(请不要问我这是如何工作的;-))

 # delete duplicate, consecutive lines from a file (emulates "uniq").
 # First line in a set of duplicate lines is kept, rest are deleted.
 sed '$!N; /^\(.*\)\n\1$/!P; D'

 # delete duplicate, nonconsecutive lines from a file. Beware not to
 # overflow the buffer size of the hold space, or else use GNU sed.
 sed -n 'G; s/\n/&&/; /^\([ -~]*\n\).*\n\1/d; s/\n//; h; P'

geekery ;-) +1,但是资源消耗是不可避免的。
Michael Krelin-黑客

3
'$!N; /^(.*)\n\1$/!P; D'的意思是“如果您不在最后一行,请换行阅读。现在查看您拥有的内容,如果它不是ISN的内容,然后再换行,然后再次输入相同的内容,则将其打印出来。现在删除东西(直到换行符为止)。”
Beta

2
'G; s / \ n / && /; / ^([-〜] * \ n)。* \ n \ 1 / d; s / \ n //; H; P'的意思大致是:“在此行的整个保留空间后面添加一行,然后,如果看到重复的行将整个内容扔掉,否则将整个混乱复制回保留空间并打印第一部分(这就是您刚刚阅读。”
Beta

$!部分是必要的吗?不会sed 'N; /^\(.*\)\n\1$/!P; D'做同样的事情?我无法给出一个例子,说明我的机器上的两者是不同的(之前我在两个版本的末尾都尝试了一个空行,并且它们都很好)。
eddi

1
大约7年后,没有人回答@amichair ... <sniff>使我难过。;)无论如何,[ -~]代表从0x20(空格)到0x7E(波浪号)的ASCII字符范围。这些被认为可打印的 ASCII字符(链接的页面也具有0x7F / delete,但这似乎不正确)。这使该解决方案对于不使用ASCII的任何人或使用制表符的任何人都是无效的。更具移植性的字符[^\n]包括更多的字符……事实上,除了一个字符以外,所有这些字符都是。
B层

14

Perl一线式类似于@jonas的awk解决方案:

perl -ne 'print if ! $x{$_}++' file

此变体在比较之前删除了尾随空白:

perl -lne 's/\s*$//; print if ! $x{$_}++' file

此变化形式就地编辑文件:

perl -i -ne 'print if ! $x{$_}++' file

此变体可就地编辑文件并进行备份 file.bak

perl -i.bak -ne 'print if ! $x{$_}++' file

6

上面安德烈·米勒(Andre Miller)发布的单行代码适用,但当输入文件以空行结尾且不包含任何字符时,最新版本的sed除外。在Mac上,我的CPU旋转了。

如果最后一行为空并且没有字符,则为无限循环

sed '$!N; /^\(.*\)\n\1$/!P; D'

没有挂起,但是您丢失了最后一行

sed '$d;N; /^\(.*\)\n\1$/!P; D'

解释位于sed FAQ的最后

GNU sed维护人员认为,尽管
这将导致可移植性问题,但更改N命令以打印(而不是
删除)模式空间与人们
对“添加下一行”命令如何表现的直觉更加一致。
支持此更改的另一个事实是,
如果文件的行数为奇数,“ {N; command;}”将删除最后一行,而如果文件的行数为偶数,
则将打印最后一行。

要将以前使用N的行为(
到达EOF时删除模式空间)的脚本转换为
与sed所有版本兼容的脚本,请更改一个单独的“ N;”。改为“ $ d; N;”


5

使用Vim的另一种方式(Vi兼容)

从文件中删除重复的连续行:

vim -esu NONE +'g/\v^(.*)\n\1$/d' +wq

从文件中删除重复的,非连续的和非空的行:

vim -esu NONE +'g/\v^(.+)$\_.{-}^\1$/d' +wq


4

第一个解决方案也来自http://sed.sourceforge.net/sed1line.txt

$ echo -e '1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5' |sed -nr '$!N;/^(.*)\n\1$/!P;D'
1
2
3
4
5

核心思想是:

print ONLY once of each duplicate consecutive lines at its LAST appearance and use D command to implement LOOP.

说明:

  1. $!N;:如果当前行不是最后一行,请使用N命令将下一行读入pattern space
  2. /^(.*)\n\1$/!P:如果current的内容pattern space是两个以duplicate string分隔的\n,则表示下一行是same当前行的,我们将无法按照我们的核心思想进行打印;否则,这意味着当前行是所有重复的连续行的最后出现,我们现在可以使用P命令在当前pattern spaceutil中打印字符\n\n也已打印)。
  3. D:我们使用D命令删除当前pattern spaceutil中的字符\n\n也已删除),然后pattern space下一行的内容。
  4. and D命令将强制sed跳转到其FIRST命令$!N,但不会从文件或标准输入流中读取下一行。

第二种解决方案(从我自己)很容易理解:

$ echo -e '1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5' |sed -nr 'p;:loop;$!N;s/^(.*)\n\1$/\1/;tloop;D'
1
2
3
4
5

核心思想是:

print ONLY once of each duplicate consecutive lines at its FIRST appearance and use : command & t command to implement LOOP.

说明:

  1. 从输入流或文件中读取新行并打印一次。
  2. 使用:loop命令设置一个label命名loop
  3. 用于N将下一行读入pattern space
  4. 使用s/^(.*)\n\1$/\1/删除当前行,如果下一行是同当前行,我们使用s命令来执行delete动作。
  5. 如果s命令执行成功,则使用tloop命令强制sed跳转到labelnamed loop,这将对下一行进行相同的循环,直到该行没有重复的连续行latest printed;否则,使用Dcommand到delete与相同的行latest-printed line,并强制sed跳到第一个命令,即该p命令,当前内容pattern space是下一个新行。

Windows上带有busybox的相同命令:busybox echo -e "1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5" | busybox sed -nr "$!N;/^(.*)\n\1$/!P;D"
清道夫

-1

可以使用awk做到这一点。Line
在下面显示唯一的值

awk file_name | uniq

您可以将这些唯一值输出到新文件

awk file_name | uniq > uniq_file_name

新文件uniq_file_name将仅包含唯一值,没有重复项


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.