基于bash中的多个列合并文件

时间:2016-06-22 15:47:11

标签: bash awk

我有一个包含多个列(以逗号分隔)的文件,其中值重复。我想要做的是合并或者#34;汇总"基于这些列的行。

例如,假设我有以下内容:

输入文件:

ID, Name , Eye Color, Hair Color, Marital Status
1 , John , Brown    , Brown     , Single
1 , Mary , Green    , Brown     , Married
2 , Joe  , Blue     , Blonde    , Divorced
2 , Brian, Green    , Brown     , Single
2 , Gary , Brown    , Blonde    , Married

我想要基于第一和第四列的以下输出:

输出文件:

ID, Name , Eye Color, Hair Color, Marital Status, Name, Eye Color, Hair Color, Marital Status
1 , John , Brown    , Brown     , Single        , Mary, Green    , Brown     , Married
2 , Joe  , Blue     , Blonde    , Divorced      , Gary, Brown    , Blonde    , Married
2 , Brian, Green    , Brown     , Single

我可以使用以下awk为第一列执行此操作:

awk -F, '
    NR!=1 && p1!=$1 { print prev; prev="" }
    { p1=$1; prev=(prev"") ? prev FS substr($0,index($0,$2)) : $0 }
    END { if(prev"") print prev }
' input.txt > output.txt

我还需要找到一种方法来包含第四列。

2 个答案:

答案 0 :(得分:2)

这里是一般的想法,不假设记录是有序的(但也不保留顺序)

$ awk 'BEGIN{ FS=" *, *"; OFS=","} 
       NR==1{split($0,header);next} 
            {a[$1,$4]=(($1,$4) in a?a[$1,$4] OFS:"") $0} 
         END{for(k in a) print a[k]}' file

2 , Joe  , Blue     , Blonde    , Divorced,2 , Gary , Brown    , Blonde    , Married
2 , Brian, Green    , Brown     , Single
1 , John , Brown    , Brown     , Single,1 , Mary , Green    , Brown     , Married

您可以像在逻辑中那样过滤不需要的重复列,并且需要使标头符合匹配记录的最大长度......

带扩展标头的格式化版本可以是

$ awk 'BEGIN{FS=" *, *"; OFS=","}
       NR==1{$1=$1; header0=$0; split($0,header); next} 
            {$1=$1; c[$1,$4]++; 
             a[$1,$4]=(($1,$4) in a?a[$1,$4] OFS $2 OFS $3 OFS $5:$0)}
         END{for(k in c) if(max<c[k]) max=c[k]; 
             printf "%s",header0; 
             for(i=2;i<=max;i++) printf "%s", OFS header[2] OFS header[3] OFS header[5]; 
             print ""; 
             for(k in a) print a[k] | "sort -n" }' file | 
  column -ts,


ID  Name   Eye Color  Hair Color  Marital Status  Name  Eye Color  Marital Status
1   John   Brown      Brown       Single          Mary  Green      Married
2   Brian  Green      Brown       Single
2   Joe    Blue       Blonde      Divorced        Gary  Brown      Married

答案 1 :(得分:0)

以下使用三个关联数组来跟踪所有内容......

  • x数组是一个二维&#34;交叉引用&#34; - 具有第1列和第4列值的索引,并且存储值是我们将找到匹配行的行号。
  • g数组会跟踪我们&#34;成长的次数。一排特别的。 (我们使用此数组在需要时增加标题。)
  • o数组是我们的输出数组,用于聚合我们的数据行。

我们使用sprintf重新格式化输入的最后一列,然后再附加到输出,以根据问题的输出要求保留表格的间距。

awk -F, -v OFS=, '
    ($1, $4) in x {
        # existent row -- append
        i = x[$1, $4]
        $1 = ""
        $5 = sprintf("%-15s", $5)
        o[i] = o[i] $0
        if(++g[i] > g[1]) {
            # grow the header row
            o[1] = o[1] h
            ++g[1]
        }
        next
    }
    {
        # new output row
        x[$1, $4] = ++n
        $5 = sprintf("%-15s", $5)
        o[n] = $0
    }
    NR==1 {
        # save header for append
        $1 = ""
        h = $0
    }
    END {
        for (i=1; i<=n; ++i)
            print o[i]
    }'

输出是问题中指定的内容(包括顺序和格式)。上述一个可能的缺点是,如果有人有相当复杂的婚姻状况,格式(间隔)可能不会成立。此外,如果尾随空白区域是不可接受的,则可以通过ENDfor区块sub(/ +$/, "", o[i])循环中轻松删除。