Grep 特定字段上的文件

Grep 特定字段上的文件

我有两个文件,比方说

文件1:

Locus_1
Locus_2
Locus_3

文件2:

3  3  Locus_1  Locus_40  etc_849    
3  2  Locus_2  Locus_94  *    
2  2  Locus_6  Locus_1  *    
2  3  Locus_3,Locus_4  Locus_50  *    
3  3  Locus_9  Locus_3  etc_667

我想grep -F为第一个文件做一个仅有的在第二个文件的第三列上(原来的File2字段是用制表符分隔的),比如输出应该是:

输出:

3  3  Locus_1  Locus_40  etc_849    
3  2  Locus_2  Locus_94  *    
2  3  Locus_3,Locus_4  Locus_50  *

我该怎么做?

编辑混乱:不,逗号不是一个错误。我在一列中可以有多个 Locus_* - 如果第二个 Locus_* (逗号后面的那个)与其中一行匹配,File1我也希望它被检索!

答案1

如果grep没有必要,一种简单的解决方案是使用join

$ join -1 1 -2 3 <(sort file1) <(sort -k3 file2)
Locus_1 3 3 Locus_40 etc_849
Locus_2 3 2 Locus_94 *
Locus_3 2 3 Locus_4 Locus_50 *

解释:

  • join -1 1 -2 3:连接两个文件,其中第一个文件中使用第一个(也是唯一一个)字段,第二个文件中使用第三个字段。当它们相等时被打印。
  • <(sort file1)join需要排序输入
  • <(sort -k3 file2):输入必须在连接字段上排序(此处为第三个字段)

答案2

调整解决方案https://stackoverflow.com/a/9937241/1673337您可以使用 (g)awk 来获取:

awk 'NR==FNR{a[$0]=1;next} {for(i in a){if($3~i){print;break}}}' File1 File2

它提供给定的输出。

虽然您可以制作一个正则表达式来输入 grep 以满足仅在第三列上的匹配,但我觉得此时使用 awk 更容易理解。

仅当第三列与 File1 中的一行(存储在数组 a 中)匹配时,该if($3~i){print;break}部分才负责打印。请参阅链接的帖子以获取其余部分的解释。

请注意,这会将 File1 的全部内容读取到内存中,但是,只有在文件很大时才需要考虑,在这种情况下,由于比较的乘法性质,您无论如何都需要优化。

答案3

使用grep -F选项搜索文字字符串任何地方在当前行。根据定义,文字意味着您不能使用正则表达式将搜索范围缩小到仅在字段 3 内(制表符分隔)

但是,您可以使用grep -f读取您的模式输入文件1- 但你确实需要将其修改为正则表达式列表。这是使用 bash 进程替换和 sed 生成标准正则表达式列表的一种方法,grep -f可以处理。

使用 grep 与基本正则表达式:

  grep -f <(sed 's/.*/^\\([^\t]\\+\t\\)\\{2\\}\\([^\t]\\+,\\)*&[,\t]/'  file1) file2

对于 grep 的基本正则表达式,file1动态转换为:

^\([^   ]\+ \)\{2\}\([^ ]\+,\)*Locus_1[,    ]
^\([^   ]\+ \)\{2\}\([^ ]\+,\)*Locus_2[,    ]
^\([^   ]\+ \)\{2\}\([^ ]\+,\)*Locus_3[,    ]

或者: 使用grep -E扩展正则表达式通过避免大多数反斜杠的需要,在视觉上简化了代码grepsed

grep -Ef <(sed 's/.*/^([^\t]+\t){2}([^\t]+,)*&[,\t]/' file1) file2

对于 grep 的扩展正则表达式,file1动态转换为:

^([^    ]+  ){2}([^ ]+,)*Locus_1[,  ]
^([^    ]+  ){2}([^ ]+,)*Locus_2[,  ]
^([^    ]+  ){2}([^ ]+,)*Locus_3[,  ]

输出(在两种情况下):

3   3   Locus_1 Locus_40    etc_849     
3   2   Locus_2 Locus_94    *       
2   3   Locus_3,Locus_4 Locus_50    *       

注意-f-F可以大大减慢速度,同时file1很大

答案4

(   t=$(printf \\t) ntt=[^$t]*$t ntc=[^$t,]*
### ^just makes it easy regardless of your sed version.
    sed  -ne"s/..*/^($ntt){2}($ntc,)*&(,$ntc)*$t/p" |
    grep -Ef- ./File2
)   <File1

3   3   Locus_1 Locus_40    etc_849
3   2   Locus_2 Locus_94    *
2   3   Locus_3,Locus_4 Locus_50    *

这将在 File2 的第三列中获得与 File1 中的行的匹配,无论其($ntc,)*前面或(,$ntc)*后面有多少组。不过,这确实取决于 File1 中的搜索字符串中没有元字符。如果 File1 中可能存在元字符,那么我们必须首先清理它:

(   t=$(printf \\t) ntt=[^$t]*$t ntc=[^$t,]*
    sed  -ne's/[]?{(^$|*.+)}\[]/\\&/g' \
          -e"s/..*/^($ntt){2}($ntc,)*&(,$ntc)*$t/p" |
    grep -Ef- ./File2
)   <File1

相关内容