GNU parallel 和 awk 的冲突(拆分一列并过滤一些行)

Conflict between GNU parallel and awk (split a column and filter some rows)

我正在处理许多大型 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 and $3>20000)选行。我将其保存为 txt。下面的命令完美运行。

zcat InputData.txt.gz | awk -F "_"  '=' | awk '{if (==1 && >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 parallel 来加速这个过程,因为我有很多大的 gz 文件要处理。但是,GNU parallel 和 awk 之间似乎存在一些冲突,可能是在引用方面?

我尝试如下单独定义 awk 选项,但它没有在输出文件中提供任何内容。

在下面的命令中,我只运行并行处理一个输入文件。但是我想 运行 in 在多个输入文件上,并保存多个输出文件,每个文件对应一个输入文件。

例如,

InputData_1.txt.gz 到 OutputData_1.txt

InputData_2.txt.gz 到 OutputData_2.txt

awk1='{ -F "_"  "=" }'
awk2='{if (==1 && >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(,NewDF,"_")} NewDF[1]==chr && NewDF[2]>start && NewDF[2]<end {gsub("_"," ",) ; print > ("OutputData.txt")}' 

#This also works using parallel

awkbody='{split(,NewDF,"_")} NewDF[1]==chr && NewDF[2]>start && NewDF[2]<end {gsub("_"," ",) ; 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

一种方法是使用 split

$ awk '{split(,f2,"_")} 
   f2[1]==1 && f2[2]>20000 {gsub("_"," ",); print > (FILENAME".output")}' file

但是,如果您通过标准输入提供数据,awk 将不会捕获要写入的文件名。您可能需要将其作为变量传递给脚本...

简单的解决方案是将过滤器合并到单个 awk 脚本中,并且只有并行才能工作。

这是一个示例解决方案,它只扫描整个 input.txt 一次(性能的两倍):

awk 'BEGIN{FS="[ ]*[_]?"}==1 &&  > 20000 {print}' input.txt

解释:

BEGIN{FS="[ ]*[_]?"} 使字段分隔符多个" " "_"

==1 && > 20000 {print} 仅打印第 2 个字段 == 1 且第 7 个字段 > 2000 的行

示例调试脚本:

BEGIN{FS="[ ]*[_]?"}
{
    for(i = 1; i <= NF; i++) printf("$%d=%s%s",i, $i, OFS);
    print "";
}
==1 &&  > 20000 {print}

生产:

=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_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 
ENSG00000223972.4   1_54490_G_A_b37 42621   112 120 0.176471    0.00824733  0.247533    0.093081

https://www.gnu.org/software/parallel/man.html#QUOTING总结:

Conclusion: To avoid dealing with the quoting problems it may be easier just to write a small script or a function (remember to export -f the function) and have GNU parallel call that.

所以:

doit() {
  zcat "" |
    awk -F "_"  '=' |
    awk '{if (==1 && >20000) {print}}'
}
export -f doit
parallel 'doit {} > {=s/In/Out/; s/.gz//=}' ::: InputData*.txt.gz