Bash - 查找另一个文件中列出的一个文件的 ID 对

Bash - 查找另一个文件中列出的一个文件的 ID 对

我有一个大文件“F1”,其中列出了 id 对:

id1 = 数字,id2 = 字符

id1 id2 id1 id2 ...

目标是从第二个文件“F2”中提取包含 id 对的行,格式如下:

id1 TYHYU 61728497 rtyheyia id2 8372819203948 id1 UJLJF 57383930 utkjruak id2 5683903048377 ...

想过用全能的“grep”来处理它,但我面临着一些障碍。

F1 中的每个 Id1 和 Id2 在 F2 中重复几次,从而变得过时grep -Fwf F1.txt F2.txt > F3.txt。换句话说,ID1+ID2代表一个完整的标识符。

虽然执行grep -w "$id1.*$id2" db.txt可以解决这个问题,但我不确定如何到达那里(可能while read -r在运行 grep 之前运行一个尴尬的循环来将 F1 中的每一行作为变量集处理??)。

答案1

你快到了。读取循环对于这种情况应该可以正常工作,所以像这样:

while read -r line; do
    id1=$(echo "$line" | cut -d ' ' -f 1)
    id2=$(echo "$line" | cut -d ' ' -f 2)
    grep -w "${id1}.*${id2}" "$F2"
done < "$F1"

但如果您要查找的 ID 存在于数据的其他字段中的任何位置,这也可能会返回误报。如果您可以保证 F2 中的 ID 字段始终出现在第 1 列和第 5 列中,我建议您也检查字段位置。这可以通过awk以下行来快速完成grep

awk -v id1="$id1" -v id2="$id2" '$1 == id1 && $5 == id2 {print $0}' "$F2"

这粗略地说,“对于 F2 中的每一行,如果第 1 列是 id1,第 5 列是 id2,则打印整行”。

免责声明,我没有对此进行测试。

答案2

@John Moon 的解决方案很有价值,我已经投了赞成票。但我注意到您将您的文件描述为“大”。基于 - 的解决方案awk需要一次完整地遍历您的大文件 f1,该文件有 N 行。然后它需要 N 次完整地遍历你的大(?)文件 f2。

我是不是专家awk。有人可能会通过 f1 文件来收集 ID,然后通过 f2 文件来打印匹配项。

这是一个相当笨拙的解决方案,grep其速度几乎与最佳awk解决方案一样快。

首先,将 f1.txt 文件(N 行长)转换为正则表达式的文件,也是 N 行长:

$ while read id1 id2; do
   printf '^%s[[:blank:]]+' "$id1"
   printf '[^[:blank:]]+[[:blank:]]+'
   printf '[^[:blank:]]+[[:blank:]]+'
   printf '[^[:blank:]]+[[:blank:]]+'
   printf '%s[[:blank:]]\n' "$id2"
done < f1.txt > regexp.txt

那个丑陋的 printf 序列创建了一个正则表达式,强制在行的开头匹配字符串(以匹配第 1 列),然后是任意空格;然后重复 3 次(非空白)(空白)字符串对(忽略第 2、3 和 4 列);然后匹配第 5 列中的另一个字符串,后跟一个空格。

因此,在一次遍历 的 N 行中f1.txt,我们创建了一个包含 N 个正则表达式的列表,这些正则表达式将匹配 中f2.txt包含相同 ID 对的行。该列表存储在regexp.txt.

f2.txt现在可以使用以下命令一次性找到匹配的行:

$ egrep -f regexp.txt f2.txt

总而言之,脚本将是:

$ while read id1 id2; do
   printf '^%s[[:blank:]]+' "$id1"
   printf '[^[:blank:]]+[[:blank:]]+'
   printf '[^[:blank:]]+[[:blank:]]+'
   printf '[^[:blank:]]+[[:blank:]]+'
   printf '%s[[:blank:]]\n' "$id2"
done < f1.txt > regexp.txt
$ egrep -f regexp.txt f2.txt

样本数据:

f1.txt:

id1 id2
id1 id2
id3 id4
id3 id5
id4 id5
id4 id6

f2.txt:

id1 TYHYU 61728497 rtyheyia id2 8372819203948
id1 UJLJF 57383930 utkjruak id2 5683903048377
id1 UJLJF 57383930 utkjruak id2 5683903048377
id3 THREE 4444444 adfhdd id4 182i3746
id2 NOPE 4444444 adfhdd id4 182i3746
id3 TREEE 555555 affff id5 8435987345
id4 FOUR  555055 asdfl id5 3728462
id4 FORE  6666666 dfiuyd id6 845687234

中间文件regexp.txt(由脚本创建):

^id1[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+id2[[:blank:]]
^id1[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+id2[[:blank:]]
^id3[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+id4[[:blank:]]
^id3[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+id5[[:blank:]]
^id4[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+id5[[:blank:]]
^id4[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+id6[[:blank:]]

结果egrep输出:

$ egrep -f regexp.txt f2.txt 
id1 TYHYU 61728497 rtyheyia id2 8372819203948
id1 UJLJF 57383930 utkjruak id2 5683903048377
id1 UJLJF 57383930 utkjruak id2 5683903048377
id3 THREE 4444444 adfhdd id4 182i3746
id3 TREEE 555555 affff id5 8435987345
id4 FOUR  555055 asdfl id5 3728462
id4 FORE  6666666 dfiuyd id6 845687234

同样,纯粹的awk解决方案可能会更快、更优雅。此外,如果模式数量太大,我概述的方法可能会导致grep内存不足。regexp.txt但我只是想我会把它作为一个基于速​​度优化的grep解决方案。

相关内容