使用Linux工具对文件的列进行排序

时间:2017-01-14 09:12:28

标签: linux sorting awk gawk

我有一个像

这样的文件
ID=1234 PCharge=2 ext=5 IMSI=1234 Int:123 Charge=3
ID=1234 PCharge=2 ext=5 IMSI=1234 Charge=3
ID=1234 PCharge=2 ext=5 IMSI=1234 Int:4567 Charge=3
Charge=3 ID=1234 PCharge=2 ext=5 IMSI=1234
PCharge=2 ID=1234 Charge=3 ext=5 IMSI=1234

如何将此文件排序为此类文件?

ID=1234 Charge=3 PCharge=2 ext=5 IMSI=1234
ID=1234 Charge=3 PCharge=2 ext=5 IMSI=1234
ID=1234 Charge=3 PCharge=2 ext=5 IMSI=1234
ID=1234 Charge=3 PCharge=2 ext=5 IMSI=1234
ID=1234 Charge=3 PCharge=2 ext=5 IMSI=1234 Int:123
ID=1234 Charge=3 PCharge=2 ext=5 IMSI=1234 Int:4567

2 个答案:

答案 0 :(得分:3)

您可以使用这样的awk脚本:

script.awk (针对可选键和分隔符“:”和“=”进行了更新)

BEGIN { keys[1] = "ID"
        keys[2] = "Charge"
        keys[3] = "PCharge"
        keys[4] = "ext"
        keys[5] = "IMSI"
        keys[6] = "Int"
      }

NF>0  { delete values # reset each line due to optional keys
        for( f =1 ; f <= NF; f++ ) {
          split( $f, kv, "[=:]",seps) # split using RE separator and store individual separator in seps
          values[ kv[1] ] = seps[1] kv[2] # prepend individual separator to value
        }

        tmp = ""
        for(k = 1; k <= length(keys); k++ ) {
            if( keys[k] in values) { # check due to optional keys
                tmp=sprintf("%s%s%s%s",
                             tmp,
                             keys[k], values[keys[k]], 
                             (k < NF) ? OFS : "" ) 
            }
        }
        print tmp
      }

运行它:awk -f script.awk yourfile

BEGIN块设置输出字段序列。第二个块上的条件NF > 0跳过空行。

第二个块迭代key=value个字段(awk将空格分割为字段)并存储键/值对。在第二个循环中,存储的对被附加到tmp以便在先前定义的序列中输出。

答案 1 :(得分:2)

我强烈建议您为每一行打印每个可能的字段,并在适当时提供“N / A”值,因为它会使您的数据更容易进行进一步处理:

$ cat tst.awk
BEGIN { OFS="," }
{
    delete name2val
    numFlds = split($0,flds,/[=:]|[[:space:]]+/,seps)
    for (fldNr=1;fldNr<numFlds;fldNr+=2) {
        name = flds[fldNr]
        if ( !seen[name]++ ) {
            names[++numNames] = name
        }
        name2sep[name] = seps[fldNr]
        name2val[name] = flds[fldNr+1]
    }
}
NR!=FNR {
    for (nameNr=1;nameNr<=numNames;nameNr++) {
        name = names[nameNr]
        sep  = name2sep[name]
        val  = (name in name2val ? name2val[name] : "N/A")
        printf "%s%s%s%s", name, sep, val, (nameNr<numNames ? OFS: ORS)
    }
}

$ awk -f tst.awk file file
ID=1234,PCharge=2,ext=5,IMSI=1234,Int:123,Charge=3
ID=1234,PCharge=2,ext=5,IMSI=1234,Int:N/A,Charge=3
ID=1234,PCharge=2,ext=5,IMSI=1234,Int:4567,Charge=3
ID=1234,PCharge=2,ext=5,IMSI=1234,Int:N/A,Charge=3
ID=1234,PCharge=2,ext=5,IMSI=1234,Int:N/A,Charge=3

上面使用GNU awk为第4个arg分割()。您只需要这样做,因为您在:中使用Int:value,而其他每个名称 - 值对都使用= Charge=value。如果您对输出中的Int=value或任何其他一致的分隔符感到满意,那么您将不需要保存分隔符,因此第四个arg不需要GNU awk来分割()。

请注意,上面不需要对字段名称进行硬编码,它只使用输入文件中的任何名称,采用2遍方法从第一遍的每一行读取所有名称,因此它知道所有可能的字段名称用于在第二遍中的每一行打印。

您还应该考虑将输出格式更改为表格,以便在Excel中使用它,例如:

$ cat tst.awk
BEGIN { FS="[=:]|[[:space:]]+"; OFS="," }
{
    delete name2val
    for (fldNr=1;fldNr<NF;fldNr+=2) {
        name = $fldNr
        if ( !seen[name]++ ) {
            names[++numNames] = name
        }
        name2val[name] = $(fldNr+1)
    }
}
NR!=FNR {
    if (FNR==1) {
        for (nameNr=1;nameNr<=numNames;nameNr++) {
            name = names[nameNr]
            printf "%s%s", name, (nameNr<numNames ? OFS: ORS)
        }
    }
    for (nameNr=1;nameNr<=numNames;nameNr++) {
        name = names[nameNr]
        val  = (name in name2val ? name2val[name] : "N/A")
        printf "%s%s", val, (nameNr<numNames ? OFS: ORS)
    }
}

$ awk -f tst.awk file file
ID,PCharge,ext,IMSI,Int,Charge
1234,2,5,1234,123,3
1234,2,5,1234,N/A,3
1234,2,5,1234,4567,3
1234,2,5,1234,N/A,3
1234,2,5,1234,N/A,3

请注意,第二个脚本不需要GNU awk,它可以在任何POSIX awk中工作,因为它不需要使用gawk特定的第4个arg来保存分隔符字符串到split()。