【问题标题】:How can I append values to each line in file1 after using part of that line to index into file2 and lookup the value?在使用该行的一部分索引到 file2 并查找值之后,如何将值附加到 file1 中的每一行?
【发布时间】:2021-02-10 20:54:53
【问题描述】:

我基本上有以下2个文件:

$ cat file1.txt
AB,12 34 56,2.4,256,,
CD,23 45 67,10.8,257,,
EF,34 56 78,0.6,258,,
GH,45 67 89,58.3,259,,
...
$ cat file2.txt
AB,12 34 56,2.4,36
XY,56 99 11,3.6,15
ZQ,12 36 89,5.9,0
EF,34 56 78,0.6,99
GH,45 67 89,58.3,79
...

对于 file1.txt 中的每一行,我想使用前 3 个字段作为 file2.txt 中的索引,获取相应的最后一个字段,并将其放入 file1.txt 中,如下所示:

cat newfile.txt
AB,12 34 56,2.4,256,36,
CD,23 45 67,10.8,257,,
EF,34 56 78,0.6,258,99,
GH,45 67 89,58.3,259,79,

不能保证 file1 中的每一行都会出现在 file2 中,反之亦然,对于这种情况,上面 newfile.txt 中显示的空字段是可以的。

在我的第一次尝试中,我在 while read 循环中从 file1 中读取每一行,然后在 file2 中查找适当的行,它确实有效,但速度太慢了。 file1 和 file2 各有几十万行。

有什么方法可以使用 sed 将 file1 中每一行的前 3 个字段用作 file2 的索引,查找我需要的值,然后将其附加到 file1 中的那一行?并且不逐行读取file1?

感谢任何帮助。

【问题讨论】:

    标签: bash sed grep comm


    【解决方案1】:

    使用joinsed(用于一些预处理和后处理),并假设| 字符不出现在两个文件中

    join -a1 -t'|' \
        <(sort file1.txt | sed 's/,/|/3') \
        <(sort file2.txt | sed 's/,/|/3') |
        sed 's/,|//; s/|/,/; s/[^,]$/&,/' > newfile.txt
    

    (使用问题中给出的输入进行测试)

    可以使用关联数组在普通的bash 中完成,但我怀疑它是否有效。例如:

    #!/bin/bash
    
    declare -A tail
    
    while IFS= read -r line; do
        if [[ $line =~ ([^,]*,){3} ]]; then
            tail[${BASH_REMATCH[0]}]=${line#"${BASH_REMATCH[0]}"}
        fi
    done < file2.txt
    
    while IFS= read -r line; do
        if [[ $line =~ ([^,]*,){3} ]] && [[ -n ${tail[${BASH_REMATCH[0]}]} ]]; then
            printf '%s%s\n' "${line%?}" "${tail[${BASH_REMATCH[0]}]},"
        else
            printf '%s\n' "$line"
        fi
    done < file1.txt > newfile.txt
    

    【讨论】:

    • Join 似乎是此任务的合适命令。感谢您的帮助!
    • @MarcRyan join 是在单个字段上连接 2 个排序文件的适当命令,但这不是这里的任务。在 2 sorts 的输出上使用 join,在 2 bash process substitutions 内通过管道传输到 2 seds,其输出通过管道传输到另一个 sed,并且假设输入中可能存在哪些字符并非如此显然,这个任务的适当命令与简单地调用一次 awk 相比。
    • @M.NejatAydin 关于“可以使用关联数组在普通 bash 中完成,但我怀疑它是否有效。” - 你是对的,它效率极低,比 awk 脚本甚至 join+sorts+seds+bash 脚本慢几个数量级。请参阅why-is-using-a-shell-loop-to-process-text-considered-bad-practice 的主要原因加上 bash 访问关联数组通常很慢。
    【解决方案2】:

    使用 awk:

    awk -F, 'FNR==NR { map[$1","$2","$3]=$4;next } { print $1","$2","$3","$4","map[$1","$2","$3]"," }' file2.txt file1.txt
    

    首先处理 file1.txt (FNR==NR) 创建一个数组映射,其中第一个、第二个和第三个逗号分隔字段作为索引,第四个字段作为值。然后对于第二个文件,打印第一个、第二个、第三个和第四个字段以及第一个索引的映射数组的内容,用逗号分隔。

    【讨论】:

      【解决方案3】:

      这将非常有效地在每个 Unix 机器上的任何 shell 中使用任何 awk,并且不依赖于输入中不存在的任何字符:

      $ awk '
          BEGIN { FS=OFS="," }
          { key = $1 FS $2 FS $3 }
          NR==FNR { map[key] = $4; next }
          { $5 = map[key] }
      1' file2 file1
      AB,12 34 56,2.4,256,36,
      CD,23 45 67,10.8,257,,
      EF,34 56 78,0.6,258,99,
      GH,45 67 89,58.3,259,79,
      

      【讨论】:

        猜你喜欢
        • 2015-01-17
        • 2012-12-08
        • 2018-09-01
        • 1970-01-01
        • 2012-10-25
        • 1970-01-01
        • 2013-06-09
        • 2019-01-30
        • 2023-03-31
        相关资源
        最近更新 更多