【问题标题】:Bash - joining (merging) files by columnsBash - 按列连接(合并)文件
【发布时间】:2015-03-13 15:09:33
【问题描述】:

三个文件有列,分隔符为“|”,行中的列可能为空。我需要使用第一个文件中的第 3 列和第 4 列以及第二个和第三个中的第 1 列将第二个和第三个文件加入到第一个文件中。

例如:

file1.txt:

123456||4|11|17|A||10|B|1
123457||4|11|17|A||12||1
123458||5|11|17|A||1|Б|1
123459||6|13|17|A||1|Б|1

file2.txt:

4|Forth
5|Fifth
6|Sixth

file3.txt:

11|st.|Eleventh
13|pr.|Thirteenth

我想要什么输出:

123456||4|Forth|11|st.|Eleventh|17|A||10|B|1
123457||4|Forth|11|st.|Eleventh|17|A||12||1
123458||5|Fifth|11|st.|Eleventh|17|A||1|Б|1
123459||6|Sixth|13|pr.|Thirteenth|17|A||1|Б|1

如何编写能够满足我需求的 Bash 脚本?我知道这是 awt 命令,但我无法编写脚本。感谢您的回答。

【问题讨论】:

    标签: linux bash file join awk


    【解决方案1】:

    你可以使用这个 awk 命令:

    awk 'BEGIN{ FS=OFS="|" }
         NR == FNR {a[$1]=$0; next}
         NR == FNR + length(a) {b[$1]=$0; next}
         {$3=b[$3]; $4=a[$4]} 1' file3.txt file2.txt file1.txt
    123456||4|Forth|11|st.|Eleventh|17|A||10|B|1
    123457||4|Forth|11|st.|Eleventh|17|A||12||1
    123458||5|Fifth|11|st.|Eleventh|17|A||1|Б|1
    123459||6|Sixth|13|pr.|Thirteenth|17|A||1|Б|1
    

    说明:

    • BEGIN{ FS=OFS="|" } - 将输入和输出字段分隔符设置为管道 |
    • NR == FNR - 仅对第一个文件执行此块
    • a[$1]=$0; next - 创建一个数组a,键为$1,值为整行
    • NR == FNR + length(a) - 仅对第二个文件执行此块
    • b[$1]=$0; next - 创建一个数组b,键为$1,值为完整行
    • 为最后一个(第 3 个)文件执行下一个块 {...}
    • $3=b[$3]; - 将 b[$3] 的值分配给第三个字段
    • $4=a[$4]; - 将 a[$4] 的值分配给第 4 个字段
    • 1 - 是打印每条记录的默认操作

    【讨论】:

    • 嗯@anubhava 我正在成为你的粉丝,这个答案是一个很好的答案,看起来就像 SQL 连接操作。我从你的回答中学到了很多东西。
    • 感谢@Skynet 的客气话。是的,它确实是 3 路 SQL 连接操作。最初我不确定如何去做,但一旦我开始构建它,事情就变得清晰起来。是的,NR == FNR + length(a) 把戏很方便:)
    • 是的,它也给出了很好的解释,如果任何时候我需要这样做,我一定会按照你的回答:)
    • 您说: 1 - 打印每条记录的默认操作是,如何将结果写入新文件?
    • 您可以使用标准 > outfile 重定向重定向输出或在 ask 中使用 print $0 > "out file"。
    【解决方案2】:

    假设文件已排序:

    join -t'|' -1 4 -2 1 \
         <(join -t '|' -1 3 -2 1 file1.txt file2.txt) file3.txt
    

    如果您确实需要特定顺序的字段,请添加输出格式选项:

     -o1.2,1.3,1.1,1.11,1.4,2.2,2.3,1.5,1.6,1.7,1.8,1.9,1.10,1.11
    

    【讨论】:

      【解决方案3】:

      这里是:

      #!/bin/bash
      
      while IFS='|' read c1 c2 c3 c4 c5 c6 c7 c8 c9 c10
              do
              st1=$( fgrep "$c3" file2.txt )
              st2=$( fgrep "$c4" file3.txt )
              echo "$c1|$c2|$st1|$st2|$c5|$c6|$c7|$c8|$c9|$c10"
              done
      

      【讨论】:

        猜你喜欢
        • 2015-09-22
        • 1970-01-01
        • 1970-01-01
        • 2017-07-25
        • 1970-01-01
        • 2021-12-07
        • 2013-01-09
        • 2020-07-04
        • 1970-01-01
        相关资源
        最近更新 更多