合并多个txt文件

合并多个txt文件

我有多个txt文件,其中3个如下:

文件1:

sample  input filtered
5809378   1      2
5811151   3      4
5811237   5      6

文件2:

sample  chi tri
5809378  7   8
5811151      
5811237  9   10

文件3:

sample   bra  doe
5809378  11 
5811151        12
5811237  13    14

我想根据第一列:样本 ID 将这 3 个文件合并为 1 个文件,因此输出如下所示:

sample  input  filters  chi  tri   bra   doe
5809378    1     2      7     8     11     0
5811151    3     4      0     0     0     12
5811237    5     6      9     10    13    14

请注意,如果没有相应的数据,则必须有一个零,或者在最坏的情况下,有一个空选项卡。

我尝试过 awk 和 join,但找不到最佳解决方案。有人有什么想法吗?

答案1

从我的角度来看, file3 并不完全正确,因为行

5811151 12

在第二列或第三列中可能有数字“12”,具体取决于我们读取文件的方式(未定义列分隔符并且在各处都不相同)。

反正。

a=$(cat file1|awk '{if($2==""){$2="0"};if($3==""){$3="0"}; print $1,$2,$3}'|sort);
for f in file2 file3; do
    b=$(cat $f|awk '{if($2==""){$2="0"};if($3==""){$3="0"}; print $1,$2,$3}'|sort);
    a=$(join -j 1 <(echo "${a}") <(echo "${b}"));
done;
echo "${a}"|sort -n

输出是

sample input filtered chi tri bra doe
5809378 1 2 7 8 11 0
5811151 3 4 0 0 12 0
5811237 5 6 9 10 13 14

所以,我们

1)每个拍摄的文件都经过转换

cat file|awk '{if($2==""){$2="0"};if($3==""){$3="0"}; print $1,$2,$3}'|sort

用“0”替换缺失的数字并对行进行排序。

2)在一个循环中,我们获取下一个文件并将其合并到上一个结果

join -j 1 file_current file_next

因此,“for f in file2 file3; do”行可以更改为包含更多文件,例如“for f in file2 file3 file4 file5 file6; do”。

3)打印结果,按照字符串数值排序(首先排序并打印列名称)。如果需要的话,我们也可以在这里格式化输出。

答案2

假设您的文件具有制表符分隔的列(因此您可以在类似第三行的情况下判断哪些列为空file3),并且像您的示例一样在第一列上排序,bash 脚本如下所示:

#!/bin/bash

function fixup() { # Add 0's to blank columns
    awk -v cols="$2" 'BEGIN { FS = OFS = "\t" }
                      { for (i = 1; i <= cols; i++)
                         if ($i == "") $i = 0
                      } 1' "$1"
}

join --header -t$'\t' -j1 \
     <(join --header -t$'\t' -j1 <(fixup "$1" 3) \
                                 <(fixup "$2" 3)) \
     <(fixup "$3" 3)

会做的:

$ ./combine file1 file2 file3
sample  input   filtered        chi     tri     bra     doe
5809378 1       2       7       8       11      0
5811151 3       4       0       0       0       12
5811237 5       6       9       10      13      14

(确实需要 GNU coreutils 版本join)。

答案3

假设您的起始文件是制表符分隔的,并且空字段仍然是制表符分隔的,您可以使用 awk 对缺失的列进行零填充,例如:

awk -F'     ' '!$2{$2=0}!$3{$3=0}{print}' file1 > file1-n
awk -F'     ' '!$2{$2=0}!$3{$3=0}{print}' file2 > file3-n
awk -F'     ' '!$2{$2=0}!$3{$3=0}{print}' file3 > file3-n

需要明确的是awk -F '<TAB>'。然后你可以使用paste来合并它们,用另一个awk来过滤不需要的列:

bash-$ paste file1-n file2-n file3-n | awk {'print $1, $2, $3, $5, $6, $8, $9'}
sample input filtered chi tri bra doe
5809378 1 2 7 8 11 0
5811151 3 4 0 0 0 12
5811237 5 6 9 10 13 14

或者,如果人类可读性很重要,则将列隔开:

bash-$ paste file1-n file2-n file3-n | awk {'printf "%-7s %-5s %-8s %-3s %-3s %-3s %-3s\n", $1, $2, $3, $5, $6, $8, $9'}
sample  input filtered chi tri bra doe
5809378 1     2        7   8   11  0
5811151 3     4        0   0   0   12
5811237 5     6        9   10  13  14

答案4

另一种选择,假设有一个tab分隔符

0首先通过在 double 之间插入 a 或在没有文本(即它是标题)或数字的tab行结束处插入 a 来修复文件$[^[:alnum:]]

TAB=$'\t'; sed -Ei "s/([^[:alnum:]]|${TAB})($|${TAB})/\10\2/g" file*

然后就只有join他们了

join --header file2 file3 | join --header file1 - | column -t

输出

sample   input  filtered  chi  tri  bra  doe
5809378  1      2         7    8    11   0
5811151  3      4         0    0    0    12
5811237  5      6         9    10   13   14

相关内容