4

我目前正在尝试学习 awk,并且我想做一项特定的任务。我的问题在范围上与之前发布的问题相似(使用 awk 将列转置为行),但对我的数据不太适用。我一直在试图找出原因,我相信它很简单。

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

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

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

4

5 回答 5

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
于 2013-05-25T04:27:29.180 回答
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
于 2013-05-25T18:07:17.760 回答
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 中,列之间以及输出列之间有一个制表符。如果-e从输出后的最后一行中删除echo是:

2300\t1220
1101\t7778\t7755\t8889\t6789
4000\t2333\t7555\t9000\t1111
于 2013-05-25T13:02:28.533 回答
1

灵感来自肯特的回答。

awk '{
         a[$1]=a[$1] ? a[$1] FS $2 : $2
    } 
END {
        for (key in a) print key,a[key]
    }' FS='\t' OFS='\t' f
于 2013-05-25T04:30:09.500 回答
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
于 2013-05-25T19:15:57.483 回答