【Shell变量替换】


一、变量替换(Parameter Expansion)概述

变量替换指通过 ${...}$var 访问并用不同方式处理变量内容。Bash 提供了非常丰富的扩展语法,用于默认值、替换、截断、长度、模式删除等。

常见形式与含义(配合示例)

#!/usr/bin/env bash
name="alice"
echo "$name"         # alice
echo "${name}"       # alice(显式)

下面按功能分类说明:

1. 默认值与赋值

  • ${var:-word} — 如果 var 未设置或为空,结果为 word不改变 var)。
  • ${var:=word} — 如果 var 未设置或为空,则将 var 设为 word 并输出它。
  • ${var:+word} — 如果 var 已设置且非空,结果为 word,否则为空。
  • ${var:?message} — 如果 var 未设置或为空,打印 message 到 stderr 并退出(用于参数校验)。

示例:

#!/usr/bin/env bash
unset A
B=""
echo "${A:-default}"   # 输出 default(A 未设置)
echo "${B:-default}"   # 输出 default(B 为空)

echo "${A:=hello}"     # A=hello,并输出 hello
echo "$A"              # hello

C="ok"
echo "${C:+present}"   # 输出 present

# 强制报错并退出(用于必需参数)
D=""
: "${D:?D is required}"

2. 字符串长度

  • ${#var} — 变量长度(字符数)。
s="hello"
echo "${#s}"  # 5

3. 子串提取

  • ${var:offset}${var:offset:length} — 按字符位置提取(支持负数偏移,Bash 特性)。
s="abcdefgh"
echo "${s:2}"      # cdefgh
echo "${s:2:3}"    # cde
echo "${s: -3}"    # fgh (注意有空格以区别负数偏移)

4. 模式删除(从头/尾裁剪)

  • ${var#pattern} — 从开头匹配最短模式,删除。
  • ${var##pattern} — 从开头匹配最长模式,删除。
  • ${var%pattern} — 从结尾匹配最短模式,删除。
  • ${var%%pattern} — 从结尾匹配最长模式,删除。
path="/home/user/docs/readme.txt"
echo "${path#*/}"       # home/user/docs/readme.txt (删除第一个 / 及之前)
echo "${path##*/}"      # readme.txt (删除最长前缀,保留最后一个文件名)
echo "${path%/*}"       # /home/user/docs (删除最后一个 / 及之后)
echo "${path%%/*}"      # (空) 删除最长后缀,原理同上

5. 字符串替换

  • ${var/pattern/repl} — 替换第一个匹配(Bash)。
  • ${var//pattern/repl} — 替换所有匹配。
  • ${var/#pattern/repl} — 如果匹配开头则替换。
  • ${var/%pattern/repl} — 如果匹配结尾则替换。
s="a-b-c-a"
echo "${s/-/_}"      # a_b-c-a (第一个 -)
echo "${s//-/_}"     # a_b_c_a (全部替换)
echo "${s/#a/X}"     # X-b-c-a (开头的 a 替换)
echo "${s/%a/X}"     # a-b-c-X (结尾的 a 替换)

6. 去除首尾空白(纯 Bash 方法)

Bash 没有内置 trim 函数,但可用模式删除实现:

s="  hello  "
# 去左空格
s="${s#"${s%%[![:space:]]*}"}"
# 去右空格
s="${s%"${s##*[![:space:]]}"}"
echo ">$s<"  # >hello<

(注:这段技巧依赖于模式匹配,复杂但无须外部命令)


二、测试

在脚本里判断条件通常使用 test 命令、[ ... ]、或 [[ ... ]](Bash 扩展)。还可以使用 (( ... )) 进行算术测试。

1. 基本用法

if [ -f "$file" ]; then
  echo "file exists"
fi

# 或
if test -f "$file"; then
  echo "file exists"
fi

# Bash 扩展(推荐用于字符串比较、模式匹配)
if [[ -n "$var" && "$var" == foo* ]]; then
  echo "starts with foo"
fi

注意:[ ... ]test 是 POSIX 标准;[[ ... ]](( ... )) 是 Bash / ksh 特性,支持更多语法且减少字符串拆词风险。

2. 常见测试选项

文件相关(常用)

  • -e file:存在(包括目录、特殊文件)
  • -f file:普通文件且存在
  • -d file:目录
  • -s file:存在且大小 > 0
  • -r file:可读
  • -w file:可写
  • -x file:可执行
  • -L file:符号链接

示例:

if [ -d "$dir" ]; then
  echo "dir exists"
else
  mkdir -p "$dir"
fi

字符串相关

  • -z str:字符串长度为 0(空)
  • -n str:字符串长度非 0
  • str1 = str2(在 [ 中)或 ==(在 [[ 中)比较相等
  • 注意 <>[ 中需要转义或使用 \> 等(更安全用 [[

示例:

if [ -z "$username" ]; then
  echo "username empty"
fi

if [[ "$name" == "alice" ]]; then
  echo "hello alice"
fi

整数比较(使用 -eq -ne -lt -le -gt -ge(( ))

  • [ "$a" -eq "$b" ]
  • (( a == b )) — 算术测试,支持 C 风格运算符

示例:

a=5
b=10
if [ "$a" -lt "$b" ]; then
  echo "a < b"
fi

# 或
if (( a < b )); then
  echo "a < b"
fi

3. 组合逻辑与返回值

  • &&|| 可在 shell 中串联命令(短路求值)。
  • if 判断的是命令的退出状态(0 为真,非 0 为假)。
  • test / [ 返回退出码(0/1/2),[[ 返回更友好。

示例:

cmd && echo "success" || echo "failed"

4. 注意事项

  • 变量引用必须加引号"$var"),避免空格与通配符展开导致脚本错误。
  • [ ... ] 中,用 = 比较字符串相等,且两边要有空格: [ "$a" = "$b" ]
  • [[ ... ]] 中不需要对 < > 进行转义用于字符串比较,但仍建议用 [[ 来避免单词分割和路径名扩展。
  • (( ... )) 中变量可不加 $,但最好加可读性更好。
  • 当用 -n/-z 时仍旧加双引号:[ -z "$s" ]

三、进阶技巧

1. 引号与扩展顺序

  • 总是为变量加双引号:"$var",避免字段分割与通配符展开(pathname expansion)。
  • 当希望展开数组或保留单元边界时使用 "$@""$*"(通常不推荐)

2. 在 POSIX sh 中的差异

  • [[ ... ]]${var//pattern/repl}${var:offset} 等是 Bash 扩展,不可在纯 /bin/sh 中使用(有些系统的 /bin/sh 是 Dash)。

  • 如果需要跨平台兼容(比如在 BusyBox 或 Dash 中运行),请仅使用 POSIX 功能:

    • 使用 exprcutawk 等外部工具替代复杂替换,或用更保守的模式删除(${var#pattern} / ${var%pattern} 在 POSIX 中可用,但 ${var//} 不可用)。

3. 性能与可维护性

  • 当要做复杂字符串处理时,调用外部命令(sedawk)常比复杂的 Bash 花式更可读。
  • 对于大量文件处理,避免不必要的子 shell 与外部命令调用以减少开销。

4. 使用 set -u 时的安全处理

  • 因为 set -u 会在使用未定义变量时报错,常用的写法为:${VAR:-}${VAR+set},或在使用前显式赋默认值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wdwc2

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值