我身边有20不同文件夹中的文件,我为其创建了一个单独的文件,名为pathtofiles.in
whichlists小路信息:
/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
您需要一个可以同时打开所有输入文件的程序。 awk
有getline <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