如何在Unix中删除文件中的重复行?

Vij*_*jay 118 unix shell scripting awk sed

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

我可以使用sort -uuniq命令,但我想使用sedawk.那可能吗?

Jon*_*röm 259

awk '!seen[$0]++' file.txt
Run Code Online (Sandbox Code Playgroud)

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

  • 这里有一个重要的警告:如果您需要对多个文件执行此操作,并且您在命令末尾添加更多文件,或者使用通配符……“seen”数组将填充来自所有文件的重复行。如果您想独立处理每个文件,则需要执行类似 `for f in *.txt; 的操作。do gawk -i inplace '!seen[$0]++' "$f"; 完成` (7认同)
  • 要将它保存在文件中,我们可以这样做```awk'!见[$ 0] ++'merge_all.txt> output.txt``` (3认同)
  • @NickK9 跨多个文件累积重复数据删除本身就很棒。不错的提示 (2认同)
  • 它还可以工作,因为“++”运算符的结果不是增量后的值,而是之前的值。 (2认同)

And*_*ler 29

来自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'
Run Code Online (Sandbox Code Playgroud)

  • "$ N!; /^\(.*\)\n\1$/!P; d"的意思是"如果你在最后一行,在另一行读取不是.现在看看你有什么,如果它不是东西,跟着一个换行符,然后再同样的东西,打印出来的东西.现在删除东西(直到换行)." (3认同)
  • 差不多 7 年后,没有人回复@amichair ... <sniff> 让我很难过。;) 无论如何,`[ -~]` 表示从 0x20(空格)到 0x7E(波形符)的 ASCII 字符范围。这些被认为是[可打印的 ASCII 字符](https://www.ascii-code.com/)(链接页面也有 0x7F/delete,但这似乎不正确)。这使得该解决方案对于任何不使用 ASCII 或使用制表符的人来说都被破坏了。更可移植的 `[^\n]` 包含了更多的字符......事实上,除了一个字符之外的所有字符。 (3认同)
  • 'G; s / \ n / && /; / ^ \([-〜] * \ n \)。* \ n \ 1 / d; s / \ n //; H; P'的意思大致是:“在此行的整个保留空间后面添加一行,然后,如果看到重复的行将整个内容扔掉,否则将整个混乱复制回保留空间并打印第一部分(这就是您刚刚读。” (2认同)

Chr*_*nat 12

类似于@ jonas的awk解决方案的Perl单线程:

perl -ne 'print if ! $x{$_}++' file
Run Code Online (Sandbox Code Playgroud)

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

perl -lne 's/\s*$//; print if ! $x{$_}++' file
Run Code Online (Sandbox Code Playgroud)

此变体就地编辑文件:

perl -i -ne 'print if ! $x{$_}++' file
Run Code Online (Sandbox Code Playgroud)

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

perl -i.bak -ne 'print if ! $x{$_}++' file
Run Code Online (Sandbox Code Playgroud)


Boh*_*ohr 7

使用 Vim(Vi compatible) 的另一种方法

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

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

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

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


Bra*_*der 6

安德烈米勒上面发布的单线工作除了最近版本的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;" .


Wei*_*ike 5

第一个解决方案也来自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
Run Code Online (Sandbox Code Playgroud)

核心思想是:

在每个重复的连续行的最后一次出现时打印一次,并使用 D 命令来实现循环

解释:

  1. $!N;:如果当前行不是最后一行,则使用该N命令将下一行读入模式空间
  2. /^(.*)\n\1$/!P:如果当前模式空间的内容是由 分隔的两个重复字符串\n,这意味着下一行与当前行相同,根据我们的核心思想,我们不能打印它否则,这意味着当前行是其所有重复连续行的最后一次出现。我们现在可以使用该命令打印当前模式空间P中的字符,直到(也打印出来)。\n\n
  3. D:我们使用该命令删除当前模式空间D中的字符,直到(也删除),然后模式空间的内容就是下一行。\n\n
  4. 并且该D命令将强制sed跳转到其第一个命令$!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
Run Code Online (Sandbox Code Playgroud)

核心思想是:

每个重复的连续行在第一次出现时打印一次,并使用命令和命令来实现LOOP。:t

解释:

  1. 从输入流或文件中读取新行并打印一次。
  2. 使用该:loop命令设置一个名为loop的标签
  3. 用于N将下一行读入模式空间
  4. s/^(.*)\n\1$/\1/如果下一行与当前行相同,则用于删除当前行。我们使用该s命令来执行删除操作。
  5. 如果s命令执行成功,则使用tloop命令强制sed跳转到名为loop的标签处,对下一行进行同样的循环,直到最后打印的行没有重复的连续行为止;否则,使用命令到与最新打印的行相同的行,并强制跳转到第一条命令,即命令。当前模式空间的内容是下一个新行。Ddeletesedp