Linux 条件Awk哈希映射匹配查找

Linux 条件Awk哈希映射匹配查找,linux,perl,bash,unix,awk,Linux,Perl,Bash,Unix,Awk,我有两个表格文件。一个文件包含50个键值的映射,仅称为lookup\u file.txt。 另一个文件包含30列和数百万行的实际表格数据data.txt 我想用查找文件.txt中的值替换第二个文件的id列。 我该怎么做?我更喜欢在bash脚本中使用awk。。 另外,我是否可以在bash中使用hashmap数据结构来存储50个键/值,而不是另一个文件 我这样做的方法是使用awk编写awk程序来处理较大的文件: awk -f <(awk ' BEGIN{print " BEGIN{"}

我有两个表格文件。一个文件包含50个键值的映射,仅称为lookup\u file.txt。 另一个文件包含30列和数百万行的实际表格数据data.txt 我想用查找文件.txt中的值替换第二个文件的id列。

我该怎么做?我更喜欢在bash脚本中使用awk。。
另外,我是否可以在bash中使用hashmap数据结构来存储50个键/值,而不是另一个文件

我这样做的方法是使用
awk
编写
awk
程序来处理较大的文件:

awk -f <(awk '
   BEGIN{print " BEGIN{"}
        {printf "      a[\"%s\"]=\"%s\";",$1,$2}
   END  {print "      }";
         print "      {$1=a[$1];print $0}"}
   ' lookup_file.txt
) data.txt

有几种方法可以做到这一点。但是如果您想要一个简单的单行程序,而不需要太多的验证,我会选择awk/sed解决方案

假设如下:

one     col2    col3    col4    col5
two     col2    col3    col4    col5
three   col2    col3    col4    col5
four    col2    col3    col4    col5
five    col2    col3    col4    col5
  • 这些文件以制表符分隔

  • 您正在使用bashshell

  • 数据文件中的id位于第一列

  • 您的文件如下所示:

  • 查找 数据 我将使用
    awk
    sed
    完成以下任务:

    awk '{print "sed -i s/^"$1"/"$2"/ data"}' lookup | bash
    
    这样做的目的是通过每一行查找并将以下内容写入stdout

    sed-is/^1/one/data

    sed-is/^2/two/data

    等等

    接下来,它将每一行传输到shell(
    |bash
    ),shell将执行
    sed
    表达式-对于inplace,您可能需要
    -i.bak
    创建备份文件。注意,您可以将扩展名更改为您想要的任何内容。 sed正在查找行开头的id,如
    ^
    所示。您不希望替换可能不包含id的列中的“id”

    您的输出如下所示:

    one     col2    col3    col4    col5
    two     col2    col3    col4    col5
    three   col2    col3    col4    col5
    four    col2    col3    col4    col5
    five    col2    col3    col4    col5
    

    当然,您的ID可能不是简单的1:1、2:2等等,但这可能会让您朝着正确的方向开始。我使用“右”一词非常松散。

    假设您的文件有逗号分隔的字段,“id列”是字段3:

    awk '
    BEGIN{ FS=OFS="," }
    NR==FNR { map[$1] = $2; next }
    { $3 = map[$3]; print }
    ' lookup_file.txt data.txt
    
    如果这些假设中的任何一个是错误的,如果修复不明显,请提示我们

    编辑:如果您希望避免(IMHO可忽略不计)NR==FNR测试性能影响,那么在适当使用getline时,这将是极少数情况之一:

    awk '
    BEGIN{
       FS=OFS=","
       while ( (getline line < "lookup_file.txt") > 0 ) {
          split(line,f)
          map[f[1]] = f[2]
       }
    }
    { $3 = map[$3]; print }
    ' data.txt
    
    awk'
    开始{
    FS=OFS=“,”
    而((getline<“lookup_file.txt”)>0){
    拆分(行,f)
    map[f[1]]=f[2]
    }
    }
    {$3=map[$3];print}
    'data.txt
    
    您可以通过bash混合使用“sort”和“join”,而不必在awk/sed中编写,而且速度可能更快:

    key.cvs(id、名称)

    data.cvs(姓名、动物、所有者、年龄)

    现在,您需要首先在“用户id”列上对这两个文件进行排序:

    cat key.cvs | sort -t, -k1,1 > sorted_keys.cvs
    cat data.cvs | sort -t, -k3,3 > sorted_data.cvs
    
    现在加入2个文件:

    join -1 1 -2 3 -o "2.1 2.2 1.2 2.4" -t , sorted_keys.cvs sorted_data.cvs > replaced_data.cvs
    
    这应产生:

    snowball,dog,bart,1
    frosty,yeti,homer,245
    cujo,dog,maggie,4
    
    这:

    是指您希望在最终输出中包含2个文件中的哪些列

    与其他脚本语言相比,它查找和替换多个GIG数据的速度非常快。我还没有直接与SED/AWK进行比较,但是编写一个封装它的bash脚本要比用SED/AWK编写容易得多(至少对我来说)

    此外,您还可以通过使用升级版的gnu coreutils来加快排序速度,以便并行地进行排序

    cat data.cvs | sort --parallel=4 -t, -k3,3 > sorted_data.cvs
    

    4表示要在其中运行它的线程数。我建议每个机器内核最多有2个线程,但如果它专门用于此,那就好了。

    您可以在bash中使用declare-A dict as dictionary,并分配dict=([“key”]=“value”)之类的值。导入到sqlite、连接和导出可能比编写大量bash、grep、,sed和awk.trival使用awk将lookup_file.txt作为assoc数组加载,然后根据需要读取data.txt。现在没有时间,别人会给你指路的。祝大家好运。数百万行使这成为一个有趣的问题。也许你可以给我们一些样本数据?+1。这是唯一明智的方法。这将为OP提到的“数百万行”中的每一行调用一次sed,每次创建一个临时文件,编辑它并将其复制回原始文件。这将需要非常、非常长的时间来运行。为什么您要这样做,而不是只在一个awk脚本中执行呢?如果您想避免NR==FNR测试性能影响(不值得避免IMHO),您可以将lookup_file.txt读入一个数组,该数组在BEGIN部分有一个getline循环。@EdMorton如果是一次性的,可能不值得,但大多数情况下,不必重新生成翻译脚本就可以让它运行起来。例如,这使得处理大文件的段变得更加容易。实际上,我可能会生成脚本的最后一行
    {$1=a[$1]}
    ,这样我就可以在命令行上附加带有
    --source
    的处理。
    join -1 1 -2 3 -o "2.1 2.2 1.2 2.4" -t , sorted_keys.cvs sorted_data.cvs > replaced_data.cvs
    
    snowball,dog,bart,1
    frosty,yeti,homer,245
    cujo,dog,maggie,4
    
    -o "2.1 2.2 1.2 2.4"
    
    cat data.cvs | sort --parallel=4 -t, -k3,3 > sorted_data.cvs