2026-01-23
最近更新:Linux基础教程 第18课:Linux云计算基础
2026-01-23
2026-01-23
2026-01-23
最近更新:Linux基础教程 第15课:Linux内核和驱动管理
2026-01-21
浏览量:23 次 发布时间:2026-01-21 18:55 作者:明扬工控商城 下载docx
2026-01-23
最近更新:Linux基础教程 第18课:Linux云计算基础
2026-01-23
2026-01-23
2026-01-23
最近更新:Linux基础教程 第15课:Linux内核和驱动管理
2026-01-21
好的,我们继续第十二课。今天学习Shell脚本的进阶功能和自动化技巧。
第一部分:Shell脚本进阶功能
1.1 高级变量操作
变量扩展和字符串操作
bash
#!/bin/bash
# 字符串操作示例
str="Hello World Linux Shell Scripting"
# 字符串长度
echo "字符串长度: ${#str}"
# 子字符串提取
echo "前5个字符: ${str:0:5}"
echo "从第6个开始: ${str:6}"
echo "最后6个字符: ${str:(-6)}"
# 字符串替换
echo "替换第一个: ${str/World/Universe}"
echo "替换所有: ${str//l/L}"
# 字符串删除
echo "删除最短匹配前缀: ${str#Hello }"
echo "删除最长匹配前缀: ${str##Hello }"
echo "删除最短匹配后缀: ${str%Scripting}"
echo "删除最长匹配后缀: ${str%% Scripting}"
# 大小写转换
echo "大写: ${str^^}"
echo "小写: ${str,,}"
echo "首字母大写: ${str^}"
# 默认值
unset myvar
echo "默认值: ${myvar:-默认值}"
echo "设置默认值: ${myvar:=默认值}"
echo "变量为空时报错: ${myvar:?变量未定义}"
echo "变量不为空时替换: ${myvar:+有值}"
数组操作
bash
#!/bin/bash
# 声明数组
fruits=("apple" "banana" "orange" "grape" "kiwi")
# 访问数组元素
echo "第一个水果: ${fruits[0]}"
echo "所有水果: ${fruits[@]}"
echo "水果数量: ${#fruits[@]}"
# 遍历数组
for fruit in "${fruits[@]}"; do
echo "水果: $fruit"
done
# 带索引的遍历
for i in "${!fruits[@]}"; do
echo "索引 $i: ${fruits[i]}"
done
# 数组切片
echo "前三个: ${fruits[@]:0:3}"
echo "从第二个开始: ${fruits[@]:2}"
# 添加元素
fruits+=("melon" "pear")
echo "添加后: ${fruits[@]}"
# 删除元素
unset fruits[1]
echo "删除banana后: ${fruits[@]}"
echo "索引重排: ${!fruits[@]}"
# 关联数组(需要bash 4.0+)
declare -A person
person=([name]="张三" [age]=25 [city]="北京")
echo "姓名: ${person[name]}"
echo "城市: ${person[city]}"
echo "所有键: ${!person[@]}"
echo "所有值: ${person[@]}"
1.2 函数进阶
返回值处理
bash
#!/bin/bash
# 函数返回值的各种方式
# 方法1:使用return(只能返回0-255)
function return_example() {
local value=42
return $value
}
return_example
echo "return返回值: $?"
# 方法2:使用全局变量
global_result=""
function global_example() {
global_result="通过全局变量返回值"
}
global_example
echo "$global_result"
# 方法3:使用echo输出
function echo_example() {
local result="通过echo返回值"
echo "$result"
}
value=$(echo_example)
echo "捕获输出: $value"
# 方法4:返回多个值
function multiple_example() {
local name="$1"
local age="$2"
echo "$name:$age"
}
read name age <<< $(multiple_example "李四" 30)
echo "姓名: $name, 年龄: $age"
# 方法5:使用关联数组返回多个值
function array_example() {
declare -A info
info[name]="王五"
info[age]=28
info[city]="上海"
# 返回关联数组(通过declare -p)
declare -p info
}
# 捕获并重建关联数组
eval $(array_example)
echo "数组返回 - 姓名: ${info[name]}, 城市: ${info[city]}"
递归函数
bash
#!/bin/bash
# 阶乘计算(递归)
function factorial() {
local n=$1
if [ $n -eq 0 ] || [ $n -eq 1 ]; then
echo 1
else
local prev=$(factorial $((n-1)))
echo $((n * prev))
fi
}
echo "5的阶乘: $(factorial 5)"
echo "10的阶乘: $(factorial 10)"
# 斐波那契数列(递归)
function fibonacci() {
local n=$1
if [ $n -eq 0 ]; then
echo 0
elif [ $n -eq 1 ]; then
echo 1
else
local a=$(fibonacci $((n-1)))
local b=$(fibonacci $((n-2)))
echo $((a + b))
fi
}
echo "斐波那契数列前10项:"
for i in {0..9}; do
echo -n "$(fibonacci $i) "
done
echo
1.3 进程和信号处理
信号处理
bash
#!/bin/bash
# 信号处理函数
function cleanup() {
echo "收到信号,正在清理..."
rm -f /tmp/tempfile_$$
echo "清理完成,退出"
exit 0
}
# 注册信号处理
trap cleanup SIGINT SIGTERM SIGQUIT
# 创建临时文件
echo "创建临时文件 /tmp/tempfile_$$"
touch /tmp/tempfile_$$
echo "脚本运行中,按 Ctrl+C 测试信号处理..."
echo "PID: $$"
# 模拟长时间运行
counter=0
while true; do
echo "运行中... $counter"
sleep 1
counter=$((counter + 1))
if [ $counter -eq 10 ]; then
echo "正常结束"
cleanup
fi
done
后台进程控制
bash
#!/bin/bash
# 启动后台进程
start_background_jobs() {
for i in {1..3}; do
(
echo "后台作业 $i 开始"
sleep $((i * 2))
echo "后台作业 $i 完成"
) &
jobs[$i]=$! # 保存PID
done
}
# 等待所有后台进程
wait_for_jobs() {
echo "等待所有作业完成..."
for pid in "${jobs[@]}"; do
wait $pid
echo "作业 $pid 已完成"
done
echo "所有作业完成"
}
# 杀死所有后台进程
kill_all_jobs() {
echo "正在终止所有后台作业..."
for pid in "${jobs[@]}"; do
kill $pid 2>/dev/null
done
wait # 等待所有进程终止
echo "所有作业已终止"
}
# 主程序
declare -A jobs
start_background_jobs
# 等待用户输入
read -t 5 -p "输入 'k' 终止作业,或等待5秒: " input
if [ "$input" = "k" ]; then
kill_all_jobs
else
wait_for_jobs
fi
第二部分:文本处理进阶
2.1 高级awk使用
bash
#!/bin/bash
# 创建测试数据
cat > employees.csv << 'EOF'
ID,Name,Department,Salary,JoinDate
101,张三,技术部,15000,2020-01-15
102,李四,市场部,12000,2019-03-20
103,王五,技术部,18000,2018-07-10
104,赵六,财务部,10000,2021-02-28
105,钱七,市场部,13000,2020-11-05
106,孙八,技术部,16000,2019-09-15
EOF
echo "=== 高级awk示例 ==="
# 1. 条件筛选
echo "1. 技术部员工:"
awk -F, '$3 == "技术部" {print $2, $4}' employees.csv
# 2. 计算统计
echo -e "\n2. 统计信息:"
awk -F, '
NR>1 {
dept[$3]++
total_salary[$3] += $4
if ($4 > max[$3]) {
max[$3] = $4
max_name[$3] = $2
}
}
END {
printf "%-10s %-6s %-10s %-10s\n", "部门", "人数", "平均工资", "最高工资者"
for (d in dept) {
avg = total_salary[d] / dept[d]
printf "%-10s %-6d %-10.2f %-10s\n", d, dept[d], avg, max_name[d]
}
}' employees.csv
# 3. 时间处理
echo -e "\n3. 按入职年份统计:"
awk -F, '
NR>1 {
split($5, date, "-")
year = date[1]
years[year]++
}
END {
print "入职年份分布:"
for (y in years) {
printf "%s年: %d人\n", y, years[y]
}
}' employees.csv
# 4. 复杂计算
echo -e "\n4. 工资等级:"
awk -F, '
NR>1 {
level = "普通"
if ($4 >= 16000) level = "高级"
else if ($4 <= 11000) level = "初级"
printf "%-10s %-10s %-8d %-10s\n", $2, $3, $4, level
}' employees.csv
2.2 sed进阶技巧
bash
#!/bin/bash
# 创建测试文件
cat > config.txt << 'EOF'
# 服务器配置
server {
hostname = "oldserver"
port = 8080
max_connections = 100
timeout = 30
}
# 数据库配置
database {
host = "localhost"
port = 3306
username = "olduser"
password = "oldpass"
}
# 应用配置
app {
name = "myapp"
version = "1.0"
debug = true
}
EOF
echo "=== sed进阶示例 ==="
# 1. 多命令执行
echo "1. 批量替换配置:"
sed -i.bak '
s/oldserver/newserver/g
s/olduser/newuser/g
s/oldpass/newpass/g
s/8080/9090/g
' config.txt
echo "修改后的配置:"
cat config.txt
# 2. 模式空间和保持空间
echo -e "\n2. 反转文件行:"
tac config.txt # 使用tac命令反转,或使用sed:
sed '1!G;h;$!d' config.txt | head -5
# 3. 条件执行
echo -e "\n3. 只修改特定块:"
cp config.txt config2.txt
sed -i '/^database/,/^}/ {
s/3306/5432/
s/localhost/dbhost/
}' config2.txt
echo "修改数据库配置后:"
sed -n '/^database/,/^}/p' config2.txt
# 4. 复杂模式匹配
echo -e "\n4. 提取所有配置项:"
sed -n 's/[[:space:]]*\([a-zA-Z_]*\)[[:space:]]*=[[:space:]]*\(.*\)/\1: \2/p' config.txt
2.3 grep高级用法
bash
#!/bin/bash
# 创建测试日志
cat > app.log << 'EOF'
2024-01-15 10:30:15 INFO User login successful user_id=123
2024-01-15 10:31:20 ERROR Database connection failed error_code=5001
2024-01-15 10:32:45 WARN High memory usage: 85%
2024-01-15 10:33:10 INFO Request processed in 150ms request_id=abc123
2024-01-15 10:34:30 ERROR File not found: /path/to/file
2024-01-15 10:35:00 INFO User logout user_id=123
2024-01-15 10:36:15 INFO New user registered user_id=124
2024-01-15 10:37:40 WARN Slow query: 2.5s query_id=xyz789
EOF
echo "=== grep进阶示例 ==="
# 1. 上下文查看
echo "1. ERROR日志及其前后行:"
grep -B1 -A1 "ERROR" app.log
# 2. Perl兼容正则表达式
echo -e "\n2. 提取所有用户ID:"
grep -oP 'user_id=\K\d+' app.log
# 3. 统计不同类型日志
echo -e "\n3. 日志类型统计:"
grep -E "(INFO|ERROR|WARN)" app.log | awk '{count[$3]++} END {for(t in count) print t, count[t]}'
# 4. 复杂模式匹配
echo -e "\n4. 时间范围内的日志:"
awk '/2024-01-15 10:3[2-5]:/ {print}' app.log
# 5. 排除特定模式
echo -e "\n5. 非INFO日志:"
grep -v "INFO" app.log
# 6. 多个模式匹配
echo -e "\n6. 包含'user'或'query'的日志:"
grep -E "user|query" app.log
第三部分:自动化脚本设计模式
3.1 配置驱动脚本
bash
#!/bin/bash
# config_automation.sh
# 读取配置文件
CONFIG_FILE="$(dirname "$0")/automation.conf"
# 默认配置
DEFAULT_SOURCE_DIR="/home/user/documents"
DEFAULT_BACKUP_DIR="/backup"
DEFAULT_RETENTION_DAYS=7
# 加载配置
load_config() {
if [ -f "$CONFIG_FILE" ]; then
source "$CONFIG_FILE"
else
echo "配置文件不存在,使用默认值" >&2
fi
# 使用配置值或默认值
SOURCE_DIR="${SOURCE_DIR:-$DEFAULT_SOURCE_DIR}"
BACKUP_DIR="${BACKUP_DIR:-$DEFAULT_BACKUP_DIR}"
RETENTION_DAYS="${RETENTION_DAYS:-$DEFAULT_RETENTION_DAYS}"
}
# 验证配置
validate_config() {
local errors=()
[ -d "$SOURCE_DIR" ] || errors+=("源目录不存在: $SOURCE_DIR")
[ -d "$BACKUP_DIR" ] || mkdir -p "$BACKUP_DIR" 2>/dev/null || errors+=("无法创建备份目录: $BACKUP_DIR")
[[ "$RETENTION_DAYS" =~ ^[0-9]+$ ]] || errors+=("保留天数必须是数字: $RETENTION_DAYS")
if [ ${#errors[@]} -gt 0 ]; then
for error in "${errors[@]}"; do
echo "错误: $error" >&2
done
return 1
fi
return 0
}
# 主函数
main() {
load_config
if ! validate_config; then
exit 1
fi
echo "配置加载成功:"
echo " 源目录: $SOURCE_DIR"
echo " 备份目录: $BACKUP_DIR"
echo " 保留天数: $RETENTION_DAYS"
# 执行备份操作
backup_name="backup_$(date +%Y%m%d_%H%M%S).tar.gz"
echo "正在备份 $SOURCE_DIR 到 $BACKUP_DIR/$backup_name"
if tar -czf "$BACKUP_DIR/$backup_name" -C "$(dirname "$SOURCE_DIR")" "$(basename "$SOURCE_DIR")" 2>/dev/null; then
echo "备份成功"
# 清理旧备份
echo "清理 $RETENTION_DAYS 天前的备份..."
find "$BACKUP_DIR" -name "backup_*.tar.gz" -mtime +$RETENTION_DAYS -delete
echo "完成"
else
echo "备份失败" >&2
exit 1
fi
}
# 运行主函数
main
bash
# 配置文件 automation.conf
SOURCE_DIR="/home/ubuntu/important_files"
BACKUP_DIR="/mnt/backup"
RETENTION_DAYS=30
LOG_FILE="/var/log/backup.log"
COMPRESSION_LEVEL=9
3.2 状态机模式脚本
bash
#!/bin/bash
# state_machine.sh
# 状态定义
declare -A STATES=(
[IDLE]="空闲"
[PROCESSING]="处理中"
[PAUSED]="已暂停"
[COMPLETED]="已完成"
[ERROR]="错误"
)
# 当前状态
CURRENT_STATE="IDLE"
CURRENT_TASK=""
# 状态转移函数
change_state() {
local new_state="$1"
local reason="$2"
echo "[$(date '+%H:%M:%S')] 状态转移: ${STATES[$CURRENT_STATE]} -> ${STATES[$new_state]} (原因: $reason)"
CURRENT_STATE="$new_state"
}
# 任务处理函数
process_task() {
local task="$1"
change_state "PROCESSING" "开始处理任务: $task"
CURRENT_TASK="$task"
# 模拟处理过程
for i in {1..5}; do
echo " 处理 $task: 步骤 $i/5"
sleep 1
# 随机模拟错误
if [ $((RANDOM % 10)) -eq 0 ]; then
change_state "ERROR" "处理任务时发生错误"
return 1
fi
done
change_state "COMPLETED" "任务完成"
CURRENT_TASK=""
return 0
}
# 状态机主循环
state_machine_loop() {
local tasks=("任务A" "任务B" "任务C" "任务D")
for task in "${tasks[@]}"; do
case $CURRENT_STATE in
IDLE)
if process_task "$task"; then
change_state "IDLE" "准备下一个任务"
fi
;;
ERROR)
echo "错误状态,尝试恢复..."
sleep 2
change_state "IDLE" "错误恢复"
;;
PAUSED)
echo "暂停状态,等待恢复..."
read -p "按回车继续或输入q退出: " input
if [ "$input" != "q" ]; then
change_state "IDLE" "手动恢复"
else
return
fi
;;
*)
echo "未知状态: $CURRENT_STATE"
return 1
;;
esac
# 检查是否需要暂停
if [ $((RANDOM % 4)) -eq 0 ]; then
change_state "PAUSED" "随机暂停"
fi
sleep 1
done
echo "所有任务完成"
}
# 运行状态机
echo "=== 状态机示例 ==="
state_machine_loop
3.3 插件式架构脚本
bash
#!/bin/bash
# plugin_architecture.sh
# 插件目录
PLUGIN_DIR="$(dirname "$0")/plugins"
mkdir -p "$PLUGIN_DIR"
# 创建示例插件
cat > "$PLUGIN_DIR/backup.sh" << 'EOF'
#!/bin/bash
# 备份插件
plugin_name="备份工具"
plugin_version="1.0"
backup_init() {
echo "初始化备份插件"
}
backup_run() {
local source="$1"
local destination="$2"
echo "正在备份 $source 到 $destination"
tar -czf "$destination/backup_$(date +%Y%m%d).tar.gz" "$source" 2>/dev/null
return $?
}
backup_cleanup() {
echo "清理备份插件"
}
EOF
cat > "$PLUGIN_DIR/cleanup.sh" << 'EOF'
#!/bin/bash
# 清理插件
plugin_name="清理工具"
plugin_version="1.0"
cleanup_init() {
echo "初始化清理插件"
}
cleanup_run() {
local dir="$1"
local days="$2"
echo "清理 $dir 中 $days 天前的文件"
find "$dir" -type f -mtime +$days -delete
return $?
}
cleanup_cleanup() {
echo "清理清理插件"
}
EOF
# 插件管理器
load_plugins() {
local plugin_manager=()
for plugin in "$PLUGIN_DIR"/*.sh; do
if [ -f "$plugin" ]; then
source "$plugin"
plugin_manager+=("$(basename "$plugin" .sh)")
echo "已加载插件: $plugin"
fi
done
echo "共加载 ${#plugin_manager[@]} 个插件"
echo "插件列表: ${plugin_manager[*]}"
}
# 插件调度器
run_plugin() {
local plugin="$1"
local action="$2"
shift 2
# 动态调用插件函数
if type "${plugin}_${action}" &>/dev/null; then
"${plugin}_${action}" "$@"
else
echo "错误: 插件 $plugin 不支持操作 $action" >&2
return 1
fi
}
# 主程序
main() {
load_plugins
echo -e "\n=== 运行备份插件 ==="
run_plugin "backup" "init"
run_plugin "backup" "run" "/home/ubuntu/documents" "/tmp"
run_plugin "backup" "cleanup"
echo -e "\n=== 运行清理插件 ==="
run_plugin "cleanup" "init"
run_plugin "cleanup" "run" "/tmp" 1
run_plugin "cleanup" "cleanup"
echo -e "\n=== 所有插件执行完成 ==="
}
# 执行
chmod +x "$PLUGIN_DIR"/*.sh
main
第四部分:系统集成和调度
4.1 Systemd服务集成
bash
# 创建服务脚本
sudo vim /opt/myapp/myapp.sh
bash
#!/bin/bash
# 服务主脚本
# 导入配置
CONFIG_FILE="/etc/myapp/config.conf"
[ -f "$CONFIG_FILE" ] && source "$CONFIG_FILE"
# 日志函数
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') [$1] $2" >> "${LOG_FILE:-/var/log/myapp.log}"
}
# 服务启动
start_service() {
log "INFO" "启动服务"
# 检查是否已运行
if [ -f "/var/run/myapp.pid" ]; then
local pid=$(cat "/var/run/myapp.pid")
if kill -0 "$pid" 2>/dev/null; then
log "WARN" "服务已在运行 (PID: $pid)"
return 1
fi
fi
# 主服务循环
while true; do
log "INFO" "执行任务..."
# 执行实际任务
perform_task
# 检查停止标志
if [ -f "/tmp/myapp.stop" ]; then
log "INFO" "收到停止信号"
rm -f "/tmp/myapp.stop"
break
fi
# 等待间隔
sleep "${INTERVAL:-60}"
done
log "INFO" "服务停止"
}
# 任务函数
perform_task() {
# 这里是实际的任务逻辑
echo "执行任务中..." > /dev/null
# 可以调用其他函数或脚本
}
# 信号处理
trap 'log "INFO" "收到终止信号"; exit 0' SIGTERM SIGINT
# 主函数
main() {
case "$1" in
start)
start_service &
echo $! > "/var/run/myapp.pid"
;;
stop)
touch "/tmp/myapp.stop"
;;
status)
if [ -f "/var/run/myapp.pid" ]; then
local pid=$(cat "/var/run/myapp.pid")
if kill -0 "$pid" 2>/dev/null; then
echo "服务运行中 (PID: $pid)"
else
echo "服务未运行"
fi
else
echo "服务未运行"
fi
;;
*)
echo "用法: $0 {start|stop|status}"
exit 1
;;
esac
}
main "$@"
bash
# 创建Systemd服务文件
sudo vim /etc/systemd/system/myapp.service
ini
[Unit]
Description=My Application Service
After=network.target
[Service]
Type=forking
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/myapp.sh start
ExecStop=/opt/myapp/myapp.sh stop
ExecReload=/bin/kill -HUP $MAINPID
PIDFile=/var/run/myapp.pid
Restart=on-failure
RestartSec=10
# 资源限制
MemoryLimit=512M
CPUQuota=50%
# 安全设置
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=/var/log/myapp /tmp
[Install]
WantedBy=multi-user.target
bash
# 设置权限并启用
sudo chmod +x /opt/myapp/myapp.sh
sudo systemctl daemon-reload
sudo systemctl enable myapp.service
sudo systemctl start myapp.service
sudo systemctl status myapp.service
4.2 高级定时任务管理
bash
#!/bin/bash
# cron_manager.sh
# 配置
CRON_DIR="/etc/cron.d"
BACKUP_DIR="/var/backup/cron"
LOG_FILE="/var/log/cron_manager.log"
# 日志函数
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG_FILE"
}
# 备份cron配置
backup_cron() {
local timestamp=$(date +%Y%m%d_%H%M%S)
local backup_file="$BACKUP_DIR/cron_backup_$timestamp.tar.gz"
mkdir -p "$BACKUP_DIR"
tar -czf "$backup_file" -C /etc cron.d crontab 2>/dev/null
if [ $? -eq 0 ]; then
log "备份成功: $backup_file"
# 清理旧备份(保留最近7天)
find "$BACKUP_DIR" -name "cron_backup_*.tar.gz" -mtime +7 -delete
else
log "备份失败"
return 1
fi
}
# 添加定时任务
add_cron_job() {
local schedule="$1"
local command="$2"
local job_name="$3"
local cron_file="$CRON_DIR/$job_name"
backup_cron || return 1
# 创建cron文件
cat > "$cron_file" << EOF
# $job_name - 添加时间: $(date)
$schedule root $command
EOF
if [ $? -eq 0 ]; then
log "添加任务: $job_name"
echo "任务添加成功: $job_name"
else
log "添加任务失败: $job_name"
return 1
fi
}
# 列出所有定时任务
list_cron_jobs() {
echo "=== 系统cron任务 ==="
for file in "$CRON_DIR"/*; do
if [ -f "$file" ]; then
echo "文件: $(basename "$file")"
cat "$file"
echo
fi
done
echo "=== 用户cron任务 ==="
for user in $(cut -d: -f1 /etc/passwd); do
local jobs=$(crontab -l -u "$user" 2>/dev/null)
if [ -n "$jobs" ]; then
echo "用户: $user"
echo "$jobs"
echo
fi
done
}
# 验证cron语法
validate_cron_schedule() {
local schedule="$1"
# 简单的cron表达式验证
local fields=($schedule)
if [ ${#fields[@]} -ne 5 ]; then
echo "错误: cron表达式必须有5个字段" >&2
return 1
fi
# 验证每个字段
for i in {0..4}; do
if ! [[ "${fields[$i]}" =~ ^[0-9*/,-]+$ ]]; then
echo "错误: 第$((i+1))个字段无效: ${fields[$i]}" >&2
return 1
fi
done
return 0
}
# 交互式菜单
interactive_menu() {
while true; do
clear
echo "=== Cron任务管理器 ==="
echo "1. 列出所有任务"
echo "2. 添加新任务"
echo "3. 备份配置"
echo "4. 查看日志"
echo "5. 退出"
echo
read -p "请选择: " choice
case $choice in
1)
list_cron_jobs | less
;;
2)
read -p "输入cron表达式(分 时 日 月 周): " schedule
if validate_cron_schedule "$schedule"; then
read -p "输入要执行的命令: " command
read -p "输入任务名称: " job_name
add_cron_job "$schedule" "$command" "$job_name"
fi
read -p "按回车继续..."
;;
3)
backup_cron
echo "备份完成"
read -p "按回车继续..."
;;
4)
less "$LOG_FILE"
;;
5)
echo "退出"
exit 0
;;
*)
echo "无效选择"
sleep 1
;;
esac
done
}
# 主函数
main() {
if [ "$1" = "--interactive" ]; then
interactive_menu
else
# 非交互模式
case "$1" in
list)
list_cron_jobs
;;
add)
if [ $# -lt 4 ]; then
echo "用法: $0 add 'schedule' 'command' 'job_name'"
exit 1
fi
add_cron_job "$2" "$3" "$4"
;;
backup)
backup_cron
;;
*)
echo "用法: $0 {--interactive|list|add|backup}"
exit 1
;;
esac
fi
}
# 运行主函数
main "$@"
第五部分:错误处理和日志系统
5.1 高级错误处理框架
bash
#!/bin/bash
# error_handling_framework.sh
# 错误处理框架
set -Euo pipefail
# 错误级别
declare -A ERROR_LEVELS=(
[DEBUG]=0
[INFO]=1
[WARN]=2
[ERROR]=3
[FATAL]=4
)
# 配置
LOG_LEVEL="${LOG_LEVEL:-INFO}"
LOG_FILE="${LOG_FILE:-/var/log/script_errors.log}"
# 日志函数
log() {
local level="$1"
local message="$2"
# 检查日志级别
[ ${ERROR_LEVELS[$level]} -lt ${ERROR_LEVELS[$LOG_LEVEL]} ] && return
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
local log_entry="$timestamp [$level] $message"
# 输出到文件
echo "$log_entry" >> "$LOG_FILE"
# 根据级别输出到stderr
case $level in
ERROR|FATAL)
echo "$log_entry" >&2
;;
*)
echo "$log_entry"
;;
esac
}
# 错误处理函数
error_handler() {
local exit_code=$?
local line_no=$1
local command=$2
log "ERROR" "脚本在行 $line_no 失败: '$command' (退出码: $exit_code)"
# 堆栈跟踪
local depth=0
while caller $depth > /dev/null 2>&1; do
local frame=$(caller $depth)
log "DEBUG" " 栈帧 $depth: $frame"
((depth++))
done
# 清理资源
cleanup_resources
exit $exit_code
}
# 信号处理
signal_handler() {
local signal=$1
log "WARN" "收到信号 $signal,正在清理..."
cleanup_resources
exit 0
}
# 清理函数
cleanup_resources() {
log "INFO" "清理资源..."
# 清理临时文件
find /tmp -name "temp_*" -mmin +60 -delete 2>/dev/null || true
# 释放锁
release_locks
log "INFO" "清理完成"
}
# 锁管理
acquire_lock() {
local lock_file="$1"
local timeout="${2:-10}"
local start_time=$(date +%s)
while [ -f "$lock_file" ]; do
local current_time=$(date +%s)
local elapsed=$((current_time - start_time))
if [ $elapsed -ge $timeout ]; then
log "ERROR" "获取锁超时: $lock_file"
return 1
fi
log "DEBUG" "等待锁: $lock_file (已等待 ${elapsed}s)"
sleep 1
done
echo $$ > "$lock_file"
log "DEBUG" "获取锁: $lock_file"
return 0
}
release_locks() {
# 释放当前进程持有的所有锁
find /tmp -name "*.lock" -exec grep -l "^$$$" {} \; 2>/dev/null | while read lock_file; do
rm -f "$lock_file"
log "DEBUG" "释放锁: $lock_file"
done
}
# 重试函数
with_retry() {
local max_attempts="${1:-3}"
local delay="${2:-1}"
local attempt=1
shift 2
while [ $attempt -le $max_attempts ]; do
log "INFO" "执行尝试 $attempt/$max_attempts: $*"
if "$@"; then
log "INFO" "尝试 $attempt 成功"
return 0
fi
log "WARN" "尝试 $attempt 失败"
if [ $attempt -lt $max_attempts ]; then
log "INFO" "等待 ${delay}s 后重试..."
sleep $delay
fi
((attempt++))
done
log "ERROR" "所有 $max_attempts 次尝试都失败"
return 1
}
# 设置错误处理和信号处理
trap 'error_handler ${LINENO} "$BASH_COMMAND"' ERR
trap 'signal_handler SIGINT' SIGINT
trap 'signal_handler SIGTERM' SIGTERM
# 示例使用
main() {
log "INFO" "脚本开始"
# 获取锁
local lock_file="/tmp/myapp.lock"
if ! acquire_lock "$lock_file" 5; then
log "FATAL" "无法获取锁,退出"
exit 1
fi
# 使用重试执行命令
with_retry 3 2 ls /nonexistent || true
# 模拟正常操作
log "INFO" "执行主任务..."
sleep 2
# 故意制造错误
# false
log "INFO" "任务完成"
}
# 运行主函数
main
第六部分:练习项目
项目1:自动化部署系统
创建一个完整的自动化部署系统,包含以下功能:
版本控制集成(Git)
环境配置管理
依赖安装
数据库迁移
服务重启
回滚功能
通知系统
项目2:监控报警平台
创建一个监控报警平台,包含:
多主机监控
自定义监控项
阈值设置
多种报警方式(邮件、短信、Webhook)
报警升级机制
历史数据存储和查询
可视化报表
项目3:配置管理工具
创建一个配置管理工具,包含:
配置版本控制
差异对比
批量部署
配置验证
回滚功能
权限管理
审计日志
项目4:数据备份恢复系统
创建一个完整的数据备份恢复系统:
全量备份和增量备份
备份验证
加密和压缩
异地备份
恢复演练
备份策略管理
监控和报告
今日总结
今天我们学习了Shell脚本的进阶功能:
高级变量操作:字符串处理、数组操作、关联数组
函数进阶:多种返回值方式、递归函数、信号处理
文本处理进阶:高级awk、sed、grep技巧
设计模式:配置驱动、状态机、插件架构
系统集成:Systemd服务、高级cron管理
错误处理:完善的错误处理框架和日志系统
重要概念:
Shell脚本可以很强大,但也要保持可读性和可维护性
错误处理是健壮脚本的关键
模块化和可配置性很重要
安全性和资源管理不可忽视
建议你在实际工作中应用这些模式,并根据需要调整。每个项目都有不同的需求,灵活运用这些技术才能写出好的脚本。
有问题吗?完成练习项目后,你对Shell脚本的理解会更加深入。
下一课我们将学习Linux容器技术基础(Docker)。
将本文的Word文档下载到电脑
推荐度: