如何用sed/awk/perl批量替换字符串中的多行内容?
多行字符串替换需求(sed/awk/perl实现)
输入文件内容
#------------------------------------------------------------------------------- # supply connections #------------------------------------------------------------------------------- connect_supply_net VSS -ports $port(VSSE) connect_supply_net VDDS_CPU -ports $port(VDDP) connect_supply_net VDD_CPU -ports $port(VDDPE) #------------------------------------------------------------------------------- # Update states #-------------------------------------------------------------------------------
期望替换后的内容
#------------------------------------------------------------------------------- # supply connections #------------------------------------------------------------------------------- if { [llength $port(VSSE)] > 0 } { connect_supply_net VSS -ports $port(VSSE) } if { [llength $port(VDDP)] > 0 } { connect_supply_net VDDS_CPU -ports $port(VDDP) } if { [llength $port(VDDPE)] > 0 } { connect_supply_net VDD_CPU -ports $port(VDDPE) } #------------------------------------------------------------------------------- # Update states #-------------------------------------------------------------------------------
尝试过的失败命令
之前尝试用sed配合shell变量实现,但未成功:
old_string="connect_supply_net VSS -ports $port(VSSE) connect_supply_net VDDS_CPU -ports $port(VDDP) connect_supply_net VDD_CPU -ports $port(VDDPE)" new_string="if { [llength $port(VSSE)] > 0 } { connect_supply_net VSS -ports $port(VSSE) } if { [llength $port(VDDP)] > 0 } { connect_supply_net VDDS_CPU -ports $port(VDDP) } if { [llength $port(VDDPE)] > 0 } { connect_supply_net VDD_CPU -ports $port(VDDPE) }" sed -i ":a;N;$!ba;s/${old_string}/${new_string}/g" file.txt
失败原因
- Shell会自动展开
$port(VSSE)这类变量,导致传递给sed的内容偏离预期 old_string中的多空格、new_string中的[/]/{/}等特殊字符会被sed当作正则元字符解析,破坏替换逻辑- 多行变量在Shell中传递时容易出现换行符解析异常
简便实现方法
方法1:awk(最直观易维护)
awk可以逐行匹配目标内容,动态生成替换代码,无需复杂转义:
awk ' /^ connect_supply_net/ { split($0, arr, /[[:space:]]+/); net = arr[2]; port = substr(arr[4], 6, length(arr[4])-7); # 提取$port(XXX)里的XXX部分 printf " if { [llength $port(%s)] > 0 } {\n", port; printf " connect_supply_net %s -ports $port(%s)\n", net, port; print " }"; next; } {print} ' file.txt > temp.txt && mv temp.txt file.txt
说明:
- 匹配以8个空格开头的
connect_supply_net行 - 拆分每行内容,提取网络名和端口标识
- 按模板生成带判断逻辑的代码块
- 通过临时文件替换原文件(避免awk直接修改原文件的兼容性问题)
方法2:修正后的sed方案
如果坚持用sed,需解决变量展开和特殊字符转义问题:
# 用单引号定义字符串,避免Shell解析$port变量 old_string='connect_supply_net VSS -ports \$port(VSSE) connect_supply_net VDDS_CPU -ports \$port(VDDP) connect_supply_net VDD_CPU -ports \$port(VDDPE)' new_string='if { \[llength \$port(VSSE)\] > 0 } { connect_supply_net VSS -ports \$port(VSSE) } if { \[llength \$port(VDDP)\] > 0 } { connect_supply_net VDDS_CPU -ports \$port(VDDP) } if { \[llength \$port(VDDPE)\] > 0 } { connect_supply_net VDD_CPU -ports \$port(VDDPE) }' # 转义sed特殊字符,处理换行符 old_escaped=$(printf '%s\n' "$old_string" | sed 's/[\/&]/\\&/g') new_escaped=$(printf '%s\n' "$new_string" | sed 's/[\/&]/\\&/g') sed -i ":a;N;$!ba;s/$old_escaped/$new_escaped/g" file.txt
说明:
- 单引号定义字符串防止Shell展开变量
- 转义
[/]/$等字符,避免sed当作正则元字符 - 用
N命令读取整个文件为单行,实现多行匹配替换
方法3:perl(简洁灵活)
perl支持多行模式,处理特殊字符更友好:
perl -0777 -i -pe ' s/ connect_supply_net VSS -ports \$port\(VSSE\)\n connect_supply_net VDDS_CPU -ports \$port\(VDDP\)\n connect_supply_net VDD_CPU -ports \$port\(VDDPE\)/ if { [llength $port(VSSE)] > 0 } {\n connect_supply_net VSS -ports $port(VSSE)\n }\n if { [llength $port(VDDP)] > 0 } {\n connect_supply_net VDDS_CPU -ports $port(VDDP)\n }\n if { [llength $port(VDDPE)] > 0 } {\n connect_supply_net VDD_CPU -ports $port(VDDPE)\n }/g ' file.txt
说明:
-0777让perl读取整个文件作为单行处理,支持多行匹配-i参数直接修改原文件- 精准匹配目标多行内容,替换为期望的代码块
内容的提问来源于stack exchange,提问作者santy
相关产品推荐
相关产品推荐

