我正在处理许多大型gz文件,如下面的示例(这里只显示了前5行(。
gene_id variant_id tss_distance ma_samples ma_count maf pval_nominal slope slope_se
ENSG00000223972.4 1_13417_C_CGAGA_b37 1548 50 50 0.0766871 0.735446 -0.0468165 0.138428
ENSG00000223972.4 1_17559_G_C_b37 5690 7 7 0.00964187 0.39765 -0.287573 0.339508
ENSG00000223972.4 1_54421_A_G_b37 42552 28 28 0.039548 0.680357 0.0741142 0.179725
ENSG00000223972.4 1_54490_G_A_b37 42621 112 120 0.176471 0.00824733 0.247533 0.093081
下面是我想要的输出。
在这里,我用"_"分割第二列,并根据第二列和第三列选择行(分割后(($2==1和$3>20000(。我把它保存为txt。下面的命令非常有效。
zcat InputData.txt.gz | awk -F "_" '$1=$1' | awk '{if ($2==1 && $3>20000) {print}}' > OutputData.txt
ENSG00000223972.4 1 54421 A G b37 42552 28 28 0.039548 0.680357 0.0741142 0.179725
ENSG00000223972.4 1 54490 G A b37 42621 112 120 0.176471 0.00824733 0.247533 0.093081
但是我想使用GNU并行来加快这个过程,因为我有很多大的gz文件要处理。然而,GNU并行和awk之间似乎存在一些冲突,可能是在引用方面?
我尝试如下单独定义awk选项,但它在输出文件中没有给我任何信息。
在下面的命令中,我只在一个输入文件上运行并行。但我想在多个输入文件上运行,并保存多个输出文件,每个输出文件对应一个输入文件。
例如,
将Data_1.txt.gz输入到OutputData_1.txt
将Data_2.txt.gz输入到OutputData_2.txt
awk1='{ -F "_" "$1=$1" }'
awk2='{if ($2==1 && $3>20000) {print}}'
parallel "zcat {} | awk '$awk1' |awk '$awk2' > OutputData.txt" ::: InputData.txt.gz
有人对这项任务有什么建议吗?非常感谢。
根据@karakfa的建议,这是一种解决方案
chr=1
RegionStart=10000
RegionEnd=50000
zcat InputData.txt.gz | awk -v chr=$chr -v start=$RegionStart -v end=$RegionEnd '{split($2,NewDF,"_")} NewDF[1]==chr && NewDF[2]>start && NewDF[2]<end {gsub("_"," ",$2) ; print > ("OutputData.txt")}'
#This also works using parallel
awkbody='{split($2,NewDF,"_")} NewDF[1]==chr && NewDF[2]>start && NewDF[2]<end {gsub("_"," ",$2) ; print > ("{}_OutputData.txt")}'
parallel "zcat {} | awk -v chr=$chr -v start=$RegionStart -v end=$RegionEnd '$awkbody' " ::: InputData_*.txt.gz
输入文件InputData_1.txt.gz
的输出文件名将为InputData_1.txt.gz_OutputData.txt
https://www.gnu.org/software/parallel/man.html#QUOTING结论:
结论:为了避免处理引用问题,只需编写一个小脚本或函数(记住导出函数-f(并让GNU并行调用它可能会更容易。
所以:
doit() {
zcat "$1" |
awk -F "_" '$1=$1' |
awk '{if ($2==1 && $3>20000) {print}}'
}
export -f doit
parallel 'doit {} > {=s/In/Out/; s/.gz//=}' ::: InputData*.txt.gz
一种方法是使用split
$ awk '{split($2,f2,"_")}
f2[1]==1 && f2[2]>20000 {gsub("_"," ",$2); print > (FILENAME".output")}' file
但是,如果您通过stdin提供数据,awk
将不会捕获要写入的文件名。您可能需要将其作为变量传递给脚本。。。
简单的解决方案是将过滤器组合到单个awk
脚本中,而只有并行才能工作。
以下是一个仅扫描整个input.txt
一次(性能的两倍(的示例解决方案:
awk 'BEGIN{FS="[ ]*[_]?"}$2==1 && $7 > 20000 {print}' input.txt
说明:
BEGIN{FS="[ ]*[_]?"}
使字段分隔符为多个">或"_">
$2==1 && $7 > 20000 {print}
仅打印第2个字段==1且第7个字段>2000 的行
示例调试脚本:
BEGIN{FS="[ ]*[_]?"}
{
for(i = 1; i <= NF; i++) printf("$%d=%s%s",i, $i, OFS);
print "";
}
$2==1 && $7 > 20000 {print}
产品:
$1=gene $2=id $3=variant $4=id $5=tss $6=distance $7=ma $8=samples $9=ma $10=count $11=maf $12=pval $13=nominal $14=slope $15=slope $16=se
$1=ENSG00000223972.4 $2=1 $3=13417 $4=C $5=CGAGA $6=b37 $7=1548 $8=50 $9=50 $10=0.0766871 $11=0.735446 $12=-0.0468165 $13=0.138428
$1=ENSG00000223972.4 $2=1 $3=17559 $4=G $5=C $6=b37 $7=5690 $8=7 $9=7 $10=0.00964187 $11=0.39765 $12=-0.287573 $13=0.339508
$1=ENSG00000223972.4 $2=1 $3=54421 $4=A $5=G $6=b37 $7=42552 $8=28 $9=28 $10=0.039548 $11=0.680357 $12=0.0741142 $13=0.179725
ENSG00000223972.4 1_54421_A_G_b37 42552 28 28 0.039548 0.680357 0.0741142 0.179725
$1=ENSG00000223972.4 $2=1 $3=54490 $4=G $5=A $6=b37 $7=42621 $8=112 $9=120 $10=0.176471 $11=0.00824733 $12=0.247533 $13=0.093081
ENSG00000223972.4 1_54490_G_A_b37 42621 112 120 0.176471 0.00824733 0.247533 0.093081