5.2.2 变量展开与替换(默认值、截取、替换)
在 Shell 中,变量展开与替换是脚本编写的核心能力之一,常用于提供默认值、进行字符串截取与替换,从而提高脚本的健壮性与可读性。变量展开语法统一使用 ${变量名} 形式,可避免歧义并支持高级操作。
原理草图:变量展开的处理流程#
一、默认值与赋值展开#
用于处理变量未定义或为空的情况,常见语法如下:
${var:-default}:若 var 未定义或为空,展开为 default。${var:=default}:若 var 未定义或为空,将 var 赋值为 default,并展开为 default。${var:+alt}:若 var 已定义且非空,展开为 alt,否则为空。${var:?error}:若 var 未定义或为空,输出错误并退出当前脚本(适合强制参数检查)。
完整示例(可直接执行):
#!/usr/bin/env bash
# file: /tmp/demo_default.sh
# 说明:演示默认值、赋值与强制校验
echo "1) 默认值:"
echo "${USER:-unknown}"
echo "2) 赋值:"
: "${PORT:=3306}"
echo "PORT=$PORT"
echo "3) 条件替换:"
DEBUG=""
echo "DEBUG='${DEBUG:+enabled}'"
echo "4) 强制校验:"
: "${APP_ENV:?APP_ENV 不能为空}"
执行与预期结果:
bash /tmp/demo_default.sh
# 预期:
# 1) 默认值:<当前用户或 unknown>
# 2) 赋值:PORT=3306
# 3) 条件替换:DEBUG=''
# 4) 强制校验:若未设置 APP_ENV,脚本报错并退出
关键命令解释:
- : 是空命令,用于触发参数展开而不做其他操作。
- APP_ENV 未定义时,${var:?error} 会输出错误并返回非零退出码。
二、字符串截取#
Shell 支持基于位置与模式的截取,常用于处理路径、文件名与业务标识。
1. 基于位置截取#
${var:start}:从 start 位置开始截取(从 0 开始)。${var:start:length}:从 start 开始截取 length 长度。
示例:
s="kafka-cluster"
echo "${s:0:5}" # kafka
echo "${s:6}" # cluster
2. 基于模式截取(删除)#
${var#pattern}:从左侧删除最短匹配。${var##pattern}:从左侧删除最长匹配。${var%pattern}:从右侧删除最短匹配。${var%%pattern}:从右侧删除最长匹配。
示例:
f="/data/logs/app.log"
echo "${f#*/}" # data/logs/app.log
echo "${f##*/}" # app.log
echo "${f%/*}" # /data/logs
echo "${f%%/*}" # 空或最前段(依赖路径结构)
常见场景脚本:
#!/usr/bin/env bash
# file: /tmp/demo_path.sh
# 说明:提取目录与文件名
path="/srv/app/releases/app-1.2.3.tar.gz"
filename="${path##*/}"
dirname="${path%/*}"
base="${filename%%.*}"
echo "dirname=$dirname"
echo "filename=$filename"
echo "base=$base"
三、字符串替换#
用于批量替换变量中的内容,适合处理配置模板与路径统一。
${var/pat/repl}:替换首次匹配。${var//pat/repl}:替换所有匹配。${var/#pat/repl}:若开头匹配则替换。${var/%pat/repl}:若结尾匹配则替换。
示例:
url="http://127.0.0.1:8080"
echo "${url/http/https}" # https://127.0.0.1:8080
echo "${url//127.0.0.1/localhost}" # http://localhost:8080
echo "${url/#http/https}" # https://127.0.0.1:8080
配置模板渲染示例:
#!/usr/bin/env bash
# file: /tmp/render_conf.sh
# 说明:替换模板中的环境变量
tpl="server_name=APP_NAME
listen=APP_PORT"
tpl="${tpl//APP_NAME/nginx-api}"
tpl="${tpl//APP_PORT/8080}"
echo "$tpl"
四、排错与验证#
常见问题与排查方法:
-
变量为空但未触发默认值
- 排查:区分未定义与空字符串,${var:-default}会对空触发默认值;${var-default}不会。
- 验证:
bash var="" echo "${var:-default}" # default echo "${var-default}" # 空 -
模式匹配不生效
- 排查:pattern 使用 Shell 通配符而非正则表达式。
- 验证:
bash v="abc123" echo "${v#*c}" # 123 -
脚本中替换失败
- 排查:变量是否包含特殊字符,必要时使用单引号包裹或先进行转义。
五、练习#
- 使用
${var:=default}给未设置的LOG_DIR赋值为/var/log/app,并打印。 - 给定
path="/opt/pkg/app-2.0.1.tgz",用参数展开输出:
- 目录名
- 文件名
- 主版本号2 - 将字符串
prod-db-1替换为prod-db-2,只替换最后一个数字。 - 编写脚本:若
ENV未定义则退出报错;若定义则拼接生成APP_${ENV}。