使用awk将特定列替换为已声明变量时的异常问题排查
问题排查:仅部分行替换生效的Shell脚本问题
场景与需求
文件filename内容如下:
141 VALE141P -2.36741 142 ILEE142M -0.978118 143 ASNE143L -0.65645 144 HISE144R 137.373 145 THRE145T -3.11502e-11 146 SERE146G -0.305383 147 ASPE147L -1.36308 148 ARGE148R -6.82121e-13 149 HISE149E 166.234 150 ARGE150P -1.81996
需求:将第三列中大于0的值替换为变量var(示例暂设为0,实际从其他文件获取)。
尝试的代码
var=0 line=($(awk '{if ($3>0) {line=NR; print line}}' filename)) for N in ${line[@]} do awk '{if (NR=='$N') {$3='$var'}; {print}}' filename > temporaryfile done
错误结果
仅第9行(原149行)的第三列被替换为0,第4行(原144行)未生效:
141 VALE141P -2.36741 142 ILEE142M -0.978118 143 ASNE143L -0.65645 144 HISE144R 137.373 145 THRE145T -3.11502e-11 146 SERE146G -0.305383 147 ASPE147L -1.36308 148 ARGE148R -6.82121e-13 149 HISE149E 0 150 ARGE150P -1.81996
问题原因
循环中每次执行awk都直接读取原始的filename,然后将结果覆盖写入temporaryfile:
- 第一次循环处理第4行时,生成的
temporaryfile里第4行已被替换,但第二次循环处理第9行时,又从原始文件读取内容,仅修改第9行后覆盖掉之前的temporaryfile,导致第一次的修改丢失。最终只有最后一次循环的修改被保留。
修正方案
方案1:单次awk完成所有替换(最优)
无需循环,直接用awk一次遍历文件完成替换:
var=0 awk -v var="$var" '$3>0 {$3=var} 1' filename > temporaryfile
-v var="$var":将Shell变量var传递给awk内部变量$3>0 {$3=var}:判断第三列大于0时,替换为var的值1:awk中非0值会触发默认的print动作,输出所有行
方案2:基于临时文件迭代修改(不推荐)
若必须使用循环,需每次基于上一次修改后的临时文件继续处理:
var=0 line=($(awk '{if ($3>0) print NR}' filename)) # 先复制原始文件到临时文件作为初始状态 cp filename temporaryfile for N in ${line[@]} do awk -v n="$N" -v var="$var" 'NR==n {$3=var} 1' temporaryfile > temp && mv temp temporaryfile done
- 初始复制原始文件到
temporaryfile - 每次循环读取当前
temporaryfile,修改指定行后写入临时文件temp,再替换回temporaryfile - 用
-v传递变量,避免Shell变量直接拼接的语法问题
内容的提问来源于stack exchange,提问作者Akash Mondal
相关产品推荐
相关产品推荐

