有人可以通过这个 awk 代码来合并多个文件吗?
Can someone walk through this awk code for merging multiple files?
我正在使用 awk 合并多个 (>3) 个文件,我想保留 headers。我发现以前的 post 完全符合我的需要,但我不太明白发生了什么。我希望有人可以引导我完成它,以便我可以从中学习! (我尝试评论原版 post 但没有足够的声誉)
此代码
awk '{a[FNR]=((a[FNR])?a[FNR]FS:[=10=])}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:[=15=])}END{for(i=1;i<=FNR;i++) print a[i]}' f* > merge.txt
我无法理解代码的第一部分 {a[FNR]=((a[FNR])?a[FNR]FS:[=16=])}
,但理解代码第二部分中的循环。
我认为在代码的第一部分中,正在建立一个数组。代码遍历并检查第一列 id
上的匹配记录,如果匹配则追加第二列 (</code>) <code>value
并打印整个记录 ([=20 =]
).
但是...我不明白开头的语法。何时确定第一列 id
在所有三个文件中都相同并且只添加第二列?
首先是数据:
file1 file2 file3
NR FNR NR FNR NR FNR
================ ================ ================
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 : [=13=] )
可以写成:
if(a[FNR]=="") # actually if(a[FNR]=="" || a[FNR]==0)
a[FNR]=[=11=] # a[FNR] is "id value1" when NR==1
else
a[FNR]=a[FNR] FS # 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.
}
.
如果您正在寻找替代方案,这里有一个基于 paste
的解决方案:
paste file1 file2 file3 | awk '{print , , , }' OFS='\t'
id value1 value2 value3
a 10 90 0
b 30 30 1
c 50 20 25
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
那个?是三元运算符,意思是,如果 a[FNR] 中已经有内容,则将当前记录的 $2 附加到那里的内容,否则它为空,因此存储整个记录(即 $0)。
可能有助于解释事情的伪代码:
if a[FNR] != ""
a[FNR] = a[FNR] : FS :
else
a[FNR] = [=10=]
您可以看到在删除第一个文件后每条记录中的 a、b、c - 可能是 x、y、z 并且此程序不会在意。它采用第二个字段并附加到 a[2]、a[3] 等
您可以使用 awk
和 pr
来执行此操作:
$ pr -mts$'\t' f1 <(awk '{print }' f2) <(awk '{print }' f3)
id value1 value2 value3
a 10 90 0
b 30 30 1
c 50 20 25
(这些是列之间的选项卡)
或以同样的方式使用paste
:
$ paste f1 <(awk '{print }' f2) <(awk '{print }' f3)
id value1 value2 value3
a 10 90 0
b 30 30 1
c 50 20 25
该代码有错误且不必要地复杂,请改用此代码:
$ awk 'NR==FNR{a[FNR]=[=10=]; next} {a[FNR] = a[FNR] OFS } 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]=[=11=];next} {a[FNR] = a[FNR] OFS } 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" }
!( in a) { ids[++numIds]= }
{ a[][ARGIND]= }
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
以对其进行测试。
如果您有任何问题,请随时提出,但我认为代码非常清楚。
我正在使用 awk 合并多个 (>3) 个文件,我想保留 headers。我发现以前的 post 完全符合我的需要,但我不太明白发生了什么。我希望有人可以引导我完成它,以便我可以从中学习! (我尝试评论原版 post 但没有足够的声誉)
此代码
awk '{a[FNR]=((a[FNR])?a[FNR]FS:[=10=])}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:[=15=])}END{for(i=1;i<=FNR;i++) print a[i]}' f* > merge.txt
我无法理解代码的第一部分 {a[FNR]=((a[FNR])?a[FNR]FS:[=16=])}
,但理解代码第二部分中的循环。
我认为在代码的第一部分中,正在建立一个数组。代码遍历并检查第一列 id
上的匹配记录,如果匹配则追加第二列 (</code>) <code>value
并打印整个记录 ([=20 =]
).
但是...我不明白开头的语法。何时确定第一列 id
在所有三个文件中都相同并且只添加第二列?
首先是数据:
file1 file2 file3
NR FNR NR FNR NR FNR
================ ================ ================
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 : [=13=] )
可以写成:
if(a[FNR]=="") # actually if(a[FNR]=="" || a[FNR]==0)
a[FNR]=[=11=] # a[FNR] is "id value1" when NR==1
else
a[FNR]=a[FNR] FS # 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.
}
如果您正在寻找替代方案,这里有一个基于 paste
的解决方案:
paste file1 file2 file3 | awk '{print , , , }' OFS='\t'
id value1 value2 value3
a 10 90 0
b 30 30 1
c 50 20 25
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
那个?是三元运算符,意思是,如果 a[FNR] 中已经有内容,则将当前记录的 $2 附加到那里的内容,否则它为空,因此存储整个记录(即 $0)。
可能有助于解释事情的伪代码:
if a[FNR] != ""
a[FNR] = a[FNR] : FS :
else
a[FNR] = [=10=]
您可以看到在删除第一个文件后每条记录中的 a、b、c - 可能是 x、y、z 并且此程序不会在意。它采用第二个字段并附加到 a[2]、a[3] 等
您可以使用 awk
和 pr
来执行此操作:
$ pr -mts$'\t' f1 <(awk '{print }' f2) <(awk '{print }' f3)
id value1 value2 value3
a 10 90 0
b 30 30 1
c 50 20 25
(这些是列之间的选项卡)
或以同样的方式使用paste
:
$ paste f1 <(awk '{print }' f2) <(awk '{print }' f3)
id value1 value2 value3
a 10 90 0
b 30 30 1
c 50 20 25
该代码有错误且不必要地复杂,请改用此代码:
$ awk 'NR==FNR{a[FNR]=[=10=]; next} {a[FNR] = a[FNR] OFS } 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]=[=11=];next} {a[FNR] = a[FNR] OFS } 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" }
!( in a) { ids[++numIds]= }
{ a[][ARGIND]= }
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
以对其进行测试。
如果您有任何问题,请随时提出,但我认为代码非常清楚。