如何比较和连接bash中巨大的csv文件中的相同行?

时间:2014-09-14 10:58:09

标签: bash csv awk sed lines

我有以下.csv文件(大小很大〜几百MB到GB,几列~20,没有排序,由&#34分隔;"):

name1,address1,town1,zip1,....,category1
name2,address2,town2,zip2,....,category2
name3,address3,town3,zip3,....,category3_1
name3,address3,town3,zip3,....,category3_2
name3,address3,town3,zip3,....,category3_3
name4,address4,town4,zip4,....,category4_1
name4,address4,town4,zip4,....,category4_2
name4,address4,town4,zip4,....,category4_3
name4,address4,town4,zip4,....,category4_4
name5,address5,town5,zip5,....,category5

我需要将行连接到只有一行,如果它们具有相同的行并且仅在类别上有所不同,并使用&#34 ;;"将这些类别放到最后一列中。分隔符,例如:

name1,address1,town1,zip1,....,category1
name2,address2,town2,zip2,....,category2
name3,address3,town3,zip3,....,category3_1;category3_2;category3_3
name4,address4,town4,zip4,....,category4_1;category4_2;category4_3;category4_4
name5,address5,town5,zip5,....,category5

我尝试用阅读...;做读...完成< $ file ,但这只是按2行读取每个文件并且不会比较每一行。还试图将类别信息保存到数组并创建合并类别列,但在某些行脚本只是停止按我想要的方式解析它。 在 awk sed 中完成此操作会很棒,因为在阅读包含大量列的大文件时,读取非常慢,但是如果用其他语言做到这一点的更好的方法我会好好的。 非常感谢!

1 个答案:

答案 0 :(得分:0)

两个答案:

Shell + sed

您可以对此(特定)案例使用

嗯,太快了!我错了!

$ sed -e ':;N;s/^\(\([^,]\+,\)\{5\}\)\(.*\)*\n\1/\1\3;/;t' file.csv

$ sed -e ':a;$!N;s/^\(\([^,]\+,\)\{5\}\)\(.*\)*\n\1/\1\3;/;ta;P;D;$!ba' file.csv

用于检索列数(-1)又称分隔符数:

read line <file.csv
cols="${line//,}"
cols=$[${#line}-${#cols}]

sed -e "
    :a;
     $!N;
     s/^\(\([^,]\+,\)\{$cols\}\)\(.*\)*\n\1/\1\3;/;
     ta;
     P;
     D;
     $!ba
  " file.csv

name1,address1,town1,zip1,....,category1
name2,address2,town2,zip2,....,category2
name3,address3,town3,zip3,....,category3_1;category3_2;category3_3
name4,address4,town4,zip4,....,category4_1;category4_2;category4_3;category4_4
name5,address5,town5,zip5,....,category5

仅限最后一个字段!

有一种基于最后一个逗号的简单方法:

sed -e ":;$!N;s/^\(.*,\)\([^,]*\)*\n\1/\1\2;/;t;P;D;$!b" file.csv

(用于在行尾添加不需要的 CR

sed -e ':;$!N;s/\o015//g;s/^\(.*,\)\([^,]*\)*\n\1/\1\2;/;t;P;D;$!b'

纯粹的bash(没有叉子)

这可以使用纯来完成(使用也可以正常工作!),但也许更适合小文件:

while read line;do
    if [ "${line%,*}" = "${last%,*}" ];then
        last="$last;${line##*,}"
    else
        echo "$last"
        last="$line"
    fi
done < file.csv
echo "$last"

name1,address1,town1,zip1,....,category1
name2,address2,town2,zip2,....,category2
name3,address3,town3,zip3,....,category3_1;category3_2;category3_3
name4,address4,town4,zip4,....,category4_1;category4_2;category4_3;category4_4
name5,address5,town5,zip5,....,category5

注意:无需知道列数,因为这是基于最后一个逗号