脚本执行过程中难免出错,良好的错误处理能让脚本更健壮、更可靠。Shell 提供了退出状态码、条件判断、trap 等机制来处理错误。
每个命令执行后都会返回一个退出状态码,保存在 $? 变量中:
ls /etc/passwd
echo $?
ls /notexist
echo $?
运行结果:
0
2
| 退出码 | 含义 |
|---|---|
| 0 | 成功 |
| 1 | 一般错误 |
| 2 | 误用命令 |
| 126 | 无法执行 |
| 127 | 命令未找到 |
| 128 | 退出参数无效 |
| 130 | Ctrl+C 终止 |
#!/bin/bash
if [ $# -eq 0 ]; then
echo "用法:$0 <参数>"
exit 1
fi
if [ ! -f "$1" ]; then
echo "错误:文件 $1 不存在"
exit 2
fi
exit 0
if mkdir /tmp/test; then
echo "目录创建成功"
else
echo "目录创建失败"
fi
mkdir /tmp/test && echo "成功" || echo "失败"
&& 前面成功才执行后面,|| 前面失败才执行后面。
command1 || exit 1
command2 || exit 1
command3 || exit 1
trap 可以捕获信号和错误,在脚本退出时执行清理操作。
#!/bin/bash
cleanup() {
echo "清理临时文件..."
rm -f /tmp/temp_$$.*
}
trap cleanup EXIT
echo "创建临时文件..."
touch /tmp/temp_$$_1
touch /tmp/temp_$$_2
# 脚本正常退出或出错都会执行 cleanup
trap 'echo "收到中断信号"; exit 1' INT
trap 'echo "收到终止信号"; exit 1' TERM
常用信号:
| 信号 | 含义 |
|---|---|
| EXIT | 脚本退出 |
| INT | Ctrl+C |
| TERM | 终止信号 |
| ERR | 命令出错(需要 set -E) |
| HUP | 挂起 |
#!/bin/bash
set -E
trap 'echo "错误发生在第 $LINENO 行"; exit 1' ERR
command_not_exist
set -E 让 trap ERR 生效。
#!/bin/bash
error_exit() {
echo "错误:$1" >&2
exit "${2:-1}"
}
check_file() {
if [ ! -f "$1" ]; then
error_exit "文件 $1 不存在" 2
fi
}
check_file "/etc/passwd"
check_file "/notexist"
有时候需要忽略某个命令的错误:
rm -f /tmp/temp 2>/dev/null || true
|| true 确保整个表达式的退出码是 0。
retry() {
local max_attempts=$1
local delay=$2
shift 2
local cmd="$@"
local attempt=1
while [ $attempt -le $max_attempts ]; do
if $cmd; then
return 0
fi
echo "第 $attempt 次尝试失败,等待 ${delay}秒..."
sleep $delay
attempt=$((attempt + 1))
done
echo "重试 $max_attempts 次后仍然失败"
return 1
}
retry 3 2 curl -s http://example.com
timeout 10s command || echo "命令超时"
或者用脚本实现:
run_with_timeout() {
local timeout=$1
shift
local cmd="$@"
$cmd &
local pid=$!
(
sleep $timeout
if kill -0 $pid 2>/dev/null; then
kill $pid
fi
) &
local watchdog=$!
wait $pid
local status=$?
kill $watchdog 2>/dev/null
return $status
}
#!/bin/bash
set -euo pipefail
cleanup() {
rm -f "$temp_file"
}
trap cleanup EXIT
temp_file=$(mktemp)
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
}
error() {
log "错误:$*" >&2
exit 1
}
main() {
log "开始执行"
# 检查参数
[ $# -ge 1 ] || error "需要提供参数"
# 执行操作
some_command || error "命令执行失败"
log "执行完成"
}
main "$@"
safe_delete() {
local file=$1
[ -e "$file" ] || { echo "文件不存在"; return 1; }
[ -w "$file" ] || { echo "无写权限"; return 1; }
read -p "确定删除 $file?(y/n) " confirm
[ "$confirm" = "y" ] || { echo "取消删除"; return 0; }
rm -f "$file"
}
$? 保存上一个命令的退出状态if、&&、|| 可以检测命令执行结果set -E 配合 trap ERR 捕获错误