我有两个文件,比方说
文件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
和扩展正则表达式通过避免大多数反斜杠的需要,在视觉上简化了代码grep
和sed
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