Bash 连接多个文件中的列

Bash 连接多个文件中的列

我身边有20不同文件夹中的文件,我为其创建了一个单独的文件,名为pathtofiles.inwhichlists小路信息:

/home/users/gray_wolf/unix/File_1.tsv
/home/users/gray_wolf/unix/File_2.tsv
.
.
.

每个文件包含大约11 大约100,000 。例子:

文件_1:

Chromosome    begin    end    .....
chr1          1000     2000
chr1          2000     3000
chr1          4000     5000
chr1          5000     6000
chr1          10000    12000
chr1          12000    13000

文件_2:

Chromosome    begin    end    .....
chr1          1000     2000
chr1          4000     5000
chr1          5000     6000
chr1          6000     7000
chr1          10000    12000
chr1          13000    14000

最终所需的文件格式:

Chromosome    begin    end     Column5                    column8 
chr1          1000     2000    File1,File2,File3...       File1,File2,File3...
chr1          2000     3000    File1,File2(0),File3       File1,File2(0),File3...
chr1          4000     5000    File1,File2,File3...       File1,File2,File3...
chr1          5000     6000    File1,File2,File3...       File1,File2,File3...
chr1          6000     7000    File1,File2,File3...       File1,File2,File3...
chr1          10000    12000   File1,File2,File3...       File1,File2,File3...
chr1          12000    13000   File1,File2,File3...       File1,File2,File3...
chr1          13000    14000   File1(0),File2,File3...    File1(0),File2,File3...

当我这样pathtofile.in传递时:script.sh

./script.sh < pathtofile.in

...脚本应该读取给出的文件pathtofiles.in并输出5- 列制表符分隔列表。首先3输出列应该是3 每个文件的合并的。这4第 输出列应该是相应的行5所有输入文件的第 列,(与 的顺序相同pathtofiles.in),以逗号分隔。这5第 输出列应该是相应的行8所有输入文件的第 列,以逗号分隔。

如果给定的输入行缺少一列8,(或列5),在其位置输出“(0)”。

我尝试单独切割列并使用粘贴/加入函数,但由于每个文件中有不同的行数,因此顺序会出错。我怎样才能使用awk或在以下运行的其他命令巴什

提前致谢。

〜M

答案1

一个简单的解决方案:将paste三个文件放在一起,然后获取所需的列:

paste -d' ' file1 file2 file3  |\
awk 'BEGIN { FS = " +" } { NR ==1} { printf "%-10s%-7s%-7s  %-12s  %-12s\n" $1,$2,$3,$6,$7 } { NR >=2 } { printf "%-10s%-7s%-7s  %s,%s%s  %s%s%s\n" $1,$2,$3,$6,$7,$8,$9,$10,$11 } '

这必须根据您的文件和输出格式的喜好来采用。说明:

1) paste -d' '-> 垂直方向合并树文件,使用空格作为-d分隔符。

2)将其通过管道传输到awk(并在新行中继续命令|\以提高可读性)

2.1) BEGIN { FS = " +" }- 对于以下所有内容,使用一个或多个 (+) 空格作为字段分隔符

2.2) 在第一行{ NR ==1}打印字段 1,2,3,6,7 ( $1,$2 ...) ,格式如下(用双引号括起来)

%-10s固定 10 个字符长的字符串(其余部分填充空格,左对齐)。

两次相同的 7 个字符长度,然后两个空格,一个 12 个字符的长字符串,两个空格,一个 12 个字符的字符串。\n最后添加一个新行。

(在部分中找到{ printf "%-10s%-7s%-7s %-12s %-12s\n" $1,$2,$3,$6,$7 }

2.2) 数据:从第二行及更多{ NR >=2 }行开始打印列$1,$2,$3,$6,$7,$8,$9,$10,$11,格式如下%-10s%-7s%-7s %s,%s,%s %s,%s,%s\n

与上面类似,但现在例如第 6,7,8 列是任意长度并用逗号分隔%s,%s,%s

答案2

您需要一个可以同时打开所有输入文件的程序。 awkgetline <file语法,所以 that 或 perl 将是不错的选择。或者您知道的任何其他高级语言。

为此我会选择 Perl。有http://www.bioperl.org/带有专门用于处理基因序列数据文件格式的perl模块。

我半途而废,意识到它可能比我想象的更复杂。您必须执行多路 diff 类型的算法来处理其中一个输入文件具有与其他文件不同的列 2/3 值的一般情况。您不能只是继续阅读其中的行,直到找到您要查找的开始/结束对的匹配项,因为可能不存在。

所以我认为你最好的选择是优先级队列或获得排序输入的东西。对于每个文件,读取直到您在队列中插入的行落后于您开始处理当前文件之前的行。 (或者,直到您刚刚读取的行是 pqueue 的新尾部)。


如果您的文件很容易同时全部放入内存,则使用关联数组(由 column2:column3 索引)构建输出将使编码变得更容易。那么您就不需要优先级队列,也不需要找出接下来要前进的文件。


这是我到目前为止的代码。它只是循环输入行而不处理乱序情况或合并。当我意识到这比 stackexchange 答案的合理范围大时,我就停下来了,但这可能会给你一个开始。

#!/usr/bin/perl -w

my @f = @ARGV;  # list of files to process


sub getfields($) {
    my $file = $_[0];
    my $ln = <$file> or return ();  # sentinel for EOF
    my @fl = split ' ', $ln, 9;
    return ( $fl[0], $fl[1], $fl[2], $fl[4], $fl[7] );
}

# open each filename in @f, storing the file handles in @f.
foreach (@f) {
    open $_, '<', $_  or die "opening $_: $!";
}

my $newdata = 0;
do {
    $newdata = 0;
    foreach my $fd (@f) {
    my @fl = getfields($fd);
    next if ! (@fl);  # end of file on $fd.  TODO: take it out of @f?
    $newdata = 1;
    print join("|", @fl), "\n";  # debug
    }
} while ($newdata);  # done when all files are EOF

相关内容