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"

四、排错与验证#

常见问题与排查方法:

  1. 变量为空但未触发默认值
    - 排查:区分未定义与空字符串,${var:-default} 会对空触发默认值;${var-default} 不会。
    - 验证:
    bash var="" echo "${var:-default}" # default echo "${var-default}" # 空

  2. 模式匹配不生效
    - 排查:pattern 使用 Shell 通配符而非正则表达式。
    - 验证:
    bash v="abc123" echo "${v#*c}" # 123

  3. 脚本中替换失败
    - 排查:变量是否包含特殊字符,必要时使用单引号包裹或先进行转义。

五、练习#

  1. 使用 ${var:=default} 给未设置的 LOG_DIR 赋值为 /var/log/app,并打印。
  2. 给定 path="/opt/pkg/app-2.0.1.tgz",用参数展开输出:
    - 目录名
    - 文件名
    - 主版本号 2
  3. 将字符串 prod-db-1 替换为 prod-db-2,只替换最后一个数字。
  4. 编写脚本:若 ENV 未定义则退出报错;若定义则拼接生成 APP_${ENV}