更快的解决scheme来比较bash中的文件

文件1:

chr1 14361 14829 NR_024540_0_r_DDX11L1,WASH7P_468 chr1 14969 15038 NR_024540_1_r_WASH7P_69 chr1 15795 15947 NR_024540_2_r_WASH7P_152 chr1 16606 16765 NR_024540_3_r_WASH7P_15 chr1 16857 17055 NR_024540_4_r_WASH7P_198 

和file2:

 NR_024540 11 

我需要在file1find匹配file2并打印整个file1 + second column of file2

所以ouptut是:

  chr1 14361 14829 NR_024540_0_r_DDX11L1,WASH7P_468 11 chr1 14969 15038 NR_024540_1_r_WASH7P_69 11 chr1 15795 15947 NR_024540_2_r_WASH7P_152 11 chr1 16606 16765 NR_024540_3_r_WASH7P_15 11 chr1 16857 17055 NR_024540_4_r_WASH7P_198 11 

我的解决scheme在bash中非常慢:

 #!/bin/bash while read line; do c=$(echo $line | awk '{print $1}') d=$(echo $line | awk '{print $2}') grep $c file1 | awk -v line="$d" -v OFS="\t" '{print $1,$2,$3,$4"_"line}' >> output done < file2 

我更喜欢更快的任何bash或awk解决scheme。 输出可以被修改,但需要保留所有的信息(列的顺序可以不同)。

编辑:

现在它看起来像根据@chepner最快的解决scheme:

 #!/bin/bash while read -rcd; do grep $c file1 | awk -v line="$d" -v OFS="\t" '{print $1,$2,$3,$4"_"line}' done < file2 > output 

另一个解决方法是使用joinsed ,假设file1file2是排序的

 join <(sed -r 's/[^ _]+_[^_]+/& &/' file1) file2 -1 4 -2 1 -o "1.1 1.2 1.3 1.5 2.2" > output 

如果输出顺序不重要,可以使用awk

 awk 'FNR==NR{d[$1]=$2; next} {split($4,v,"_"); key=v[1]"_"v[2]; if(key in d) print $0, d[key]} ' file2 file1 

你得到,

 chr1 14361 14829 NR_024540_0_r_DDX11L1,WASH7P_468 11
 chr1 14969 15038 NR_024540_1_r_WASH7P_69 11
 chr1 15795 15947 NR_024540_2_r_WASH7P_152 11
 chr1 16606 16765 NR_024540_3_r_WASH7P_15 11
 chr1 16857 17055 NR_024540_4_r_WASH7P_198 11

在一个Awk命令中,

 awk 'FNR==NR{map[$1]=$2; next}{ for (i in map) if($0 ~ i){$(NF+1)=map[i]; print; next}}' file2 file1 chr1 14361 14829 NR_024540_0_r_DDX11L1,WASH7P_468 11 chr1 14969 15038 NR_024540_1_r_WASH7P_69 11 chr1 15795 15947 NR_024540_2_r_WASH7P_152 11 chr1 16606 16765 NR_024540_3_r_WASH7P_15 11 chr1 16857 17055 NR_024540_4_r_WASH7P_198 11 

多线程中更可读的版本

 FNR==NR { # map the values from 'file2' into the hash-map 'map' map[$1]=$2 next } # On 'file1' do { # Iterate through the array map for (i in map){ # If there is a direct regex match on the line with the # element from the hash-map, print it and append the # hash-mapped value at last if($0 ~ i){ $(NF+1)=map[i] print next } } } 

尝试这个 –

  cat file2 NR_024540 11 NR_024541 12 cat file11 chr1 14361 14829 NR_024540_0_r_DDX11L1,WASH7P_468 chr1 14361 14829 NR_024542_0_r_DDX11L1,WASH7P_468 chr1 14969 15038 NR_024540_1_r_WASH7P_69 chr1 15795 15947 NR_024540_2_r_WASH7P_152 chr1 16606 16765 NR_024540_3_r_WASH7P_15 chr1 16857 17055 NR_024540_4_r_WASH7P_198 chr1 14361 14829 NR_024540_0_r_DDX11L1,WASH7P_468 chr1 14969 15038 NR_024540_1_r_WASH7P_69 chr1 15795 15947 NR_024540_2_r_WASH7P_152 chr1 16606 16765 NR_024540_3_r_WASH7P_15 awk 'NR==FNR{a[$1]=$2;next} substr($4,1,9) in a {print $0,a[substr($4,1,9)]}' file2 file11 chr1 14361 14829 NR_024540_0_r_DDX11L1,WASH7P_468 11 chr1 14969 15038 NR_024540_1_r_WASH7P_69 11 chr1 15795 15947 NR_024540_2_r_WASH7P_152 11 chr1 16606 16765 NR_024540_3_r_WASH7P_15 11 chr1 16857 17055 NR_024540_4_r_WASH7P_198 11 chr1 14361 14829 NR_024540_0_r_DDX11L1,WASH7P_468 11 chr1 14969 15038 NR_024540_1_r_WASH7P_69 11 chr1 15795 15947 NR_024540_2_r_WASH7P_152 11 chr1 16606 16765 NR_024540_3_r_WASH7P_15 11 

性能 – (测试55000条记录)

 time awk 'NR==FNR{a[$1]=$2;next} substr($4,1,9) in a {print $0,a[substr($4,1,9)]}' file2 file1 > output1 real 0m0.16s user 0m0.14s sys 0m0.01s 

您不必要地启动了大量的外部程序。 让read从文件2拆分入线,而不是两次调用awk 。 也没有必要运行grep ; awk可以做自己的过滤。

 while read -rcd; do awk -v field="$c" -v line="$d" -v OFS='\t' '$0 ~ field {print $1,$2,$3,$4"_"line}' file1 done < file2 > output 

如果搜索到的字符串总是相同的长度( length("NR_024540")==9 ):

 awk 'NR==FNR{a[$1]=$2;next} (i=substr($4,1,9)) && (i in a){print $0, a[i]}' file2 file1 

解释:

 NR==FNR { # process file2 a[$1]=$2 # hash record using $1 as the key next # skip to next record } (i=substr($4,1,9)) && (i in a) { # read the first 9 bytes of $4 to i and search in a print $0, a[i] # output if found } 
 awk -F '[[:blank:]_]+' ' FNR==NR { a[$2]=$3 ;next } { if ( $5 in a ) $0 = $0 " " a[$5] } 7 ' file2 file1 

评论:

  • 使用_作为额外的字段分隔符,所以文件名比较容易在两个文件中进行比较(只使用数字部分)。
  • 7是为了好玩,它只是一个非0值 – >打印行
  • 我不改变字段(NF + 1,…),所以我们保持原来的格式只加入参考号码

一个较小的oneliner代码(针对代码大小进行了优化)(假定file1中的非空行是必需的)。 如果分隔符只有空格,则可以用空格字符替换[:blank:]

 awk -F '[[:blank:]_]+' 'NF==3{a[$2]=$3;next}$0=$0" "a[$5]' file2 file1 

没有awksed需要。 这假定file2只有一行:

 n="`cut -f 2 file2`" ; while read x ; do echo "$x $n" ; done < file1