我正在使用awk合并多个(> 3)文件,我想保留标题。我发现之前的帖子完全符合我的要求,但我不太明白发生了什么。我希望有人能指引我,所以我可以从中学习! (我尝试评论原帖,但没有足够的声誉)
此代码
awk '{a[FNR]=((a[FNR])?a[FNR]FS$2:$0)}END{for(i=1;i<=FNR;i++) print a[i]}' f*
根据需要转换输入文件。见下面的示例表。
输入文件:
FILE1.TXT:
id value1
a 10
b 30
c 50
FILE2.TXT:
id value2
a 90
b 30
c 20
file3.txt:
id value3
a 0
b 1
c 25
期望的输出
merge.txt:
id value1 value2 value3
a 10 90 0
b 30 30 1
c 50 20 25
再次,这是代码
awk '{a[FNR]=((a[FNR])?a[FNR]FS$2:$0)}END{for(i=1;i<=FNR;i++) print a[i]}' f* > merge.txt
我无法理解代码{a[FNR]=((a[FNR])?a[FNR]FS$2:$0)}
的第一部分,但理解代码第二部分的循环。
我认为在代码的第一部分中,正在建立一个数组。代码将运行并检查第一列id
上的匹配记录,如果匹配,则附加第二列($2
)value
并打印整个记录({{1 }})。
但是......我不明白开头的语法。何时确定第一列$0
在所有三个文件中是相同的,并且只添加第二列?
答案 0 :(得分:4)
首先是数据:
file1 file2 file3
NR FNR $1 $2 NR FNR $1 $2 NR FNR $1 $2
================ ================ ================
1 1 id value1 5 1 id value2 9 1 id value3
2 2 a 10 6 2 a 90 10 2 a 0
3 3 b 30 7 3 b 30 11 3 b 1
4 4 c 50 8 4 c 20 12 4 c 25
第一部分:a[FNR]=( (a[FNR]) ? a[FNR]FS$2 : $0 )
可以写成:
if(a[FNR]=="") # actually if(a[FNR]=="" || a[FNR]==0)
a[FNR]=$0 # a[FNR] is "id value1" when NR==1
else
a[FNR]=a[FNR] FS $2 # a[FNR]="id value1" FS "value2" when NR==5
每个文件有4条记录,即。 FNR==4
在每个文件的最后一条记录上,尤其是最后一个文件,因为FNR
的值在处理完最后一个文件后仍然存在:
END { # after hashing all record in all files
for(i=1;i<=FNR;i++) # i=1, 2, 3, 4
print a[i] # print "id value1 value value3" etc.
}
答案 1 :(得分:4)
该代码有错误并且不必要地复杂化,请改用:
$ awk 'NR==FNR{a[FNR]=$0; next} {a[FNR] = a[FNR] OFS $2} END{for (i=1;i<=FNR;i++) print a[i]}' file1 file2 file3
id value1 value2 value3
a 10 90 0
b 30 30 1
c 50 20 25
如果您愿意,将输出传递给列-t以进行对齐:
$ awk 'NR==FNR{a[NR]=$0;next} {a[FNR] = a[FNR] OFS $2} END{for (i=1;i<=FNR;i++) print a[i]}' file1 file2 file3 | column -t
id value1 value2 value3
a 10 90 0
b 30 30 1
c 50 20 25
如果您需要关闭id
s(例如因为它们在文件中不同),那么它将是:
$ awk '
BEGIN { OFS="\t" }
!($1 in a) { ids[++numIds]=$1 }
{ a[$1][ARGIND]=$2 }
END {
for (i=1;i<=numIds;i++) {
id = ids[i]
printf "%s%s", id, OFS
for (j=1;j<=ARGIND;j++) {
printf "%s%s", a[id][j], (j<ARGIND ? OFS : ORS)
}
}
}
' file1 file2 file3 | column -s$'\t' -t
id value1 value2 value3
a 10 90 0
b 30 30 1
c 50 25
x 20
最后一个脚本将GNU awk用于多维数组,并且在输入文件2中将c
更改为x
以进行测试。
随意询问您是否有疑问,但我认为代码非常清楚。
答案 2 :(得分:2)
James has explained pretty well the awk logic in his answer
如果您正在寻找替代方案,那么基于paste
的解决方案:
paste file1 file2 file3 | awk '{print $1, $2, $4, $6}' OFS='\t'
id value1 value2 value3
a 10 90 0
b 30 30 1
c 50 20 25
答案 3 :(得分:1)
FNR是相对于当前输入文件的记录数。所以file1,file2等中的行号http://www.thegeekstuff.com/2010/01/8-powerful-awk-built-in-variables-fs-ofs-rs-ors-nr-nf-filename-fnr/?ref=binfind.com/web
?是三元运算符并且说,如果[FNR]中已存在某些东西,则将当前记录的2美元附加到那里,否则它为空,因此存储整个记录(即$ 0)。
可能有助于解释事情的伪代码:
if a[FNR] != ""
a[FNR] = a[FNR] : FS : $2
else
a[FNR] = $0
你可以看到第一个文件被删除后每条记录中的a,b,c可能是x,y,z,这个程序并不关心。它取第二个字段并附加到[2],[3]等
答案 4 :(得分:1)
您可以awk
使用pr
来执行此操作:
$ pr -mts$'\t' f1 <(awk '{print $2}' f2) <(awk '{print $2}' f3)
id value1 value2 value3
a 10 90 0
b 30 30 1
c 50 20 25
(这些是列之间的标签)
或以同样的方式使用paste
:
$ paste f1 <(awk '{print $2}' f2) <(awk '{print $2}' f3)
id value1 value2 value3
a 10 90 0
b 30 30 1
c 50 20 25