从巨大的(具有数百万个记录和列)OFS文件(没有固定的标题行)unix中查找所有唯一的列

时间:2018-08-27 11:33:46

标签: linux unix awk sed

输入

119764469|14100733//1,k1=v1,k2=v2,STREET:1:1=NY
119764469|14100733//1,k1=v1,k2=v2,k3=v3 
119764469|14100733//1,k1=v1,k4=v4,abc.xyz:1:1=nmb,abc,po.foo:1:1=yu

k1可以是任何带有字母数字的名称。 &:特殊字符,例如abc.nm.1:1

期望的输出(所有唯一的列),不需要/不必要的排序,应该是超快的

k1,k2,STREET:1:1,k3,k4,abc.xyz:1:1

我当前的方法/解决方案是

awk -F',' '{for (i=0; i<=NR; i++) {for(j=1; j<=NF; j++){split($j,a,"="); print a[1];}}}' file.txt | awk '!x[$1]++' | grep -v '|' | sed -e :a -e '$!N; s/\n/ | /; ta'

它可以正常工作,但对于文件太大(可能以MB或GB为单位)来说太慢了

注意:这在数据迁移中是必需的,应使用基本的unix shell命令,因为生产可能不允许使用第三方工具。

3 个答案:

答案 0 :(得分:2)

不确定速度,请尝试一下

$ cut -d, -f2- file |        # select the key/value pairs
  tr ',' '\n'       |        # split each k=v to its own line
  cut -d= -f1       |        # select only keys
  sort -u           |        # filter uniques
  paste -sd,                 # serialize back to single csv line

abc.xyz:1:1,k1,k2,k3,k4,STREET:1:1

我希望它比grep更快,因为不涉及任何正则表达式。

答案 1 :(得分:1)

使用grep -o仅复制所需的部分:

grep -o -e '[^=,]\+=[^,]\+' file.txt |awk -F'=' '{print $1}' |sort |uniq |tr '\n' ',' |sed 's/,$/\n/'
>>> abc.xyz:1:1,k1,k2,k3,k4,STREET:1:1

(这里需要{sort,因为否则uniq无效)

答案 2 :(得分:0)

如果您真的不需要全部输出:

$ awk -F'[,=]' '{for (i=2;i<=NF;i+=2) print $i}' file | sort -u
abc.xyz:1:1
k1
k2
k3
k4
STREET:1:1

如果您这样做:

$ awk -F'[,=]' '{for (i=2;i<=NF;i+=2) print $i}' file | sort -u |
    awk -v ORS= '{print sep $0; sep=","} END{print RS}'
abc.xyz:1:1,k1,k2,k3,k4,STREET:1:1

您可以在一个awk脚本中完成所有操作,但是我不确定它是否会像上面那样高效,或者如果/当数组增长到数百万个值时,可能会遇到内存问题:

$ cat tst.awk
BEGIN { FS="[,=]"; ORS="" }
{
    for (i=2; i<=NF; i+=2) {
        vals[$i]
    }
}
END {
    for (val in vals) {
        print sep val
        sep = ","
    }
    print RS
}

$ awk -f tst.awk file
k1,abc.xyz:1:1,k2,k3,k4,STREET:1:1