繁体   English   中英

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

[英]Sort columns of a file using Linux tools

我有一个像

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

您可以像这样使用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 ,以按照先前定义的顺序输出。

我强烈建议您为每一行打印每个可能的字段,并在适当时提供“ 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作为split()的第四个参数。 您只需要这样做,因为您在Int:value使用: ,而每个其他名称/值对在Charge=value使用= 如果您对输出中的Int=value或任何其他一致的分隔符感到满意,则无需保存分隔符,因此也不需要GNU awk来使第4个arg达到split()。

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

您还应该考虑将输出格式更改为表格格式,以便可以在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的第四个arg来保存分隔符字符串split()。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM