有条件的 awk hashmap 匹配查找

use*_*087 3 unix linux bash perl awk

我有 2 个表格文件。一个文件包含仅称为lookup_file.txt的 50 个键值的映射 另一个文件具有 30 列和数百万行的实际表格数据。data.txt 我想用lookup_file.txt 中的值替换第二个文件的id 列.

我怎样才能做到这一点?我更喜欢在 bash 脚本中使用 awk .. 另外,我可以在 bash 中使用哈希图数据结构来存储 50 个键/值而不是另一个文件吗?

Ed *_*ton 7

假设您的文件有逗号分隔的字段,并且“id 列”是字段 3:

awk '
BEGIN{ FS=OFS="," }
NR==FNR { map[$1] = $2; next }
{ $3 = map[$3]; print }
' lookup_file.txt data.txt
Run Code Online (Sandbox Code Playgroud)

如果这些假设中的任何一个是错误的,请提示我们是否修复不明显......

编辑:如果您想避免(恕我直言可以忽略不计)NR==FNR 测试性能影响,这将是适合使用 getline 的极少数情况之一:

awk '
BEGIN{
   FS=OFS=","
   while ( (getline line < "lookup_file.txt") > 0 ) {
      split(line,f)
      map[f[1]] = f[2]
   }
}
{ $3 = map[$3]; print }
' data.txt
Run Code Online (Sandbox Code Playgroud)

  • **+1**。这是唯一明智的方法。 (3认同)

小智 5

您可以通过 bash 混合使用“sort”和“join”,而不必在 awk/sed 中编写它,而且它可能会更快:

key.cvs(id,名称)

1,homer
2,marge
3,bart
4,lisa
5,maggie
Run Code Online (Sandbox Code Playgroud)

data.cvs(姓名、动物、所有者、年龄)

snowball,dog,3,1
frosty,yeti,1,245
cujo,dog,5,4
Run Code Online (Sandbox Code Playgroud)

现在,您需要首先在用户 ID 列上对两个文件进行排序:

cat key.cvs | sort -t, -k1,1 > sorted_keys.cvs
cat data.cvs | sort -t, -k3,3 > sorted_data.cvs
Run Code Online (Sandbox Code Playgroud)

现在加入这两个文件:

join -1 1 -2 3 -o "2.1 2.2 1.2 2.4" -t , sorted_keys.cvs sorted_data.cvs > replaced_data.cvs
Run Code Online (Sandbox Code Playgroud)

这应该产生:

snowball,dog,bart,1
frosty,yeti,homer,245
cujo,dog,maggie,4
Run Code Online (Sandbox Code Playgroud)

这:

-o "2.1 2.2 1.2 2.4"
Run Code Online (Sandbox Code Playgroud)

是说您希望在最终输出中包含 2 个文件中的哪些列。

与其他脚本语言相比,它查找和替换大量数据的速度相当快。我没有与 SED/AWK 进行直接比较,但编写一个包装它的 bash 脚本比用 SED/AWK 编写要容易得多(至少对我来说)。

另外,您可以使用升级版本的 gnu coreutils 来加快排序速度,以便可以并行进行排序

cat data.cvs | sort --parallel=4 -t, -k3,3 > sorted_data.cvs
Run Code Online (Sandbox Code Playgroud)

4 是你想要运行它的线程数。我建议每个机器核心 2 个线程通常会最大化机器,但如果它专用于此,那就没问题了。