Bash 根据awk中的字典文件替换字符串
和我的字典文件dict:Bash 根据awk中的字典文件替换字符串,bash,unix,awk,sed,Bash,Unix,Awk,Sed,和我的字典文件dict: cat input aaa paul peter bbb john mike ccc paul mike bbb paul john 我需要找到字符串格式input,如果匹配文件dict中的第一列,则将第二列格式文件dict打印到第一列文件input。我可以使用sub和gsub,但我在dict文件中有数千行(用不同的字母) 谢谢你的帮助 我的解决方案: cat output: 000 paul peter 111 john mike 222 paul mike
cat input
aaa paul peter
bbb john mike
ccc paul mike
bbb paul john
我需要找到字符串格式input
,如果匹配文件dict
中的第一列,则将第二列格式文件dict
打印到第一列文件input
。我可以使用sub
和gsub
,但我在dict
文件中有数千行(用不同的字母)
谢谢你的帮助
我的解决方案:
cat output:
000 paul peter
111 john mike
222 paul mike
111 paul john
更新:
如果在dict
中的input
中未找到匹配项,请保持第一列中的单词不变
cat输入
awk:
awk '{sub(/aaa/,"000",$1); sub(/bbb/,"111",$1); sub(/ccc/,"222",$1)1' input
我认为您可以有效地使用GNU
join
:
aaa paul peter
bbb john mike
ccc paul mike
bbb paul john
ddd paul peter
cat dict
aaa OOO
bbb 111
ccc 222
cat output:
000 paul peter
111 john mike
222 paul mike
111 paul john
ddd paul peter
这将为您的示例数据提供以下输出(请注意,排序修改了输出,但这是join
工作所必需的):
所有这些都依赖于连接字段是每个文件的第一个字段,否则您需要指定文件应在哪个字段上连接
-o
参数是一种格式输出规范,指的是输出中所需的每个文件的字段:dict
的第二个字段,后面是每个字段,但输入的第一个字段除外
您提到了一些键可能在dict
中找不到,您希望保留input
的第一个字段中的值。有一个-一个选项来处理这个问题,但它会干扰我们的输出,所以我认为更容易的方法是执行两个执行,第一个执行在每个文件中输出具有对应关系的行,第二个执行在dict
中处理不具有对应关系的行:
OOO paul peter
111 john mike
111 paul john
222 paul mike
如果由于文件的大小而增加了太多的开销,则应改为使用-a2
执行一次,而不使用输出规范,然后使用sed
转换结果,awk
或处理缺少字段的行的其他方法。注释中建议的处理输入
和dict
文件中名称不匹配的更通用方法可以如下操作:
$ join sorted_dict sorted_input -o 1.2,2.2,2.3; join sorted_dict sorted_input -v 2
OOO paul peter
111 john mike
111 paul john
222 paul mike
ddd paul peter
下面我的原始解决方案适用于输入
和dict
文件之间没有遗漏映射的情况
awk 'FNR==NR {dict[$1]=$2; next} {$1=($1 in dict) ? dict[$1] : $1}1' dict input
其思想是创建一个散列映射,索引为$2FS$3
,值为$1
,即散列[“paul peter”]=“aaa”
,等等。一旦构建了该映射,现在字典文件将查看dict
中$1
的匹配行以及input
文件中的散列值。如果找到匹配项,请根据需要打印内容。将我的答案更改为:
awk 'FNR==NR{hash[$2FS$3]=$1; next}{for (i in hash) if (match(hash[i],$1)){print $2, i} }' input dict
OOO paul peter
111 john mike
111 paul john
222 paul mike
印刷品
awk 'NR==FNR{a[$1]=$2;next}{if ($1 in a)print a[$1],$2,$3; else print $0}' dict input
对于命令NR==FNR,仅对第一个文件执行以下命令。每行存储在数组a中,键为$1,值为$2。然后a中的$1从第二个文件中获取$1,并查看是否可以在数组a中找到该值。如果为真,则a[$1]打印数字,$2和$3打印名称。现在有一个额外的else子句,如果没有找到匹配项,它将从输入中打印整行 awk的操作速度快了很多,但这里有一个纯bash解决方案
OOO paul peter
111 john mike
222 paul mike
111 paul john
ddd paul peter
#/bin/bash
打字机-口述
函数add_dict()
{
dict[$1]=$2
}
添加目录aaa 000
添加内容bbb 111
添加目录ccc 222
读行时
做
列=(${row/://})
如果[“${dict[${column[0]}]}”];然后
echo${dict[${column[0]}]}${column[1]}${column[2]}
其他的
echo${column[0]}${column[1]}${column[2]}
fi
完成processed.txt
#实0.281s
#用户0.242s
#系统0m0.024s
input
如何在bbb
中有两个条目,它不应该是唯一的吗?第一列中的任何条目都不能重复多次。不同之处在其他栏中。@fedorqui我试了一下。但是这项工作如果我只有几句话的话。我想读一下dict文件。我不确定我是否可以使用awk..对于与dict文件不匹配的行,应该怎么办?@Geroge您能更新示例数据和相关输出以反映这一点吗?考虑到这一点,我认为我的答案需要更新。谢谢。如果我有不同数量的列,比如输入有500行,dict有50K行,行还是列?行,是的,它会工作;我想它应该和您可以编写的任何awk
脚本一样高效,但您肯定应该测试它。由于列太多,-o
格式规范编写起来会很麻烦,而我会依赖于cut
的范围(例如cut-d'-f2-
,它只会删除第一个字段)。GNU工具箱中总是有一个用于执行任务的工具!(当然它通常是awk
:p)你可能可以说{$1=(a中的$1)?dict[$1]:$1}1
,使它更简洁。@Inian我用awk'FNR==NR{dict[$1]=2;next}{$1=(dict中的$1=):dict输入
,我认为你是在过度复制索引。@fedorqui。谢谢^^是的,我指的是这个答案。使用字段2和3似乎有点不必要。还有,为什么要先检查输入,然后再检查dict?映射是通过dict完成的,所以应该先加载它,然后使用它的值来更改输入中的数据。@fedorqui:在更改之前,我碰巧在第一个需求中使用了这种方法,所以碰巧我没有使它灵活地用于其他情况。我能用你的评论作为我答案的更新吗?我明白了。由于它不灵活,并且比没有任何循环的简单数组中的val更复杂,我认为首先解析dict
显然是最简单的方法@费多基:没错!
awk 'NR==FNR{a[$1]=$2;next}{if ($1 in a)print a[$1],$2,$3; else print $0}' dict input
OOO paul peter
111 john mike
222 paul mike
111 paul john
ddd paul peter
#!/bin/bash
typeset -A dict
function add_dict()
{
dict[$1]=$2
}
add_dict aaa 000
add_dict bbb 111
add_dict ccc 222
while read row
do
column=(${row//:/ })
if [ "${dict[${column[0]}]}" ];then
echo ${dict[${column[0]}]} ${column[1]} ${column[2]}
else
echo ${column[0]} ${column[1]} ${column[2]}
fi
done < /tmp/1M.txt
#1 Million lines processed in
#real 0m40.173s
#user 0m37.668s
#sys 0m2.462s
#time awk 'NR==FNR{a[$1]=$2;next}{if ($1 in a)print a[$1],$2,$3; else print $0}' dict 1M.txt > processed.txt
#real 0m0.281s
#user 0m0.242s
#sys 0m0.024s