【问题标题】:Linux merge files awk commandLinux 合并文件 awk 命令
【发布时间】:2016-06-28 21:49:36
【问题描述】:

我有两个文件;文件 1:

ARS-BFGL-BAC-10975 0.9303 688423261 1 01/04/2015 0.9983763305
ARS-BFGL-BAC-11025 0.9092 688423261 1 01/04/2015 0.9983763305
ARS-BFGL-BAC-11044 0.9626 688423261 2 01/04/2015 0.9983763305
ARS-BFGL-BAC-11193 0.9544 688423261 1 01/04/2015 0.9983763305
ARS-BFGL-BAC-10975 0.9303 688423263 1 01/04/2015 0.9983763000
ARS-BFGL-BAC-11025 0.9082 688423263 1 01/04/2015 0.9983763000
ARS-BFGL-BAC-11044 0.9926 688423263 2 01/04/2015 0.9983763000
ARS-BFGL-BAC-11193 0.9144 688423263 1 01/04/2015 0.9983763000

第二个文件,文件2:

ARS-BFGL-BAC-10975 10 21225382
ARS-BFGL-BAC-11025 10 84516867
ARS-BFGL-BAC-11193 1 29303546

想要的输出:

ARS-BFGL-BAC-10975 0.9303 688423261 1 01/04/2015 0.9983763305 10 21225382
ARS-BFGL-BAC-11025 0.9092 688423261 1 01/04/2015 0.9983763305 10 84516867
ARS-BFGL-BAC-11193 0.9544 688423261 1 01/04/2015 0.9983763305 1 29303546
ARS-BFGL-BAC-10975 0.9303 688423263 1 01/04/2015 0.9983763000 10 21225382
ARS-BFGL-BAC-11025 0.9992 688423263 1 01/04/2015 0.9983763000 10 84516867
ARS-BFGL-BAC-11193 0.9144 688423263 1 01/04/2015 0.9983763000 1 29303546

所以文件 1 的行数比文件 2 的多。我只想根据第一列在输出中保留文件 2 中的行,所以我希望 ARS-BFGL-BAC-10975 重复多次列 1 出现在文件 2 中,但我不希望 ARS-BFGL-BAC-11044 出现在输出中,因为它从未出现在文件 2 中。

我最接近的是以下 awk 命令,但它不允许 ARS-BFGL-BAC-10975 被复制,即当我的输出应该更长时,我只合并到文件 2 的长度。

awk 'FNR==NR{a[$1]=$2 FS $3;next} $1 in a {print $0, a[$1]}' file2 file1 > output

【问题讨论】:

    标签: linux join awk merge


    【解决方案1】:

    编辑:看到 karakfa 回答的第二部分,我意识到不需要整个 grep/cut 马戏团......简单的连接具有相同的结果。仅当 grepping 将 file1 的大小减小到足以使 grepping 加排序比排序整个文件便宜时,这才有用。如果输入文件更小,join 也可能更快。


    如果不限于 awk,可以使用 grep 和 core utils(需要 Bash 进行进程替换):

    $ join <(grep -wFf <(cut -d ' ' -f 1 file2) file1 | sort) <(sort file2)
    ARS-BFGL-BAC-10975 0.9303 688423261 1 01/04/2015 0.9983763305 10 21225382
    ARS-BFGL-BAC-10975 0.9303 688423263 1 01/04/2015 0.9983763000 10 21225382
    ARS-BFGL-BAC-11025 0.9082 688423263 1 01/04/2015 0.9983763000 10 84516867
    ARS-BFGL-BAC-11025 0.9092 688423261 1 01/04/2015 0.9983763305 10 84516867
    ARS-BFGL-BAC-11193 0.9144 688423263 1 01/04/2015 0.9983763000 1 29303546
    ARS-BFGL-BAC-11193 0.9544 688423261 1 01/04/2015 0.9983763305 1 29303546
    

    由内而外:

    • cut -d ' ' -f 1 file2 提取file2 的第一列。

    • grep -wFf &lt;(cut -d ' ' -f 1 file2) file1 | sortfile2 中提取第一列并将其视为 grep -f 的文件参数(“匹配与此文件中的任何行匹配的行”),-F 将这些行视为固定的字符串而不是正则表达式。 -w 确保我们只查看整个列,没有子字符串匹配。

      结果通过管道传送到sort,因为join 需要排序输入。

    • join 然后将这一切的结果作为第一个文件,并与&lt;(sort file2) 连接。

    请注意,输出是按字母顺序排序的。如果它的出现顺序与file 完全相同,我们就不能使用join

    【讨论】:

    • 此答案取决于问题中未标记的bash。虽然 bash 存在于 OP 的 Linux 系统上是一个安全的赌注,并且这个答案提供了一个可行的解决方案,但您应该指出,它是一种 alternate 解决方案,而不是 OP 所要求的解决方案。
    • 感谢这完美的作品。我这边唯一的缺点是 file1 非常大,所以排序需要一段时间,但正是我想要的。感谢您的帮助
    【解决方案2】:

    这按预期工作

    $ awk 'NR==FNR{a[$1]=$2 FS $3; next} $1 in a{print $0, a[$1]}' file2 file1
    
    ARS-BFGL-BAC-10975 0.9303 688423261 1 01/04/2015 0.9983763305 10 21225382
    ARS-BFGL-BAC-11025 0.9092 688423261 1 01/04/2015 0.9983763305 10 84516867
    ARS-BFGL-BAC-11193 0.9544 688423261 1 01/04/2015 0.9983763305 1 29303546
    ARS-BFGL-BAC-10975 0.9303 688423263 1 01/04/2015 0.9983763000 10 21225382
    ARS-BFGL-BAC-11025 0.9082 688423263 1 01/04/2015 0.9983763000 10 84516867
    ARS-BFGL-BAC-11193 0.9144 688423263 1 01/04/2015 0.9983763000 1 29303546
    

    file1 与 file2 匹配的记录数没有限制,但显然 file2 的键应该是唯一的。我不确定当你运行相同的脚本时你得到了什么。

    如果您不介意丢失订单,可以使用joinsorted 文件

    $ join <(sort file1) <(sort file2)
    
    ARS-BFGL-BAC-10975 0.9303 688423261 1 01/04/2015 0.9983763305 10 21225382
    ARS-BFGL-BAC-10975 0.9303 688423263 1 01/04/2015 0.9983763000 10 21225382
    ARS-BFGL-BAC-11025 0.9082 688423263 1 01/04/2015 0.9983763000 10 84516867
    ARS-BFGL-BAC-11025 0.9092 688423261 1 01/04/2015 0.9983763305 10 84516867
    ARS-BFGL-BAC-11193 0.9144 688423263 1 01/04/2015 0.9983763000 1 29303546
    ARS-BFGL-BAC-11193 0.9544 688423261 1 01/04/2015 0.9983763305 1 29303546
    

    只需一点额外的杂耍,您就可以恢复订单(装饰/取消装饰)

    $ join -12 <(cat -n file1 | sort -k2) <(sort file2) | 
      sort -k2,2 | cut -d' ' -f2 --complement
    
    ARS-BFGL-BAC-10975 0.9303 688423261 1 01/04/2015 0.9983763305 10 21225382
    ARS-BFGL-BAC-11025 0.9092 688423261 1 01/04/2015 0.9983763305 10 84516867
    ARS-BFGL-BAC-11193 0.9544 688423261 1 01/04/2015 0.9983763305 1 29303546
    ARS-BFGL-BAC-10975 0.9303 688423263 1 01/04/2015 0.9983763000 10 21225382
    ARS-BFGL-BAC-11025 0.9082 688423263 1 01/04/2015 0.9983763000 10 84516867
    ARS-BFGL-BAC-11193 0.9144 688423263 1 01/04/2015 0.9983763000 1 29303546
    

    【讨论】:

    • 嗯,现在我的回答看起来有点愚蠢;)
    • 您好,感谢您的回复。我希望避免加入等,因为我需要排序并且文件会很大。然而,awk 命令似乎没有将第 2 列和第 3 列从 file2 加入到输出中。不过,它保留了适量的行。感谢您的帮助
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-31
    • 1970-01-01
    • 2018-12-11
    相关资源
    最近更新 更多