我有多个文件,SRR3384742.Gene.out.tab
SRR3384743.Gene.out.tab
SRR3384744.Gene.out.tab
还有更多按此顺序排列的文件。我从这些文件中提取第一列和第四列并将其存储在输出文件中。我试图确保当我的脚本读取新文件时,它应该以选项卡分隔的方式提取数据,而不是将数据附加在每个文件的末尾。
输入文件:
SRR3384742.Gene.out.tab
N_unmapped 313860 313860 313860
N_multimapping 5786679 5786679 5786679
N_noFeature 286816 31696770 438410
N_ambiguous 1283487 32117 65902
AT1G01010 301 0 301
AT1G01020 623 1 622
AT1G03987 5 5 0
AT1G01030 151 2 149
SRR3384743.Gene.out.tab
N_unmapped 780346 780346 780346
N_multimapping 4621162 4621162 4621162
N_noFeature 182428 28470016 362650
N_ambiguous 1451612 43059 117293
AT1G01010 154 3 151
AT1G01020 685 2 683
AT1G03987 0 0 0
AT1G01030 63 0 63
我得到的输出:
SRR3384742.Gene.out.tab
AT1G01010 301
AT1G01020 622
AT1G03987 0
AT1G01030 149
SRR3384743.Gene.out.tab
AT1G01010 151
AT1G01020 683
AT1G03987 0
AT1G01030 63
所需输出:
SRR3384742.Gene.out.tab SRR3384743.Gene.out.tab
AT1G01010 301 151
AT1G01020 622 683
AT1G03987 0 0
AT1G01030 149 63
我尝试了以下脚本:
for sample in *Gene.out.tab; do echo -en $sample "\n"; awk 'NR>4 {print $1 "\t" $4}' $sample; awk '{print $0, $sample}' OFS='\t' $sample; done > output
答案1
这应该使用 GNU 为您提供注释中描述的输出awk
:
gawk 'FNR==1{names[c++]=FILENAME}
FNR>4{ lines[$1] = "x"lines[$1] ? lines[$1]"\t"$4 : $4; }
END{
for(i=0;i<=c;i++){
printf "\t%s",names[i]
}
printf "\n";
for(i in lines){
print i,lines[i]
}
}' *Gene.out.tab
SRR3384742.Gene.out.tab SRR3384743.Gene.out.tab
AT1G01010 301 151
AT1G01020 622 683
AT1G01030 149 63
AT1G03987 0 0
并且,为了使所有内容在视觉上也很好地对齐,请将其传递column
:
$ gawk 'FNR==1{names[c++]=FILENAME}FNR>4{ lines[$1] = "x"lines[$1] ? lines[$1]"\t"$4 : $4; } END{ for(i=0;i<=c;i++){printf "\t%s",names[i];} printf "\n"; for(i in lines){ print i,lines[i]}}' *Gene.out.tab | column -s$'\t' -t
SRR3384742.Gene.out.tab SRR3384743.Gene.out.tab
AT1G01010 301 151
AT1G01020 622 683
AT1G01030 149 63
AT1G03987 0 0
FNR
是一个特殊的 awk 变量,它始终保存当前正在处理的文件的行号。FILENAME
是一个 GNUawk
特殊变量,保存当前正在处理的文件的名称。
FNR==1{names[c++]=FILENAME}
:如果这是一个输入文件的第一行,则使用该变量作为其值为文件名的数组的c
索引,并增加其值 yb 1 ( )。处理完所有文件后,将是第一个文件名,将是第二个文件名,依此类推。names
c++
files[0]
files[1]
FNR>4{ lines[$1] = "x"lines[$1] ? lines[$1]"\t"$4 : $4; }
:这相当于:if(FNR>4){ if("x"lines[$1]){ lines[$1]"\t"$4 else{ lines[$1] = $4 } }
如果当前输入文件的行号为 5 或更多,请检查第一个字段在数组中是否有关联值
lines
。我们检查使用,"x"lines[$i]
因为如果lines[$1]
是0
,那么测试将为假,但x0
为真,因此可以x
防止这种情况。因此,如果我们确实有一个值,我们会向其附加一个制表符和当前行的第二个字段,如果我们没有值,我们会将其设置为当前行的第四个字段。END{ ... }
:处理完所有输入后执行此操作。for(i=0;i<=c;i++){printf "\t%s",names[i]}; printf "\n";
:打印names
数组中的每个文件名,前面带有制表符。我们希望前导选项卡能够确保标题行和内容中的字段数量相同。打印文件名后,打印换行符。for(i in lines){print i,lines[i]}
:对于数组的每个索引lines
,打印索引(ID),然后打印第一步中存储的关联值。
局限性:这需要将所有输出数据存储在内存中。在现代机器上这确实不应该成为问题,因为我们只存储 ID,每个文件每个 ID 只存储一个值,因此它应该能够在一台相当不错的机器上阻塞之前处理大量输入,但它可能会成为一个问题。确实存在大量数据的问题。
答案2
使用join
join -o '1.1 1.4 2.4' SRR3384742.Gene.out.tab SRR3384743.Gene.out.tab
N_unmapped 313860 780346
N_multimapping 5786679 4621162
N_noFeature 438410 362650
N_ambiguous 65902 117293
AT1G01010 301 151
AT1G01020 622 683
AT1G03987 0 0
AT1G01030 149 63
使用paste
我想paste
这就是您正在寻找的:
paste <(cut -f 1,4 SRR3384742.Gene.out.tab) <(cut -f4 SRR3384743.Gene.out.tab)
N_unmapped 313860 780346
N_multimapping 5786679 4621162
N_noFeature 438410 362650
N_ambiguous 65902 117293
AT1G01010 301 151
AT1G01020 622 683
AT1G03987 0 0
AT1G01030 149 63
该解决方案假设所有内容都*.tab
应该具有:
- 相同的行数。
- 相同的顺序。
这是一个打印标题并处理多个文件的脚本:
#!/bin/bash
set -euo pipefail
echo $(printf '%s\t' "$@")
first_file=$1
shift
fifos="<(cut -f1,4 $first_file) "$(printf '<(cut -f4 %s) ' "$@")
eval "paste $fifos"
command *.out.tab
SRR3384742.Gene.out.tab SRR3384743.Gene.out.tab
N_unmapped 313860 780346
N_multimapping 5786679 4621162
N_noFeature 438410 362650
N_ambiguous 65902 117293
AT1G01010 301 151
AT1G01020 622 683
AT1G03987 0 0
AT1G01030 149 63
答案3
for i in *.tab; do echo $i >/tmp/"$i"_out.txt ; awk '/^AT/{print $1,$4}' $i >> /tmp/"$i"_out.txt ; done
paste /tmp/*_out.txt| awk '{$3="";print }'
输出
SRR3384742.Gene.out.tab SRR3384743.Gene.out.tab
AT1G01010 301 151
AT1G01020 622 683
AT1G03987 0 0
AT1G01030 149 63