Linux 条件Awk哈希映射匹配查找
我有两个表格文件。一个文件包含50个键值的映射,仅称为lookup\u file.txt。 另一个文件包含30列和数百万行的实际表格数据data.txt 我想用查找文件.txt中的值替换第二个文件的id列。 我该怎么做?我更喜欢在bash脚本中使用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{"}
另外,我是否可以在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
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