合并文件在空白字段中打印0

时间:2019-08-04 20:54:12

标签: awk

我有5个制表符delim文件 文件0基本上是一个密钥

A 
C 
F 
AA 
BC 
CC 
D 
KKK 
S

file1

A 2 
C 3 
F 5 
AA 5 
BC 4 
D 7

file2

A 2
C 3
F 7
D 10

file3

A 2
C 2
F 5
CC 4
D 7

file4

A 1
C 3
F 5
CC 4
D 7
KKK 10

我想基于第一列合并所有文件,并在缺少的字段中打印0。

A 2 2 2 1
C 3 3 2 3
F 5 7 5 5
AA 5 0 0 0
BC 4 0 0 0
CC 0 0 4 4
D 7 10 7 7
KKK 0 0 0 10
S 0 0 0 0

列必须保持输入file0,file1,file2,file3,file4的顺序

5 个答案:

答案 0 :(得分:2)

不是awk,但是join的确切含义是在公共字段上进行这种文件联接。一次只处理两个文件使它有点复杂。您必须将每个文件的结果作为第一个文件传送到下一个文件中。

$ join -o 0,2.2 -e0 -a1 <(sort file0) <(sort file1) \
  | join -o 0,1.2,2.2 -e0 -a1 - <(sort file2) \
  | join -o 0,1.2,1.3,2.2 -e0 -a1 - <(sort file3) \
  | join -o 0,1.2,1.3,1.4,2.2 -e0 -a1 - <(sort file4) \
  | tr ' ' '\t'
A       2       2       2       1
AA      5       0       0       0
BC      4       0       0       0
C       3       3       2       3
CC      0       0       4       4
D       7       10      7       7
F       5       7       5       5
KKK     0       0       0       10
S       0       0       0       0

注意事项:这需要像bashzsh这样的shell能够理解<(command)重定向。预先对所有文件进行排序是一种选择。或者指出,即使join通常要求其输入文件在要连接的列上进行排序,但无论如何,它都无需为该特定输入进行排序。

答案 1 :(得分:2)

awk解决方案

awk '
    FNR==1{f++}
    {
        a[f""$1]=$2
        b[$1]++
    }
    END{
        for(i in b){
            printf i" "
            for(j=1;j<=f;j++){
                tmp=j""i
                if(tmp in a){
                    printf a[tmp]" "
                }else{
                    printf 0" "
                }
            }
            print ""
        }
    }
' file*

输出量:

A  2 2 2 1 
AA  5 0 0 0 
BC  4 0 0 0 
C  3 3 2 3 
CC  0 0 4 4 
D  7 10 7 7 
F  5 7 5 5 
KKK  0 0 0 10 
S  0 0 0 0

首先,我将每个文件号的每个值和键值存储在变量a中 然后将所有唯一键存储在变量b

并在END块中,检查密钥是否存在,如果存在,则打印它,或者不存在,则打印0

我们可以删除file0,如果删除,awk仅显示文件1,2,3,4,..中的存在密钥。

答案 2 :(得分:2)

我要等到您在问题中做出自己的尝试,但是既然您已经有2个答案了......

$ cat tst.awk
NR==FNR {
    key2rowNr[$1] = ++numRows
    rowNr2key[numRows] = $1
    next
}
FNR==1 { ++numCols }
{
    rowNr = key2rowNr[$1]
    vals[rowNr,numCols] = $2
}
END {
    for (rowNr=1; rowNr<=numRows; rowNr++) {
        printf "%s", rowNr2key[rowNr]
        for (colNr=1; colNr<=numCols; colNr++) {
            printf "%s%d", OFS, vals[rowNr,colNr]
        }
        print ""
    }
}

$ awk -f tst.awk file0 file1 file2 file3 file4
A 2 2 2 1
C 3 3 2 3
F 5 7 5 5
AA 5 0 0 0
BC 4 0 0 0
CC 0 0 4 4
D 7 10 7 7
KKK 0 0 0 10
S 0 0 0 0

答案 3 :(得分:1)

使用GNU awk,您可以使用ENDFILE子句来确保所有行中都有足够的元素,例如:

parse.awk

BEGIN { OFS = "\t" }

# Collect all information into the `h` hash
{ h[$1] = (ARGIND == 1 ? $1 : h[$1] OFS $2) }

# At the end of each file do the necessary padding
ENDFILE {
  for(k in h) {
    elems = split(h[k], a, OFS)
    if (elems !=  ARGIND)
      h[k] = h[k] OFS 0
  }
}

# Print the content of `h`
END {
  for(k in h)
    print h[k]
}

像这样运行它:

awk -f parse.awk file[0-4]

输出:

AA  5   0   0   0
A   2   2   2   1
C   3   3   2   3
D   7   10  7   7
BC  4   0   0   0
CC  0   0   4   4
S   0   0   0   0
KKK 0   0   0   10
F   5   7   5   5

NB:此解决方案假定每个文件只有两列(第一个除外)。

答案 4 :(得分:1)

您可以使用coreutils join确定缺少的字段并将其添加到每个文件中:

sort file0 > file0.sorted
for file in file[1-4]; do 
  { 
    cat $file
    join -j 1 -v 1 file0.sorted <(sort $file) | sed 's/$/ 0/'
  } | sort > $file.sorted
done

现在,您只需将它们paste一起使用:

paste file0.sorted \
  <(cut -d' ' -f2 file1.sorted) \
  <(cut -d' ' -f2 file2.sorted) \
  <(cut -d' ' -f2 file3.sorted) \
  <(cut -d' ' -f2 file4.sorted)

输出:

A   2   2   2   1
AA  5   0   0   0
BC  4   0   0   0
C   3   3   2   3
CC  0   0   4   4
D   7   10  7   7
F   5   7   5   5
KKK 0   0   0   10
S   0   0   0   0