使用Bash基于查找表替换制表符分隔文件指定列内容
问题需求
我有一个制表符分隔的main.txt文件,需要在同样是制表符分隔的查找表lookup.txt存在映射关系时,将main.txt的第2列Target_Sector替换为对应的Sector_name;如果没有映射关系,则保留原始值。
文件内容
main.txt(制表符分隔)
Serving_Sector Target_Sector HO_Attempts HO_Successful_Attempts 1112928 1112929 2 2 1112928 1112930 0 0 1112929 1112928 3 3
lookup.txt(制表符分隔)
Sector_id Sector_name 1112929 SectorTEST
现有脚本问题
使用@Dielna Reboot提供的Bash脚本执行后出现异常:会新增换行,且误替换第1列的Serving_Sector内容,不符合预期。
现有脚本
#!/bin/bash #put only ids in variable ids="$(cat hostats.txt | awk '{print $2}' | grep -v Sector)" for sector_name in $ids; do #match id condition grep "$sector_name" lookup.txt >/dev/null 2>&1 && { #save sector name sector_id="$(grep "$sector_name" lookup.txt | awk '{print $2}')" # replace via sed in-place sed -i "s/$sector_name/$sector_id/g" hostats.txt } || true done
错误执行结果(->代表制表符)
Serving_Sector->Target_Sector->HO_Attempts_HO->Successful_Attempts 1112928->SectorTEST ->2->2 1112928->1112930->0->0 SectorTEST ->1112928->3->3
期望正确结果(->代表制表符)
Serving_Sector->Target_Sector->HO_Attempts->HO_Successful_Attempts 1112928->SectorTEST->2->2 1112928->1112930->0->0 1112929->1112928->3->3
问题原因分析
- 全局替换误匹配:
sed的s/$sector_name/$sector_id/g会全局匹配文件中所有出现的ID,包括第1列的Serving_Sector,导致不该替换的内容被修改。 - 循环修改文件:每次循环都直接修改原文件,多次替换会导致格式错乱(比如新增换行)。
- 未区分制表符边界:没有限定只替换第2列的内容,匹配逻辑不精准。
正确解决方案
使用awk一次性处理,先将lookup.txt的映射关系读入数组,再遍历main.txt,仅替换第2列的匹配项:
#!/bin/bash awk -F'\t' -v OFS='\t' ' NR==FNR { if (NR>1) map[$1]=$2 # 跳过lookup表头,将Sector_id和Sector_name存入数组 next } { if ($2 in map) $2=map[$2] # 如果第2列在映射数组中,替换为对应名称 print # 输出处理后的行 } ' lookup.txt main.txt > main_updated.txt
说明
-F'\t'和-v OFS='\t'指定输入输出的分隔符为制表符,保证格式不混乱。NR==FNR:处理第一个文件(lookup.txt)时,将映射关系存入map数组,跳过表头行。- 处理第二个文件(main.txt)时,检查第2列是否存在于
map中,存在则替换,最后输出所有行。 - 结果保存到
main_updated.txt,避免直接修改原文件导致的问题,也可根据需求改为原地替换(需注意awk版本支持)。
内容的提问来源于stack exchange,提问作者SHR
相关产品推荐
相关产品推荐

