有没有办法在Unix中删除文件中的重复行?
我可以使用sort -u
和uniq
命令来实现,但是我想使用sed
或awk
。那可能吗?
awk
,但在较大的文件上会消耗大量资源。
有没有办法在Unix中删除文件中的重复行?
我可以使用sort -u
和uniq
命令来实现,但是我想使用sed
或awk
。那可能吗?
awk
,但在较大的文件上会消耗大量资源。
Answers:
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,并且该行将不会写入输出。
awk '!seen[$0]++' merge_all.txt > output.txt
for f in *.txt; do gawk -i inplace '!seen[$0]++' "$f"; done
从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'
$!
部分是必要的吗?不会sed 'N; /^\(.*\)\n\1$/!P; D'
做同样的事情?我无法给出一个例子,说明我的机器上的两者是不同的(之前我在两个版本的末尾都尝试了一个空行,并且它们都很好)。
[ -~]
代表从0x20(空格)到0x7E(波浪号)的ASCII字符范围。这些被认为是可打印的 ASCII字符(链接的页面也具有0x7F / delete,但这似乎不正确)。这使该解决方案对于不使用ASCII的任何人或使用制表符的任何人都是无效的。更具移植性的字符[^\n]
包括更多的字符……事实上,除了一个字符以外,所有这些字符都是。
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
上面安德烈·米勒(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;” 。
$ 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.
说明:
$!N;
:如果当前行不是最后一行,请使用N
命令将下一行读入pattern space
。/^(.*)\n\1$/!P
:如果current的内容pattern space
是两个以duplicate string
分隔的\n
,则表示下一行是same
当前行的,我们将无法按照我们的核心思想进行打印;否则,这意味着当前行是所有重复的连续行的最后出现,我们现在可以使用P
命令在当前pattern space
util中打印字符\n
(\n
也已打印)。D
:我们使用D
命令删除当前pattern space
util中的字符\n
(\n
也已删除),然后pattern space
下一行的内容。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.
说明:
:loop
命令设置一个label
命名loop
。N
将下一行读入pattern space
。s/^(.*)\n\1$/\1/
删除当前行,如果下一行是同当前行,我们使用s
命令来执行delete
动作。s
命令执行成功,则使用tloop
命令强制sed
跳转到label
named loop
,这将对下一行进行相同的循环,直到该行没有重复的连续行latest printed
;否则,使用D
command到delete
与相同的行latest-printed line
,并强制sed
跳到第一个命令,即该p
命令,当前内容pattern space
是下一个新行。busybox echo -e "1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5" | busybox sed -nr "$!N;/^(.*)\n\1$/!P;D"
uniq
仅此一项就足够了。