这个问题作为单独的主题进行讨论,但我无法让所有内容一起工作。
我有一个 tsv 文件,这是一个示例行:(file1)
NODE_1_length_45927_cov_22.029055_g0_i0 WP_055195622.1 93.1 729 50 0 291 2477 1 729 0.0e+00 1275.4 166486;1897048
最后一个字段包含一个数字列表,以 分隔;
。
我有一个包含搜索模式列表的文件(文件2):
33090
3041
1897
97100
97101
166486
我想输出包含搜索模式的行:
- 只有最后一个字段
- 最后字段中的任何数字,以 ; 分隔可以匹配,但它们必须是模式的精确单词匹配
到目前为止我最好的尝试是
awk 'NR==FNR{a[$1]; next} {for (i in a) if ($NF ~ i) print $0}' file2 file1
然而,这包括部分匹配,例如 1897 将匹配第二个数字,而它不应该匹配
我尝试过使用 gawk 正则表达式来匹配整个单词的许多变体:
awk 'NR==FNR{a[$1]; next} {for (i in a) if ($NF ~ '/\yi\y/') print $0}' file2 file1
awk 'NR==FNR{a[$1]; next} {for (i in a) if ($NF ~ '/[^[:alpha:]]i[^[:alpha:]]/') print $0}' file2 file1
但都没有输出。
任何 bash 解决方案都会很棒,无论多么丑陋 - 但是我无法用循环解决这个问题,因为我有 12 个文件,每个文件有 2000 万行,所以它需要快速地。
任何帮助深表感谢。
答案1
awk 'NR==FNR{a[$1];next}{n=split($NF,b,";");for(i=1;i<=n;i++){if(b[i] in a){print;break}}}' file2 file1
未卷绕版本:
awk '
NR==FNR{a[$1];next}
{
n=split($NF,b,";")
for(i=1;i<=n;i++){
if(b[i] in a){print;break}
}
}
' file2 file1
split($NF,b,";")
分割分号上的最后一个字段并将结果放入b
数组中。它返回b
分配给 的元素数量n
。在您的示例行中n=2
,b[1]=166486
和b[2]=1897048
.
if(b[i] in a){print;break}
如果完全匹配,则打印该行并中断循环,以免同一行打印两次。
答案2
python数据结构set
为我们提供了一种方法来比较并查找其中一个分号分隔的数字是否在由file2数字组成的另一个集合中找到。当yield为非空时设置交集,我们继续打印当前行。
python3 - file1 file2 <<\eof
import sys
data, pat = sys.argv[1:]
rs, fs = '\n', '\t'
with open(pat) as fp, open(data) as fd:
pats = set(fp.read().splitlines())
for l in fd:
s = l.rstrip(rs).split(fs)[-1].split(';')
if set(s) & pats: print(l, end='')
eof
答案3
命令
awk 'NR == FNR {a[$1];next}FS=";"($NF in a){print $0}' f2 f1
输出
NODE_1_length_45927_cov_22.029055_g0_i0 WP_055195622.1 93.1 729 50 0 291 2477 1 729 0.0e+00 1275.4 166486;1897048