2017-04-17 44 views
3

我正在使用awk合并多个(> 3)文件,并且我想保留标题。我发现以前的帖子完全符合我的需求,但我不太明白发生了什么。我希望有人能够通过它,让我可以从中学习! (我想评论对原来的职位,但没有足够的声誉)有人可以通过这个awk代码合并多个文件吗?

此代码

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上的匹配记录,如果匹配,则附加第二列($2value并打印整个记录($0)。

但是...我不明白开始语法。什么时候确定第一列id在所有三个文件中都是相同的,并且只添加第二列?

+1

该代码不使用或引用'id',它根据行的顺序执行所有操作,而不是每行的第一个字段的值。如果你需要检查'id's,因为它们可以跨文件变化,那么你需要一个不同的解决方案。 –

+1

啊。很高兴知道。我确实希望程序检查ID。事实证明,对于当前使用ID的文件是一样的,但将来并不总是如此。如果我想引用id,那么最好的解决方案是什么?用'$ 1'替换'FNR'中的'FNR')?[FNR] FS $ 2:$ 0)'? - NVM在下面看到您的代码。谢谢! – moxed

回答

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多维数组,只是已经c改为x输入文件2进行测试。

随意问你是否有问题,但我认为代码很清楚。

+1

这太好了。谢谢! – moxed

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

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

The?是三元运算符,并且表示如果[FNR]中已经有东西存在,那么将当前记录的$ 2附加到那里,否则它是空的,因此存储整个记录(即$ 0)。

伪代码,可能有助于解释事情:

if a[FNR] != "" 
    a[FNR] = a[FNR] : FS : $2 
else 
    a[FNR] = $0 

你可以看到,从第一个文件被删除后,每个记录的A,B,C - 可能是X,Y,Z,并将该软件止跌不在乎。它走的是第二场追加到[2],[3]等

1

您可以使用awkpr做到这一点:

$ 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 
+1

fyi:默认是制表符,所以'-mts'就足够了 – Sundeep

相关问题