鉴于此文件(注释不是文件的一部分,而是解释的一部分)...
x,a,001,b,c,d,y
x,a,002,b,c,e,yy
x,bb,003,b,d,e,y
x,c,004,b,d,e,y
x,c,005,b,d,e,y # nb - dupe of row 4
x,dd,006,b,d,e,y
x,c,007,b,d,e,y # nb - dupe of row 4 and 5
x,dd,008,b,d,f,y
x,dd,009,b,d,e,y # nb - dupe of row 6
x,e,010,b,d,f,y
...我想得到以下输出:
x,a,001,b,c,d,y
x,a,002,b,c,e,yy
x,bb,003,b,d,e,y
x,c,004,b,d,e,y
x,dd,006,b,d,e,y
x,dd,008,b,d,f,y
x,e,010,b,d,f,y
如果从文件中删除第 3 列,然后对文件运行 uniq,然后如果剩余行将其第三列值添加回正确的位置,那么我将得到上述结果。
但我真的很努力想出一些可以做到这一点的东西。我很高兴有机会了解 Linux 的文本处理实用程序。
性能:文件看起来不可能增长到超过 1MB,而且每天只有 1 个文件。
目标:Debian GNU/Linux 7 amd64、256MB/Xeon。
编辑:调整示例,因为字段不是固定宽度的,uniq --skip-chars=n
据我所知,涉及的解决方案将不起作用。
答案1
有了awk
,你可以这样做:
awk -F, -vOFS=, '{l=$0; $3=""}; ! ($0 in seen) {print l; seen[$0]}'
答案2
最简单的方法:
sort -u -t, -k1,2 -k4
-u
: 只输出第一行等于-t,
: 使用逗号作为字段分隔符-k1,2 -k4
:仅对字段 1,2 和 4 以及其余字段进行排序
另一种选择是在两侧重新排列数据sed
(注意 GNU 选项-r
)——这要求记录大部分是固定长度的,否则它将失败(而且几乎不明显):
sed -r 's/^([^,]+,[^,]+)(,[^,]+)(.*)$/\1\3\2/' \
| sort \
| uniq -w 12 \
| sed -r 's/^([^,]+,[^,]+)(.*)(,[^,]+)$/\1\3\2/'
如果需要,您可能想sort
在末尾添加另一个以按数字排序(使用该-k
选项根据应执行的排序进行选择 - 即类似sed -k3 -t,
)
例如,在 Perl 中,您可以使用要决定唯一性的部分作为散列中的键(整行的值),并仅在键尚未定义时插入散列。这当然比使用sed
(或awk
) 更灵活,但也更多的写作(我距离 Perl 大师还很远,所以很可能它可以以更优雅的方式完成 - 请参阅 Perl-like 的其他答案Perl 解决方案):
#!/usr/bin/perl
use strict;
my %lines;
while (<>) {
(my $k1, my $v, my $k2) = /^([^,]+,[^,]+,)([^,]+)(,.*)$/;
my $k = $k1 . $k2;
if (!exists($lines{$k})) {
$lines{$k} = $_;
}
}
for my $k (sort(keys(%lines))) {
print $lines{$k};
}
答案3
一种方法来做到这一点awk | sort | uniq | awk
:
awk -F, '{a=$1;$1=$3;$3=a;print}' file | sort -k 2 | uniq -f 1 | awk -v OFS=',' '{a=$1;$1=$3;$3=a;print}'
答案4
一个更简单的 Perl 方法是:
perl -F"," -ane '$a=join(",",@F[0,1,3 .. $#F]); print unless $k{$a}; $k{$a}++' file
将-a
字段拆分到@F
数组中并将-F","
字段分隔符设置为,
。表示在输入文件的每一行-n
运行由 给出的脚本。-e
这个想法是获取一个数组切片(元素 0,1 和 3 直到数组末尾),将它们连接到一个字符串 ( $a
) 中,并使用该字符串作为哈希(关联数组)引用。然后,仅当之前未见过哈希键时才打印每一行。