我有个问题。我试图自己解决这个问题,但似乎我太新了,无法awk
让它发挥作用。
假设我们有一个文件(例如database.txt)(值以制表符分隔):
NA64715 YU24921 MI84612 MI98142 NA94732
3241531 4957192 4912030 6574918 0473625
0294637 9301032 8561730 8175919 8175920
9481732 9359032 8571930 8134983 9385130
9345091 9385112 2845830 4901742 3455141
在一个单独的文件(例如populations.txt
)中,我有关于哪个 ID 属于哪个组的信息,例如:
NA64715 Europe
YU24921 Europe
MI84612 Asia
MI98142 Africa
NA94732 Asia
我需要做的是强制awk
为所有组(欧洲、亚洲、非洲)创建包含列的单独文件。我需要处理的文件很大,所以我不能简单地对列进行计数和编号并以简单的方式完成。我需要awk
检查哪个 ID 属于哪个人口(欧洲等),然后在数据库文件中找到该特定列,然后将整个列复制到一个新文件(对于所有人口来说是分开的)。
结果应该如下所示:
文件1(europe.txt
):
NA64715 YU24921
3241531 4957192
0294637 9301032
9481732 9359032
9345091 9385112
文件2 ( asia.txt
)
MI84612 NA94732
4912030 0473625
8561730 8175920
8571930 9385130
2845830 3455141
文件 3 ( africa.txt
)
MI98142
6574918
8175919
8134983
4901742
谁能帮我解决这个问题吗?
答案1
这可以一次性遍历文件,并且不需要将整个文件存储在内存中。它确实为每个目标文件保留打开的文件描述符。
awk -F '\t' '
NR==FNR {population[$1]=$2; next}
FNR==1 {
for (i=1; i<=NF; i++) {
destination[i] = population[$i] ".txt"
}
}
{
delete separator
for (i=1; i<=NF; i++) {
printf "%s%s", separator[destination[i]], $i > destination[i]
separator[destination[i]] = FS
}
for (file in separator) {
printf "\n" > file
}
}
' populations.txt database.txt
答案2
我相信这不是最好的方法,因为我们需要读取database.txt的次数与我们拥有的区域加一一样多。不幸的是,我没有想到另一种方式。
转置数据库.txt:
awk '{for(i=1;i<=NF;i++){a[NR,i]=$i}}NF>p{p=NF}END{for(j=1;j<=p;j++ ){str=a[1,j];for(i=2;i<=NR;i++){str=str" "a[i,j];}print str}}' 数据库.txt > 数据库.tmp
更具可读性(相同的命令):
awk '
{
for (i=1; i<=NF; i++) {
a[NR,i] = $i
}
}
NF>p { p = NF }
END {
for(j=1; j<=p; j++) {
str=a[1,j]
for(i=2; i<=NR; i++){
str=str" "a[i,j];
}
print str
}
}' database.txt > database.tmp
2.读取带有ids的文件并从转置的database.tmp中grep所有id:
while read id region ; do grep -m 1 $id database.tmp >> $region.txt.tmp ; done < population.txt
3.将所有region.txt.tmp文件转置为您需要的形式:
for region_file in *txt.tmp ; do awk '{for(i=1;i<=NF;i++){a[NR,i]=$i}}NF>p{p=NF}END{for(j=1;j<=p;j++){str=a[1,j];for(i=2;i<=NR;i++){str=str" "a[i,j];}print str}}' $region_file > ${region_file%.tmp} ; done
4.删除所有临时文件