文章目录
一、变量替换(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:字符串长度非 0str1 = 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 功能:
- 使用
expr、cut、awk等外部工具替代复杂替换,或用更保守的模式删除(${var#pattern}/${var%pattern}在 POSIX 中可用,但${var//}不可用)。
- 使用
3. 性能与可维护性
- 当要做复杂字符串处理时,调用外部命令(
sed、awk)常比复杂的 Bash 花式更可读。 - 对于大量文件处理,避免不必要的子 shell 与外部命令调用以减少开销。
4. 使用 set -u 时的安全处理
- 因为
set -u会在使用未定义变量时报错,常用的写法为:${VAR:-}或${VAR+set},或在使用前显式赋默认值。
5001

被折叠的 条评论
为什么被折叠?



