有没有办法在Unix中删除文件中的重复行?
我可以使用sort -u
和uniq
命令执行此操作,但我想使用sed
或awk
。
这可能吗?
答案 0 :(得分:236)
awk '!seen[$0]++' file.txt
seen
是一个关联数组,Awk会将文件的每一行传递给。如果一行不在数组中,那么seen[$0]
将评估为false。 !
是逻辑NOT运算符,将false反转为true。 Awk将打印表达式计算结果为true的行。 ++
增加seen
,以便在第一次找到一行后再seen[$0] == 1
,然后seen[$0] == 2
,依此类推。
Awk会评估除0
和""
(空字符串)之外的所有内容。如果在seen
中放置了重复的行,则!seen[$0]
将评估为false,并且该行不会被写入输出。
答案 1 :(得分:27)
来自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'
答案 2 :(得分:11)
Perl one-liner类似于@ 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
答案 3 :(得分:6)
无效循环,如果最后一行为空且没有字符:
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;”。
答案 4 :(得分:4)
使用Vim(Vi兼容)的替代方法:
从文件中删除重复的连续行:
vim -esu NONE +'g/\v^(.*)\n\1$/d' +wq
从文件中删除重复,非连续和非空的行:
vim -esu NONE +'g/\v^(.+)$\_.{-}^\1$/d' +wq
答案 5 :(得分:3)
$ 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
:如果当前pattern space
的内容是由duplicate string
分隔的两个\n
,则表示下一行是带有当前行的same
,我们不能按照我们的核心理念打印它;否则,这意味着当前行是其所有重复连续行的最后一次出现,我们现在可以使用P
命令在当前pattern space
util \n
中打印字符({{1}也印刷了。)。\n
:我们使用D
命令删除当前D
util pattern space
(\n
也已删除)中的字符,然后\n
的内容1}}是下一行。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
的{{1}}。label
将下一行读入loop
。N
删除当前行,我们使用pattern space
命令执行s/^(.*)\n\1$/\1/
操作。s
命令,则使用delete
命令强制s
跳转到名为tloop
的{{1}},这将执行相同操作循环到下一行util,没有重复的行连续行sed
;否则,使用label
命令loop
与latest printed
相同的行,并强制D
跳转到第一个命令,即delete
命令,当前latest-printed line
的内容是下一个新行。答案 6 :(得分:1)
uniq将被尾部的空格和制表符所欺骗。为了模拟人类如何进行比较,我在比较之前会修剪所有尾随空格和制表符。
我认为$!N;需要花括号,否则它将继续,这是无限循环的原因。
我在Ubuntu 20.10中拥有bash 5.0和sed 4.7。在字符集匹配时,第二个单行代码不起作用。
三种变体,第一种是消除相邻的重复行,第二种是消除重复出现的行,第三种是消除文件中最后一行的所有实例。
# First line in a set of duplicate lines is kept, rest are deleted.
# Emulate human eyes on trailing spaces and tabs by trimming those.
# Use after norepeat() to dedupe blank lines.
dedupe() {
sed -E '
$!{
N;
s/[ \t]+$//;
/^(.*)\n\1$/!P;
D;
}
';
}
# Delete duplicate, nonconsecutive lines from a file. Ignore blank
# lines. Trailing spaces and tabs are trimmed to humanize comparisons
# squeeze blank lines to one
norepeat() {
sed -n -E '
s/[ \t]+$//;
G;
/^(\n){2,}/d;
/^([^\n]+).*\n\1(\n|$)/d;
h;
P;
';
}
lastrepeat() {
sed -n -E '
s/[ \t]+$//;
/^$/{
H;
d;
};
G;
# delete previous repeated line if found
s/^([^\n]+)(.*)(\n\1(\n.*|$))/\1\2\4/;
# after searching for previous repeat, move tested last line to end
s/^([^\n]+)(\n)(.*)/\3\2\1/;
$!{
h;
d;
};
# squeeze blank lines to one
s/(\n){3,}/\n\n/g;
s/^\n//;
p;
';
}
答案 7 :(得分:-2)
这可以通过 awk
实现
行下方将显示唯一值
awk file_name | uniq
您可以将这些唯一值输出到新文件
awk file_name | uniq > uniq_file_name
新文件uniq_file_name仅包含唯一值,没有重复值
答案 8 :(得分:-4)
cat filename | sort | uniq -c | awk -F" " '$1<2 {print $2}'
使用awk删除重复的行。