使用awk对字段进行排序和排列

时间:2013-05-25 04:19:05

标签: awk

我正在尝试学习awk,我想做一个特定的任务。我的问题范围与先前发布的问题类似(Using awk to transpose column to row),但对我的数据不太适用。我一直试图找出原因,并确定它非常简单。

我在制表符分隔表中有大量数据,只有两个字段(例如下面的代码):

1101\t7778
1101\t7755
1101\t8889
1101\t6789
2300\t1220
4000\t2333
4000\t7555
4000\t9000
4000\t1111

我希望在字段匹配时将第二个字段附加到行上。所需的输出是:

1101\t7778\t7755\t8889\t6789
2300\t1220
4000\t2333\t7555\t9000\t1111

如果可能的话,我想对命令中的所有部分进行解释,以便我将来能够理解它。提前谢谢。

5 个答案:

答案 0 :(得分:5)

awk '    { list[$1] = list[$1] "\t" $2 }
     END { for (i in list) printf "%s%s\n", i, list[i] }' data

第一行将标签和第二个字段添加到list索引的$1元素。第二行打印出键和累积的值列表。

示例输出:

1101    7778    7755    8889    6789
4000    2333    7555    9000    1111
2300    1220

如果要对第一列进行排序,可以通过sort -n管道输出。如果你有GNU awk,你也可以调查内置的排序函数:

/usr/gnu/bin/awk '    { list[$1] = list[$1] "\t" $2 }
                  END { n = asorti(list, indexes);
                        for (i = 1; i <= n; i++)
                            printf "%s%s\n", indexes[i], list[indexes[i]]
                      }' data

排序输出:

1101    7778    7755    8889    6789
2300    1220
4000    2333    7555    9000    1111

答案 1 :(得分:3)

对于abasu的请求,纯bash版本:

#!/bin/bash

declare -A hash
while read x y; do
  hash[$x]=${hash[$x]}"\t"$y
done <<XXX
1101    7778
1101    7755
1101    8889
1101    6789
2300    1220
4000    2333
4000    7555
4000    9000
4000    1111
XXX

for i in ${!hash[*]}; { echo -e $i${hash[$i]};}

输出:

2300    1220
1101    7778    7755    8889    6789
4000    2333    7555    9000    1111

在here-is-the-document中,列之间以及输出列之间有一个制表符。如果-eecho输出后的最后一行中删除:

2300\t1220
1101\t7778\t7755\t8889\t6789
4000\t2333\t7555\t9000\t1111

答案 2 :(得分:3)

此版本最终不会将整个文件存储在内存中。它也没有重新排列键的顺序。

awk -F '\t' '
    $1 != prev {
        if (prev) print ""
        printf "%s", $1
        prev=$1
    }
    {printf "%s%s", FS, $2}
    END {print ""}
' f
1101    7778    7755    8889    6789
2300    1220
4000    2333    7555    9000    1111

答案 3 :(得分:1)

受到Kent's回答的启发。

awk '{
         a[$1]=a[$1] ? a[$1] FS $2 : $2
    } 
END {
        for (key in a) print key,a[key]
    }' FS='\t' OFS='\t' f

答案 4 :(得分:0)

另一个纯粹的bash实现只是为了好玩,使用字符串切片而不是仅使用4位数的正则表达式,所以它不健壮,但我正在使用的bash版本没有内置正则表达式,所以我不知道我还能做什么!

#!/bin/bash

while read line; do
        array[${line:0:4}]="${array[${line:0:4}]}${line:4:8}"
        indicies[${line:0:4}]=${line:0:4}

done < $1

for i in ${indicies[@]}; do
        echo "$i${array[$i]}"
done