Bash 默认值展开:没有设置和空字符串,为什么会走不同分支

10-01 3阅读

空值可能是调用者有意留下的

脚本接受一个可选标签:没有传入时使用默认标签,明确传入空字符串时则不显示标签。写成 ${demo_value:-default} 后,主动留空也变回了默认值。问题来自冒号,它改变了检查条件,把“未设置”和“已设置但为空”放到同一分支。

设计配置入口前,先决定空值有没有独立含义。对输出前缀来说,空字符串可能代表关闭;对必填目录来说,空字符串可能是错误。Shell 提供几种展开形式,但不会自动理解业务约定,不能把所有变量都套上同一种默认规则。

用三行输入检查四种展开

下面脚本在 Bash 5.2 验证,保存后用 bash 文件名运行,不要用不确定实现的 sh。示例只读写自己的演示变量,set -u 用来说明这些默认展开也可以安全处理未设置状态。方括号让空结果在终端里清楚可见。

Bash 默认值展开:没有设置和空字符串,为什么会走不同分支

AI概念示意图,非真实界面

set -u

show() {
    printf '%s | [%s] [%s] [%s] [%s]\n' "$1" \
        "${demo_value-default}" "${demo_value:-default}" \
        "${demo_value+set}" "${demo_value:+nonempty}"
}

unset demo_value
show unset
demo_value=''
show empty
demo_value='hello'
show value

unset demo_value
printf 'temporary: %s\n' "${demo_value:-default}"
[[ ${demo_value+set} == '' ]] || exit 1
: "${demo_value:=saved}"
printf 'assigned: %s\n' "$demo_value"
[[ $demo_value == saved ]] || exit 1

demo_value=''
( : "${demo_value?required}" ) || exit 1
printf 'empty with question: accepted\n'
if ( : "${demo_value:?required}" ) 2>/dev/null; then
    printf 'unexpected acceptance\n'
    exit 1
else
    printf 'empty with colon-question: rejected\n'
fi

冒号决定空字符串是否参与默认处理

unset 行的前两列都是 default,后两列为空;empty 行分别是空、default、set、空;value 行分别是 hello、hello、set、nonempty。减号形式决定替代值,加号形式决定条件成立时是否返回指定标记,标记不是原变量的内容。

${demo_value-default} 只在变量未设置时替代,所以保留了主动传入的空字符串。${demo_value:-default} 对未设置和空字符串都替代。对应地,${demo_value+set} 可检查是否设置,带冒号的加号则要求已设置且非空。

这里的空不是由空白字符组成的字符串。一个空格仍然是非空值,字面文本 false 或数字零也都是非空字符串。若配置需要禁止纯空白或识别真假,必须在取值之后按业务规则另行校验,默认展开不会替你完成这些判断。

临时替代、写回变量和立即报错

第二段输出 temporary: default,随后断言变量仍未设置,说明减号形式只提供这次展开的结果。冒号等号形式会真正写回变量,因此接下来输出 assigned: saved。调用函数前初始化配置时,应明确后续步骤是否需要看到这个写回值。

冒号命令本身不处理参数内容,但 Shell 会先完成参数展开,所以 : "${demo_value:=saved}" 能在不额外打印内容的情况下执行默认赋值。双引号也保留了值里的空格,避免将一个配置值在其他命令里拆成多个参数。

最后把变量设为空:不带冒号的问号检查允许它通过,带冒号的问号检查会拒绝。后者在非交互 Shell 中可能直接结束当前执行,因此示例放在子 Shell 里捕获失败,只为了完整展示结果,不应据此忽略真正的必填配置错误。

默认内容只有需要时才会展开,这使复杂回退值可以延迟计算。但如果回退中包含命令替换,就会在选中该分支时执行命令。配置默认值最好保持简单、可预测;涉及外部操作时,应把读取和失败处理写成清晰步骤。

为脚本补测试时,至少覆盖未设置、空、普通文本和包含空格的文本。检查最终取值,也检查变量是否被写回以及错误是否按预期退出。只有把这三类观察一起记录,才不会在重构默认写法时悄悄改变调用方传入空值的含义。

参考资料

  1. GNU Bash 官方手册:Shell Parameter Expansion

文章版权声明:除非注明,否则均为云鹊BLOG原创文章,转载或复制请以超链接形式并注明出处。