解决常见Bash语法错误:实用指南
通过引号、括号、变量、重定向和命令查找问题的示例,修复常见的Bash语法错误。
解决常见Bash语法错误:实用指南
Bash语法错误通常源于小失误:缺少引号、括号错误、重定向位置不当,或者变量展开成了你预料之外的内容。当你的Bash脚本因 syntax error near unexpected token 停止时,先检查Bash报告的那一行之前的代码,然后通过语法检查运行脚本。
在服务器上运行修改后的脚本前,使用这个快速检查:
bash -n script.sh
bash -n 会解析文件但不执行命令。它不会捕获所有逻辑错误,但能捕获许多未闭合的引号、缺失的 fi 语句以及格式错误的循环。
缺失引号
未闭合的引号是混淆解析器最快的方式之一。
name="deploy
printf 'Deploying %s\n' "$name"
Bash会继续读取后续行,因为它仍在寻找闭合的 "。通过闭合引号并引用可能包含空格的变量展开来修复:
name="deploy"
printf 'Deploying %s\n' "$name"
当你希望变量展开时使用双引号。当你想要字面文本时使用单引号:
printf 'HOME保持字面:$HOME\n'
printf "HOME展开:%s\n" "$HOME"
错误的 if、for 和 while 块
每个复合命令都需要其结束关键字。缺失一个通常会在文件末尾附近报告错误。
if systemctl is-active --quiet nginx; then
echo "nginx 正在运行"
# 缺少 fi
正确版本:
if systemctl is-active --quiet nginx; then
echo "nginx 正在运行"
fi
同样的模式适用于循环和 case 语句:
for host in web1 web2 web3; do
ssh "$host" uptime
done
case "$env" in
prod) echo "生产环境" ;;
dev) echo "开发环境" ;;
*) echo "未知" ;;
esac
括号和测试错误
[ 命令是一个实际命令,因此其参数和结束括号前后都需要空格。
错误:
if [$count -gt 5]; then
echo "太多"
fi
修复:
if [ "$count" -gt 5 ]; then
echo "太多"
fi
对于特定于Bash的脚本,[[ ... ]] 在字符串测试中通常更安全,因为它能更优雅地处理空变量并支持模式匹配:
if [[ "$file" == *.log ]]; then
gzip "$file"
fi
对数字使用数值运算符,对文本使用字符串运算符:
[[ "$status" == "ready" ]] # 字符串比较
[[ "$retries" -lt 3 ]] # 数值比较
变量展开问题
一个常见错误是在赋值时在 = 周围添加空格。Bash会将其视为命令而不是变量赋值。
错误:
backup_dir = /var/backups
修复:
backup_dir=/var/backups
当变量名与其他文本接触时使用花括号:
service="nginx"
log="/var/log/${service}.log"
没有花括号,Bash可能会读取比你预期更长的变量名。
command not found 错误
command not found 不总是语法错误。它通常意味着Bash找不到具有该名称的可执行文件。
首先检查拼写错误:
systemctl status nginx
然后检查命令是否存在且在 PATH 中:
command -v systemctl
printf '%s\n' "$PATH"
如果脚本在cron、systemd或CI作业下运行,PATH 可能比你的交互式shell更短。对关键命令使用绝对路径,或在脚本顶部设置 PATH:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
export PATH
重定向和管道错误
重定向顺序很重要。此命令将stdout写入 app.log,然后将stderr发送到同一位置:
./deploy.sh >app.log 2>&1
这个不同,因为stderr在stdout被重定向之前复制到了旧的stdout:
./deploy.sh 2>&1 >app.log
对于管道,请记住Bash通常返回最后一个命令的退出码。当中间的命令失败应导致整个管道失败时,使用 pipefail:
set -o pipefail
kubectl get pods | grep CrashLoopBackOff
实用的调试流程
从语法检查开始:
bash -n script.sh
当语法有效但行为错误时,使用跟踪运行:
bash -x script.sh
为了更安全的生产脚本,有意添加严格模式并在每次更改后测试脚本:
set -euo pipefail
set -e 在许多命令失败时退出,set -u 将未设置变量视为错误,pipefail 捕获管道内的失败。这些选项很有用,但它们会改变脚本行为,因此请有意启用它们,而不是不加测试地放入旧脚本。
要点
大多数Bash语法错误一旦你按顺序检查引号、结束关键字、括号间距、变量赋值和重定向,就会变得简单。将 bash -n 和 bash -x 保留在你的正常工作流程中,并使用将在生产环境中运行脚本的相同shell和环境进行测试。