awk + ​​bash:组合任意数量的文件

时间:2015-10-26 16:09:19

标签: bash awk gawk

我有一个脚本,它接受大量具有相同布局但数据不同的数据文件,并将指定的数据列合并到一个新文件中,如下所示:

gawk '{
        names[$1]= 1;
        data[$1,ARGIND]= $2
} END {
        for (i in names) print i"\t"data[i,1]"\t"data[i,2]"\t"data[i,3]
}' $1 $2 $3 > combined_data.txt

...可以在第一列中找到行ID,在第二列中找到感兴趣的数据。

这很好用,但不适用于任意数量的文件。虽然我可以在最后一行添加$4 $5 ... $n,直到我认为我需要的最大文件数,并在上面的行中添加相等n量的"\t"data[i,4]"\t"data[i,5] ... "\t"data[i,n](这似乎对小于n的文件有用; awk似乎忽略了n大于这些情况下的输入文件数),这看起来像是一个“丑陋”的解决方案。有没有办法使这个脚本(或给出相同结果的东西)采用任意数量的输入文件?

或者,更好的是,你能以某种方式在其中加入find,搜索子文件夹并找到符合某些标准的文件吗?

以下是一些示例数据:

file.1中

A      554
B       13
C      634
D       84
E        9

file.2中:

C      TRUE
E      TRUE
F      FALSE

预期产出:

A      554
B       13
C      634       TRUE
D       84
E        9       TRUE
F                FALSE

4 个答案:

答案 0 :(得分:3)

这可能是您正在寻找的(使用GNU awk作为ARGIND就像您的原始脚本一样):

$ cat tst.awk
BEGIN { OFS="\t" }
!seen[$1]++ { keys[++numKeys]=$1 }
{ vals[$1,ARGIND]=$2 }
END {
    for (rowNr=1; rowNr<=numKeys; rowNr++) {
        key = keys[rowNr]
        printf "%s%s", key, OFS
        for (colNr=1; colNr<=ARGIND; colNr++) {
            printf "%s%s", vals[key,colNr], (colNr<ARGIND?OFS:ORS)
        }
    }
}

$ awk -f tst.awk file1 file2
A       554
B       13
C       634     TRUE
D       84
E       9       TRUE
F               FALSE

如果您不关心输出行的顺序,那么您只需要:

BEGIN { OFS="\t" }
{ vals[$1,ARGIND]=$2; keys[$1] }
END {
    for (key in keys) {
        printf "%s%s", key, OFS
        for (colNr=1; colNr<=ARGIND; colNr++) {
            printf "%s%s", vals[key,colNr], (colNr<ARGIND?OFS:ORS)
        }
    }
}

答案 1 :(得分:0)

您可以通过ARGV列表上的重定向getline访问任意数量的文件(绕过awk的默认文件处理(通过BEGIN并退出)):

awk 'BEGIN {
  for(i=1;i<=ARGC;++i){
    while (getline < ARGV[i]) {
      ...
      }
    }
  <END-type code>
  exit}' $(find -type f ...)

答案 2 :(得分:0)

假设输入文件的这个命名方案:1 2 ....

   gawk '{ 
        names[$1]=$1
        data[$1,ARGIND]=$2
      } 
      END {
        for (i in names) {
           printf("%s\t",i)
           for (x=1;x<=ARGIND;x++) {
             printf("%s\t", data[i,x])
             }
           print ""
           }
       }' [0-9]* > combined_data.txt

结果:

A   554 
B   13  
C   634 TRUE
D   84  
E   9   TRUE
F       FALSE

答案 3 :(得分:0)

使用joinbashawktrfile1file2file3等的另一种解决方案。排序

<强> multijoin.sh

#!/bin/bash
function __t { 
  join -a1 -a2 -o '1.1 2.1 1.2 2.2' - "$1" | 
  awk -vFS='[ ]' '{print ($1!=""?$1:$2),$3"_"$4;}'; 
}
CMD="cat '$1'"
for i in `seq 2 $#`; do
  CMD="$CMD | __t '${@:$i:1}'";
done
eval "$CMD | tr '_' '\t' | tr ' ' '\t'";

或,递归版

#!/bin/bash
function __t { 
  join -a1 -a2 -o '1.1 2.1 1.2 2.2' - "$1" | 
  awk -vFS='[ ]' '{print ($1!=""?$1:$2),$3"_"$4;}'; 
}
function __r { 
  if [[ "$#" -gt 1 ]]; then
    __t "$1" | __r "${@:2}"; 
  else
    __t "$1"; 
  fi
}
__r "${@:2}" < "$1" | tr '_' '\t' | tr ' ' '\t'

注意:数据不能包含字符_,这被用作通配符

你明白了,

./multijoin file1 file2
A   554
B   13
C   634 TRUE
D   84
E   9   TRUE
F       FALSE
  

例如,如果file3包含

A    111
D    222
E    333
./multijoin file1 file2 file3

你明白了,

A   554       111
B   13      
C   634 TRUE    
D   84        222
E   9   TRUE  333
F       FALSE