如何用sed移除算术扩展$(( ))中的嵌套${}括号?
移除算术扩展
$(( ))中的${}的Shell实现方案 需求与问题
需要编写Shell工具实现:移除算术扩展$(( ... ))结构内的变量引用括号${},将如cnt=$(( ${cnt} + ${cnt123} ))的代码转换为cnt=$(( cnt + cnt123 )),同时支持嵌套括号内的替换(比如echo $(( (${var} * ${var2}) / ${var3} ))需转为echo $(( (var * var2) / var3 )))。
此前尝试的sed命令仅能匹配并替换单次目标,无法处理同一$(( ))内的多个${},例如执行sed -Ei 's/(\$\(\()([^\)]*?)\$\{([^\}]+?)\}(.*?)(\)\))/\1\2\3\4\5/g' $file后,仅能替换最后一个${cnt123},剩余${cnt}未被处理。
解决方案
方法1:使用sed循环替换
利用sed的标签循环功能,重复执行替换直到所有目标都被处理:
sed -Ei ':loop; s/(\$\(\([^)]*)\$\{([^}]+)\}([^)]*\)\))/\1\2\3/g; t loop' "$file"
:loop:定义循环标签- 正则匹配
$(( ... ))内的${xxx},将其替换为xxx t loop:如果本次替换成功,回到标签继续执行,直到无匹配项
方法2:使用Perl一行命令
Perl的正则支持更灵活的循环替换,代码更简洁:
perl -pi -e 'while (s/\$\(\((.*?)\$\{([^}]+)\}(.*?)\)\)/$(( $1$2$3 ))/g) {}' "$file"
while (s/.../.../g):循环执行替换,直到没有可匹配的${}
验证示例
以下测试用例均可被正确转换:
s=$(( ${s} ** 2 ))→s=$(( s ** 2 ))sum=$(( ${a} + ${b} ))→sum=$(( a + b ))echo $(( (${var} * ${var2}) / ${var3} ))→echo $(( (var * var2) / var3 ))echo ${d} $((${t1} + ${t2})) ${e}→echo ${d} $(( t1 + t2 )) ${e}
样本文件处理结果
原样本输入中的行cnt=$(( ${cnt}+ ${cnt123} ))会被转换为cnt=$(( cnt+ cnt123 )),处理后的完整代码如下:
#! /bin/bash cnt=0 cnt123=1 for filename in * do fname=$(basename $filename) cname=$(echo $fname | tr A-Z a-z) if [ "$fname" != "$cname" ] then if [ -e "$cname" ] then echo "$cname already exists" exit 1 fi echo "$fname is renamed $cname" mv $fname $cname cnt=$(( cnt+ cnt123 )) fi done echo "Total count: $cnt" exit 0
内容的提问来源于stack exchange,提问作者Seung Yun Baek
相关产品推荐
相关产品推荐

