如何使用 awk 进行 vlookup?

如何使用 awk 进行 vlookup?

我有一个文件,其字段是ID,指定,家长ID, 和家长指定。文件内容如下。

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

我想要得到家长指定对于那些缺少第四列的行,这本质上意味着:

  • 读取每一行
  • 得到家长ID从第三列开始
  • 将其与第一列中的值匹配
  • 将其插入到该子项前面的第四列 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

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

答案1

最终答案在下面给出了更多评论并更新了相关示例输入/输出:

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

$ 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 }
'

$ ./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

对 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使用删除装饰。cutawk$4

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


之前的回答:

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

$ cat tst.awk
BEGIN { FS=OFS="\t" }
$4 != "" {
    id2des[$1] = $2
}
$4 == "" {
    $4 = id2des[$3]
}
{ print }

$ 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

原答案:

您的问题实际上似乎比您指定的更简单,因为您似乎有一个包含所有信息的父行,后跟缺少 $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

$ 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

答案2

如果父母总是出现在孩子面前......

awk -F"\t" -v OFS="\t" '
    {dad[$1]=$2}
    !$4{$4=dad[$3]}1
    ' file

如果没有,则运行两次......

awk -F"\t" -v OFS="\t" '
    NR==FNR{dad[$1]=$2;next}
    !$4{$4=dad[$3]}1
    ' file file

答案3

另一个非常相似的解决方案:一个 (gnu)awk 多行脚本,访问输入文件两次,尝试使用垂直对称。 $ cat awklookup

#!/usr/bin/awk -f
BEGIN{FS=OFS="\t";  
      ARGC=3; ARGV[2] = ARGV[1]}       ## visit the input file twice 
ARGIND==1        { tab[$1]=$2      }
ARGIND==2 && !$4 { $4=tab[$3]      }
ARGIND==2        { print           }

然后:

$ chmod 755 awklookup
$ ./awklookup infile
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

相关内容