bash内置参数扩展${#string}取长度、${string#pattern}删前缀、${string:offset:length}截取最高效安全:无需fork进程,不惧特殊字符,但变量须已定义,否则作空字符串处理。

${#string} 能直接取长度,${string#pattern} 可删前缀,${string:offset:length} 支持灵活截取——这些 Bash 内置参数扩展比 expr 更快、更安全,也无需额外进程开销。
怎么用 ${#string} 获取字符串长度
这是最轻量、最推荐的方式。Bash 原生支持,不启动子进程,无空格/特殊字符解析风险。
-
${#string}中的string必须是已定义变量名,不能是字面值(如${#"abc"}会报错) - 变量未定义或为空时,结果为
0,不会报错 - 注意:它统计的是字符数,不是字节数;含中文或 emoji 时,UTF-8 编码下仍按字符计(Bash 5.0+ 默认行为)
- 别写成
expr length "$string"——多一次 fork,且当$string含换行或特殊字符时易出错
怎么删掉字符串开头的固定前缀
用 ${var#prefix} 或 ${var##prefix},本质是模式匹配删除,不是正则。
-
${var#test-}删除最短匹配的test-(只删一次,哪怕开头有多个test-) -
${var##test-}删除最长匹配(即贪心删到最后一个test-出现的位置) - 如果
$var不以test-开头,原值不变,不会报错或变空 - 前缀中可含通配符:
${var#file*.log}会删掉从file开始、到第一个.log结束的最短部分 - 别用
sed 's/^test-//' ——重了,还可能因 <code>$var含斜杠或&导致意外替换
怎么从指定位置截取子串
${string:offset} 和 ${string:offset:length} 是最常用截取方式,下标从 0 开始(注意:和 expr substr 的 1 起始不同)。
-
${string:3}从第 4 个字符起截到末尾(等价于${string:3:${#string}}) -
${string: -2}注意空格:-2前必须有空格,表示倒数第 2 个字符起(即取最后两个) -
${string:(-2)}括号写法可省略空格,语义相同,但某些老版本 Bash(如 3.x)不支持 - 超出范围不报错:若
offset超长,结果为空;若length超出剩余长度,自动截断到末尾 - 避免用
expr substr "$string" 4 3——它下标从 1 开始,易混淆;且expr对空格和换行敏感,脚本健壮性差
为什么别轻易用 expr 处理字符串
expr 是外部命令,每次调用都 fork 新进程,性能低;更重要的是它对空格、反斜杠、括号等极其脆弱。
-
expr length "$string"中,若$string是"a b c",expr会把空格当分隔符,实际只收到a,返回长度 1 -
expr match "$string" ".*"依赖正则引擎,而不同系统expr实现有差异(如 GNU vs BSD),.*在某些版本里不匹配换行 -
expr index "$string" "$char"若$char是/或.,需额外转义,易漏 - Bash 内置扩展全部在当前 shell 进程内完成,无环境依赖,也无词法分割风险
真正容易被忽略的是:所有这些 ${...} 操作都要求变量名真实存在。如果写成 ${undef_var#prefix},它就按空字符串处理——这有时是预期行为,有时却是隐藏 bug 的根源,尤其在条件判断中要格外留神变量是否真被赋过值。










