- 约866字
- 技术
- 2026年4月16日
你有没有遇到过这种情况:本地测试正常的脚本,一到服务器上就报错?
去年帮同事排查一个凌晨失败的定时任务,光定位问题就花了2小时。最后发现是一个变量未定义的细节导致的。这类问题不算复杂,但足以让人深夜抓狂。
变量未定义:默认值的正确写法
最常见的坑是变量为空时导致脚本失败:
#!/bin/bash
# 错误写法
echo "用户名是: $username"
如果username变量未定义,脚本可能会输出空值,或者在后续操作中报错。
正确做法是使用默认值:
#!/bin/bash
# 正确写法:使用 ${var:-default}
echo "用户名是: ${username:-root}"
# 或者使用 := 赋值
${db_host:='localhost'}
这里的区别是:-只负责默认值输出,=则会实际赋值。
引号陷阱:空格不是你想的那样
看这个例子:
#!/bin/bash
files="a.txt b.txt c.txt"
# 错误写法
for f in $files; do
echo $f
done
输出会是三行,但每次循环处理的是a.txt、b.txt、c.txt三个独立的词,而不是一个包含空格的文件名。
正确做法:
#!/bin/bash
files="a.txt b.txt c.txt"
# 加引号
for f in "$files"; do
echo "$f"
done
# 或者用数组
arr=(a.txt "b file.txt" c.txt)
for f in "${arr[@]}"; do
echo "$f"
done
引号是Shell的生命线——宁可多写一对,不要少写一个。
命令结果赋值:容易被忽略的细节
把命令输出存到变量时,也容易出错:
#!/bin/bash
# 错误写法
current_date=`date +%Y-%m-%d`
# 或者
current_date=$(date +%Y-%m-%d)
这两种写法在简单场景下没问题,但如果命令输出包含空格,就会出问题:
#!/bin/bash
# 错误:输出多行时会被拆开
result=$(ls -la)
# 正确:保留换行
result="$(ls -la)"
更安全的做法是用数组或逐行处理:
#!/bin/bash
while IFS= read -r line; do
echo "Line: $line"
done < <(ls -la)
变量作用域:local不是摆设
函数里变量全局污染是另一个常见问题:
#!/bin/bash
name="global"
set_name() {
name="modified"
echo "函数内: $name"
}
set_name
echo "函数外: $name" # 输出 modified
正确做法:
#!/bin/bash
name="global"
set_name() {
local name="local"
echo "函数内: $name"
}
set_name
echo "函数外: $name" # 输出 global
用local关键字把变量限定在函数内部,避免意外修改全局状态。
总结
Shell变量这4个坑,踩一个就影响一片:
- 变量未定义——用
${var:-default}设置默认值 - 引号丢失——凡是变量引用都加引号
- 命令输出——用双引号包住命令替换结果
- 作用域——函数内用
local
这些问题单个都不难,但组合在一起足以让人debug到怀疑人生。我的经验是:写Shell脚本时宁可"冗余"一点,多写引号和默认值检查,也不要侥幸跳过。