Linux环境下Bash替换字符串指定标记区间内指定字符的实现方案
在Bash中替换指定区间内的字符
需求:在Linux Bash环境下,替换字符串中指定标记区间内的特定字符(本次为句号.),优先用纯Bash实现,也可使用系统常见工具(sed、Python等)。
示例
- 起始字符串:
<mark>foo.bar.baz</mark> blah. blah. blah. <mark>abc.def.ghi</mark> ... - 转换目标:将
<mark>与</mark>之间的所有.替换为<wbr /> - 期望结果:
<mark>foo<wbr />bar<wbr />baz</mark> blah. blah. blah. <mark>abc<wbr />def<wbr />ghi</mark>
补充说明
<mark>和</mark>标记不会嵌套- 希望了解是否能用Bash内置功能实现:先定位标记区间,再对区间内内容执行替换
- 无需当作XML解析,标记可替换为任意内容
已实现的非纯Bash方案
while $(echo "${my_str}" | grep -E '<mark>[^.]*\.[^<]*</mark>' >/dev/null 2>&1) ; do my_str=$(echo "${my_str}" | sed -E -e 's,(<mark>[^.]*)\.([^<]*</mark>),\1<wbr />\2,g') done
解决方案
一、纯Bash实现
利用Bash内置的字符串操作、遍历功能完成,无需外部工具:
my_str='<mark>foo.bar.baz</mark> blah. blah. blah. <mark>abc.def.ghi</mark> ...' result="" in_mark=0 # 逐个字符遍历字符串 for ((i=0; i<${#my_str}; i++)); do char="${my_str:i:1}" # 检测进入标记区间 if [[ "${my_str:i:6}" == "<mark>" ]]; then result+="<mark>" ((i+=5)) # 跳过已处理的标记字符 in_mark=1 continue fi # 检测退出标记区间 if [[ "${my_str:i:7}" == "</mark>" ]]; then result+="</mark>" ((i+=6)) in_mark=0 continue fi # 区间内替换句号,其他情况直接追加字符 if ((in_mark == 1)) && [[ "$char" == "." ]]; then result+="<wbr />" else result+="$char" fi done echo "$result"
逻辑说明
- 通过
in_mark变量跟踪是否处于<mark>区间内 - 遇到
<mark>或</mark>时,直接追加标记并跳过对应长度的字符,切换区间状态 - 区间内的
.替换为<wbr />,其余字符及区间外内容原样保留
二、高效sed单命令实现
你的循环sed方案需要多次扫描字符串,用sed的分支循环功能可一次完成替换:
sed -E ':loop s/(<mark>[^<.]*)\.([^<]*<\/mark>)/\1<wbr\/>\2/g t loop' <<< "$my_str"
逻辑说明
:loop定义循环标签- 单次替换
<mark>内的一个.,g确保当前行内所有符合的单次替换都执行 t loop:如果上一次替换成功,跳回loop标签继续循环,直到没有可替换的.为止
三、Python实现(适合复杂场景)
若需处理更复杂的字符串结构,Python正则分组替换更直观:
import re my_str = '<mark>foo.bar.baz</mark> blah. blah. blah. <mark>abc.def.ghi</mark> ...' result = re.sub(r'(<mark>)(.*?)(</mark>)', lambda m: m.group(1) + m.group(2).replace('.', '<wbr />') + m.group(3), my_str) print(result)
逻辑说明
- 用非贪婪正则
(.*?)匹配<mark>...</mark>的完整区间,避免跨区间匹配 - 对匹配到的区间内内容执行
.到<wbr />的替换,再拼接前后标记输出
内容的提问来源于stack exchange,提问作者codesniffer
相关产品推荐
相关产品推荐

