Shell调试的核心技巧
Shell调试的核心是:打开执行跟踪、检查命令返回值、定位变量问题、捕获错误、缩小问题范围。
1.常用调试命令速查
| 命令 | 作用 |
|---|---|
bash -x script.sh |
打印执行的命令及展开后的参数 |
bash -n script.sh |
只检查语法,不执行脚本 |
bash -v script.sh |
打印读取到的脚本原始内容 |
echo "$?" |
查看上一条命令的退出码 |
set -x |
开启执行跟踪 |
set +x |
关闭执行跟踪 |
type command |
查看命令是别名、函数还是可执行文件 |
command -v command |
查找命令的位置或类型 |
declare -p var |
查看变量的值和属性 |
trap '...' ERR |
在发生未捕获的错误时执行处理逻辑 |
2.最常用的5种调试技巧
2.1使用bash -x跟踪执行过程
例如有一个脚本test.sh:
#!/usr/bin/env bash
name="Alice"
dir="/tmp/demo"
mkdir -p "$dir"
echo "Hello, $name"
ls "$dir/not_exist"
echo "Finished"
执行:
bash -x test.sh
输出类似:
+ name=Alice
+ dir=/tmp/demo
+ mkdir -p /tmp/demo
+ echo 'Hello, Alice'
Hello, Alice
+ ls /tmp/demo/not_exist
ls: cannot access '/tmp/demo/not_exist': No such file or directory
+ echo Finished
Finished
可以快速看出:
- 每一步执行了什么命令。
- 变量展开后的实际内容是什么。
- 错误发生在哪一步。
- 命令失败后脚本是否继续执行。
如果只想跟踪某一段代码:
set -x
# 需要调试的代码
process_data "$input"
set +x
注意:
set -x可能输出密码、Token等敏感信息,调试生产环境时应避免直接打印敏感变量。
2.2检查退出码:$?
Shell通常通过退出码表示命令执行结果:
0:成功。- 非
0:失败,具体含义取决于命令。
grep "ERROR" app.log
rc=$?
if [ "$rc" -ne 0 ]; then
echo "grep failed or no match, rc=$rc"
fi
2.3调试变量:检查空值、空格和引号
Shell中大量问题来自变量展开和字符串处理。
name="hello world"
echo $name
echo "$name"
两者看起来相似,但行为不同:
echo $name:变量展开后还会发生单词分割和路径名展开。echo "$name":保留变量内容中的空格,通常更安全。
调试变量时推荐:
printf 'name=<%s>\n' "$name"
declare -p name
例如:
file="my file.txt"
ls $file
2.4使用set -euo pipefail捕获常见错误
脚本开头可以使用:
#!/usr/bin/env bash
set -euo pipefail
各选项的含义:
| 选项 | 含义 |
|---|---|
-e |
某些未处理的命令失败时退出 |
-u |
使用未定义变量时触发错误 |
-o pipefail |
管道中任意命令失败时,管道整体返回失败 |
例如:
#!/usr/bin/env bash
set -euo pipefail
input="data.txt"
cat "$input" | grep "ERROR" | wc -l
echo "Done"
需要注意,set -e存在条件判断、函数、管道等方面的例外行为,不能替代显式错误处理。实际开发中,关键操作最好明确检查:
if ! cp source.txt target.txt; then
echo "Copy failed" >&2
exit 1
fi
2.5使用trap定位错误行
对于较长的脚本,可以在开头添加:
#!/usr/bin/env bash
set -Eeuo pipefail
trap 'rc=$?; printf "ERROR: line=%s rc=%s command=%s\n" \
"$LINENO" "$rc" "$BASH_COMMAND" >&2' ERR
当符合ERR触发条件的命令失败时,可以输出:
- 出错行号。
- 命令退出码。
- 发生错误的命令。
ERR陷阱和set -e一样存在特定的触发规则,并非所有失败都会触发。复杂脚本中还可以使用DEBUG和RETURN陷阱,但它们会显著增加输出量。