我正在尝试学习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
如果可能的话,我想对命令中的所有部分进行解释,以便我将来能够理解它。提前谢谢。
答案 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中,列之间以及输出列之间有一个制表符。如果-e
在echo
输出后的最后一行中删除:
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