问题描述
我需要通过检查系统中的所有CSV文件来找到所有列中具有相同内容的所有行。示例:
MYCOL;1;2;3;4
MYCOL2;2;3;4;5
MYCOL3;1;1;1;1
MYCOL4;;;;
在我的示例中,我将需要对MYCOL3和MYCOL4进行grep,因为它们对于所有列都具有相同的字段内容,所以没有内容都没关系。
我想到了这样的事情:
find / -name *.csv | xargs awk -F "," '{col[$1,$2]++} END {for(i in col) print i,col[i]}'
但是我错过了所有列之间的比较。
解决方法
您可以使用grep
命令:
$ grep -xE '[^;]*(;[^;]*)\1+' ip.txt
MYCOL3;1;1;1;1
MYCOL4;;;;
-
-x
仅匹配整行 -
[^;]*
第一个字段 -
(;[^;]*)
捕获;
后跟非;
个字符(即第二个字段) -
\1+
使用捕获的字段重复直到行尾为止的次数
如果输入仅包含ASCII字符,则可以使用LC_ALL=C grep <...>
更快地获得结果。
如果您有GNU grep
,则可以使用-r
选项和--include=
选项代替find+grep
另外,使用find <...> -exec grep <...> {} +
代替find + xargs
仅进行了速度样本检查,此正则表达式可能太糟糕而无法与BRE / ERE一起使用。如果可用,请使用grep -P
。否则,请使用awk
或perl
。
$ perl -0777 -ne 'print $_ x 1000000' ip.txt | shuf > f1
$ du -h f1
53M f1
$ time LC_ALL=C grep -xE '[^;]*(;[^;]*)\1+' f1 > t1
real 0m44.815s
$ time LC_ALL=C grep -xP '[^;]*(;[^;]*)\1+' f1 > t2
real 0m0.507s
$ time perl -ne 'print if /^[^;]*(;[^;]*)\1+$/' f1 > t3
real 0m3.973s
$ time LC_ALL=C awk -F ';' '{for (i=3; i<=NF; i++) if ($i != $2) next} 1' f1 > t4
real 0m2.728s
$ diff -sq t1 t2
Files t1 and t2 are identical
$ diff -sq t1 t3
Files t1 and t3 are identical
$ diff -sq t1 t4
Files t1 and t4 are identical
,
使用awk
的非正则表达式方法:
awk -F ';' '{for (i=3; i<=NF; i++) if ($i != $2) next} 1' file
MYCOL3;1;1;1;1
MYCOL4;;;;
,
另一个awk
$ awk -F";" -v OFS=";" ' { a=$0; $1=""; c=split($0,ar,$2); if(length($0)==NF-1 || c==NF) print a } ' gipsy.txt
MYCOL3;1;1;1;1
MYCOL4;;;;
$