超越基础:Shell脚本高级技巧与错误处理
掌握了Shell脚本的基础后,是时候迈向更高层次------编写健壮、高效且可维护的脚本。
函数与模块化:如何编写可复用的函数
当脚本变得复杂时,重复代码会降低可读性和维护性。函数可以将代码模块化,提高复用性。
定义和调用函数
函数定义无需特别关键字,直接写函数名和代码块:
bash
#!/bin/bash
# 定义函数
say_hello() {
echo "Hello, $1!"
}
# 调用函数
say_hello "Alice"
say_hello "Bob"
输出:
text
Hello, Alice!
Hello, Bob!

- $1 是函数的第一个参数,类似脚本中的命令行参数。
带返回值的函数
Shell函数返回值通常通过全局变量或退出码实现:
bash
#!/bin/bash
add_numbers() {
local SUM=$(( $1 + $2 ))
echo $SUM # 输出结果
}
RESULT=$(add_numbers 3 5)
echo "3 + 5 = $RESULT"
输出:3 + 5 = 8
模块化实践
将常用功能封装成函数,并保存到外部文件(如 utils.sh),然后在脚本中引入:
bash
# utils.sh
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> /var/log/script.log
}
bash
#!/bin/bash
source ./utils.sh
log "脚本开始执行"
echo "做一些工作..."
log "脚本结束"
通过 source 或 .
引入外部文件,实现代码复用。
错误处理:退出码、trap和日志
生产环境的脚本必须能处理错误,避免因异常导致系统混乱。
使用退出码
每个命令执行后都有一个退出码(0 表示成功,非 0 表示失败),可用 $? 检查:
bash
#!/bin/bash
ls /nonexistent
if [ $? -ne 0 ]; then
echo "错误:目录不存在"
exit 1 # 非零退出码表示脚本失败
fi

trap捕获信号
trap 命令可捕获中断信号(如Ctrl+C)或脚本退出时的状态:
bash
#!/bin/bash
cleanup() {
echo "脚本被中断,正在清理..."
# 清理临时文件等
exit 1
}
trap cleanup INT TERM # 捕获中断和终止信号
echo "运行中,按Ctrl+C测试"
sleep 100
按Ctrl+C后,脚本会执行 cleanup 函数。
记录错误日志
将错误信息记录到日志文件,便于排查问题:
bash
#!/bin/bash
LOG_FILE="/var/log/script_errors.log"
log_error() {
echo "$(date) - ERROR: $1" >> "$LOG_FILE"
}
ls /nonexistent 2>/dev/null || log_error "无法访问目录"
echo "继续执行..."
- 2>/dev/null:屏蔽标准错误输出。
- ||:前命令失败时执行后命令。
脚本优化:提高性能的最佳实践
高效的脚本不仅功能强大,还要运行迅速且资源占用低。
1. 减少外部命令调用
频繁调用外部命令(如 grep、awk)会增加开销,尽量使用Shell内置功能:
bash
# 低效
COUNT=$(ls -l | wc -l)
# 高效
files=( * )
COUNT=${#files[@]}
2. 并行执行
对于耗时任务,使用后台进程(&)并行运行:
bash
#!/bin/bash
task() {
echo "任务 $1 開始"
sleep 2
echo "任務 $1 完成"
}
task 1 &
task 2 &
wait # 等待所有后台任务完成
3. 避免冗余操作
缓存重复使用的结果:
bash
#!/bin/bash
# 每次调用date都重新计算
for i in {1..3}; do
echo "$(date) - 循环 $i"
done
# 优化:只计算一次
NOW=$(date)
for i in {1..3}; do
echo "$NOW - 循环 $i"
done
实战案例:自动化用户管理
以下是一个完整的用户管理脚本,包含创建、删除用户和权限设置功能。
bash
#!/bin/bash
LOG_FILE="/var/log/user_management.log"
USERNAME=""
ACTION=""
# 函数:记录日志
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
}
# 函数:检查用户是否存在
check_user() {
if id "$1" &>/dev/null; then
return 0 # 用户存在
else
return 1
fi
}
# 函数:创建用户
create_user() {
if check_user "$1"; then
log "用户 $1 已存在"
echo "用户 $1 已存在"
else
useradd -m "$1" && echo "用户 $1 创建成功" || log "创建用户 $1 失败"
passwd "$1" # 设置密码
chmod 700 "/home/$1" # 设置权限
log "用户 $1 创建并配置完成"
fi
}
# 函数:删除用户
delete_user() {
if check_user "$1"; then
userdel -r "$1" && echo "用户 $1 删除成功" || log "删除用户 $1 失败"
log "用户 $1 已删除"
else
echo "用户 $1 不存在"
log "尝试删除不存在的用户 $1"
fi
}
# 主逻辑
if [ $# -ne 2 ]; then
echo "用法:$0 {create|delete} 用户名"
exit 1
fi
ACTION=$1
USERNAME=$2
case $ACTION in
create) create_user "$USERNAME";;
delete) delete_user "$USERNAME";;
*) echo "无效操作:$ACTION"; exit 1;;
esac
exit 0
使用方法
- 创建用户:./user_manage.sh create testuser
- 删除用户:./user_manage.sh delete testuser
功能亮点
- 模块化:将创建和删除逻辑封装为函数。
- 错误处理:检查用户是否存在,记录失败日志。
- 安全性:设置合理的目录权限。