【问题标题】:How to merge two files using AWK? [duplicate]如何使用 AWK 合并两个文件? [复制]
【发布时间】:2011-07-24 23:20:27
【问题描述】:

文件 1 有 5 个字段 A B C D E,其中字段 A 是一个整数值

文件 2 有 3 个字段 A F G

文件1的行数比文件2大很多(20^6到5000)

文件1中A的所有条目都出现在文件2中的字段A中

我喜欢按字段A合并两个文件并携带F和G

期望的输出是 A B C D E F G

例子

文件 1

 A     B     C    D    E
4050 S00001 31228 3286 0
4050 S00012 31227 4251 0
4049 S00001 28342 3021 1
4048 S00001 46578 4210 0
4048 S00113 31221 4250 0
4047 S00122 31225 4249 0
4046 S00344 31322 4000 1

文件 2

A     F    G   
4050 12.1 23.6
4049 14.4 47.8   
4048 23.2 43.9
4047 45.5 21.6

期望的输出

A    B      C      D   E F    G
4050 S00001 31228 3286 0 12.1 23.6
4050 S00012 31227 4251 0 12.1 23.6
4049 S00001 28342 3021 1 14.4 47.8
4048 S00001 46578 4210 0 23.2 43.9
4048 S00113 31221 4250 0 23.2 43.9
4047 S00122 31225 4249 0 45.5 21.6

【问题讨论】:

  • 您是故意遗漏了文件 2 中以 4046 开头的行还是意外遗漏?这很重要,因为您想要的输出没有显示 4046 行,但之前您说文件 1 的所有行都包含在文件 2 中。
  • @SiegeX。抱歉,这是一个意外遗漏。
  • 在三元组中,我认为这个例子是最好的。

标签: linux bash unix awk


【解决方案1】:

您需要将文件 2 中的条目读入 BEGIN 块中的一对关联数组中。假设 GNU Awk:

BEGIN { while (getline < "File 2") { f[$1] = $2; g[$1] = $3 } }

在主处理块中,您从文件 1 中读取该行并使用在 BEGIN 块中创建的数组中的正确数据打印它:

{ print $0, f[$1], g[$1] }

将文件 1 作为文件名参数提供给程序。

awk 'BEGIN { while (getline < "File 2") { f[$1] = $2; g[$1] = $3 } }
     print $0, f[$1], g[$1] }' "File 1"

由于文件名中有空格,因此需要在文件名参数周围加上引号。您需要在 getline 文件名周围加上引号,即使它不包含空格,否则它将是一个变量名。

【讨论】:

    【解决方案2】:

    谢天谢地,你根本不需要写这个。 Unix 有一个 join 命令可以为您执行此操作。

    join -1 1 -2 1 File1 File2
    

    这里是“在行动”:

    will-hartungs-computer:tmp will$ cat f1
    4050 S00001 31228 3286 0
    4050 S00012 31227 4251 0
    4049 S00001 28342 3021 1
    4048 S00001 46578 4210 0
    4048 S00113 31221 4250 0
    4047 S00122 31225 4249 0
    4046 S00344 31322 4000 1
    will-hartungs-computer:tmp will$ cat f2
    4050 12.1 23.6
    4049 14.4 47.8   
    4048 23.2 43.9
    4047 45.5 21.6
    will-hartungs-computer:tmp will$ join -1 1 -2 1 f1 f2
    4050 S00001 31228 3286 0 12.1 23.6
    4050 S00012 31227 4251 0 12.1 23.6
    4049 S00001 28342 3021 1 14.4 47.8
    4048 S00001 46578 4210 0 23.2 43.9
    4048 S00113 31221 4250 0 23.2 43.9
    4047 S00122 31225 4249 0 45.5 21.6
    will-hartungs-computer:tmp will$ 
    

    【讨论】:

    • 使用join 要求文件按顺序排列;显示的输入未按排序顺序(或未按升序排序)。这可能是使用awk 的练习,所以很可能您的其他明智(一旦更正)解决方案是不可接受的。
    • @Will。哇。 join 命令是否要求两个文件的行数相同?谢谢
    • @Jonathan 好吧,至少在我的 Mac 上,join 命令完全符合他的要求,而且文件没有排序,我只是将它们剪切/粘贴到 vi 中。
    • @Tony 不,文件(显然)不必具有相同的行数。
    • 是的,当它们出现故障时似乎有问题。它似乎不喜欢第二个文件中不在第一个文件中的项目。当然,如果顺序不重要,还有 sort(1)。
    【解决方案3】:
    $ awk 'FNR==NR{a[$1]=$2 FS $3;next}{ print $0, a[$1]}' file2 file1
    4050 S00001 31228 3286 0 12.1 23.6
    4050 S00012 31227 4251 0 12.1 23.6
    4049 S00001 28342 3021 1 14.4 47.8
    4048 S00001 46578 4210 0 23.2 43.9
    4048 S00113 31221 4250 0 23.2 43.9
    4047 S00122 31225 4249 0 45.5 21.6
    4046 S00344 31322 4000 1
    

    解释:(部分基于另一个question。不过有点晚了。)

    FNR指的是当前文件中的记录号(通常是行号),NR指的是总记录号。运算符 == 是一个比较运算符,当两个周围的操作数相等时返回 true。所以FNR==NR{commands} 表示括号内的命令只在处理第一个文件时执行(file2 现在)。

    FS 指的是字段分隔符,$1$2 等是一行中的第一个、第二个等字段。 a[$1]=$2 FS $3 表示字典(/array)(名为a)填充有$1 键和$2 FS $3 值。

    ; 分隔命令

    next 表示当前行忽略任何其他命令。 (处理在下一行继续。)

    $0 是整行

    {print $0, a[$1]} 简单地打印出整行和a[$1] 的值(如果$1 在字典中,否则只打印$0)。现在它只对第二个文件(file1 now)执行,因为FNR==NR{...;next}

    【讨论】:

    • @kurumi 没有合并,只打印了文件 1
    • @tony,不,我测试过了。查看我的编辑
    • @kurumi 在看到你的答案之前,我想出了几乎相同的答案,但最好对那些最先到达那里的人进行投票而不是复制。因此,在我删除我的并为你投票之前,你应该编辑你的,在我的回答中添加 (a[$1]) 部分。
    • 你能详细说明a[$1]=$2吗?你是怎么拿到 2 美元的?
    • 一点解释也无妨。
    【解决方案4】:
    awk 'BEGIN{OFS=","}  FNR==NR {F[$1]=$2;G[$1]=$3;next} {print $1,$2,$3,$4,$5,F[$1],G[$1]}' file2.txt file1.txt
    

    【讨论】:

    • 这个答案出现在低质量审查队列中,大概是因为您没有提供任何代码解释。如果此代码回答了问题,请考虑在答案中添加一些解释代码的文本。这样一来,您就更有可能获得更多支持,并帮助提问者学习新知识。
    • 您能稍微解释一下吗?真的很感激。
    猜你喜欢
    • 2020-07-07
    • 2016-07-30
    • 1970-01-01
    • 1970-01-01
    • 2017-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-14
    相关资源
    最近更新 更多