如何使用 awk 进行 vlookup?

Sol*_*osa 3 scripting awk

我有一个文件,其字段为IDDesignationParentIDParentDesignation。文件内容如下。

A1  M.D-Sales    0    UmbrellaCorp
a1  Sr.Sales    A1
b1  Sr.R&D      B1
b2  Jr.SR&D     B1
a2  Jr.Sales    A1
B1  M.D-R&D      0    UmbrellaCorp
Run Code Online (Sandbox Code Playgroud)

我想为那些缺少第四列的行获取ParentDesignation,这基本上意味着:

  • 读取每一行
  • 从第三列获取ParentID
  • 将其与第一列中的值匹配
  • 将其插入到该孩子面前的第四列 4 中。

结果将是以下一个。

A1  M.D-Sales    0  UmbrellaCorp
a1  Sr.Sales    A1  M.D-Sales
b1  Sr.R&D      B1  M.D-R&D
b2  Jr.SR&D     B1  M.D-R&D
a2  Jr.Sales    A1  M.D-Sales
B1  M.D-R&D      0  UmbrellaCorp
Run Code Online (Sandbox Code Playgroud)

我知道如何在 Excel 中使用 完成相同的任务vlookup,但我需要使用脚本。

Ed *_*ton 6

最终答案在下面给出了更多评论并更新了有问题的示例输入/输出:

我会首先对数据进行排序,因此填充缺失值的行为比在 awk 中执行 2-pass 方法更有效并且使用更少的内存,并且最终输出的组织比输入的可读性要好得多:

$ cat tst.sh
#!/usr/bin/env bash

awk '
    BEGIN { FS=OFS="\t" }
    { print (NR>1), ($4=="" ? $3 : $1), $4, $1, NR, $0 }
' "${@:--}" |
sort -t$'\t' -k1,1n -k2,2 -k3,3r -k4,4 -k5,5n |
cut -f6- |
awk '
    BEGIN { FS=OFS="\t" }
    $4 != "" { d = $2 }
    $4 == "" { $4 = d }
    { print }
'
Run Code Online (Sandbox Code Playgroud)

$ ./tst.sh file | column -s$'\t' -t
ID  Designation  ParentID  ParentDesignation
A1  M.D-Sales    0         UmbrellaCorp
a1  Sr.Sales     A1        M.D-Sales
a2  Jr.Sales     A1        M.D-Sales
B1  M.D-R&D      0         UmbrellaCorp
b1  Sr.R&D       B1        M.D-R&D
b2  Jr.SR&D      B1        M.D-R&D
Run Code Online (Sandbox Code Playgroud)

对 awk 的第一次调用只是修饰输入,以便它可以按以下方式排序:

  1. (NR>1) = header-or-not 0-or-1 指示符,以确保排序后标题行保持在最前面,
  2. ($4=="" ? $3 : $1) = 每行的 ID 或 ParentID 将相关行组合在一起
  3. $4 = ParentDesignation,所以我们可以对它进行排序,使得具有 ParentDesignation 的行排在那些不具有相同 ID/ParentID 的行之前,
  4. $1 = ID,这样我们就可以按孩子的 ID 按字母顺序对孩子进行排序,
  5. NR = 所以如果其他所有东西都是通用的,我们可以按照它们在输入中出现的顺序打印这些行(在这种情况下可能没有必要,因为每个 ID 似乎都是唯一的,但对于其他类似情况是一种很好的做法)。

然后我们只是sort通过上面的字段,然后cut在传递给最终awk脚本以实际进行$4填充之前使用删除装饰。

如果您不确定其中任何一个步骤的作用,只需一次将每个步骤更改|| cat; exit一个,然后您就会看到每个步骤发生了什么。


上一个答案:

鉴于下面的评论,这可能是您想要的,假设父项(如果存在)始终出现在您的数据中的子项之前:

$ cat tst.awk
BEGIN { FS=OFS="\t" }
$4 != "" {
    id2des[$1] = $2
}
$4 == "" {
    $4 = id2des[$3]
}
{ print }
Run Code Online (Sandbox Code Playgroud)

$ awk -f tst.awk file
ID      Designation     ParentID        ParentDesignation
A1      M.D-Sales       0       UmbrellaCorp
a1      Sr.Sales        A1      M.D-Sales
a2      Jr.Sales        A1      M.D-Sales
B1      M.D-R&D 0       UmbrellaCorp
b1      Sr.R&D  B1      M.D-R&D
b2      Jr.SR&D B1      M.D-R&D
Run Code Online (Sandbox Code Playgroud)

原答案:

您的问题实际上似乎比您指定的更简单,因为您似乎有一个包含所有信息的父行,后跟缺少 $4 的子行,在这种情况下,您不需要查找任何内容,您只需要:

$ awk 'BEGIN{FS=OFS="\t"} $4!=""{d=$2} $4==""{$4=d} 1' file
ID      Designation     ParentID        ParentDesignation
A1      M.D-Sales       0       UmbrellaCorp
a1      Sr.Sales        A1      M.D-Sales
a2      Jr.Sales        A1      M.D-Sales
B1      M.D-R&D 0       UmbrellaCorp
b1      Sr.R&D  B1      M.D-R&D
b2      Jr.SR&D B1      M.D-R&D
Run Code Online (Sandbox Code Playgroud)

$ awk 'BEGIN{FS=OFS="\t"} $4!=""{d=$2} $4==""{$4=d} 1' file | column -s$'\t' -t
ID  Designation  ParentID  ParentDesignation
A1  M.D-Sales    0         UmbrellaCorp
a1  Sr.Sales     A1        M.D-Sales
a2  Jr.Sales     A1        M.D-Sales
B1  M.D-R&D      0         UmbrellaCorp
b1  Sr.R&D       B1        M.D-R&D
b2  Jr.SR&D      B1        M.D-R&D
Run Code Online (Sandbox Code Playgroud)

  • 然后修复您问题中的示例,使其更真实地代表您的真实数据,即并非所有孩子都直接在其父母之下,并且包括没有父母的情况。如果孩子有可能发生在父母之前,那么请确保也包括在内。顺便说一句 - 谢谢! (2认同)
  • 是的,这回答了您提出的问题,但是如果您想在更大的 awk 脚本的上下文中做类似的事情并且无法弄清楚如何去做,那么只需提出一个新问题并确保说明这一点并提供最小的此功能需要适应的脚本。 (2认同)