Linux基础教程 第12课:Shell脚本进阶和自动化

浏览量:23 次 发布时间:2026-01-21 18:55 作者:明扬工控商城 下载docx

最近更新:Linux基础教程 第15课:Linux内核和驱动管理


好的,我们继续第十二课。今天学习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)。


明扬工控商城

推荐阅读:

Linux基础教程 第20课:Linux安全攻防和渗透测试基础

Linux基础教程 第19课:性能调优和容量规划

Linux基础教程 第18课:Linux云计算基础

Linux基础教程 第17课:自动化运维工具

Linux基础教程 第16课:集群和高可用性

Linux基础教程 第15课:Linux内核和驱动管理

热门标签:
Linux基础教程 第12课:Shell脚本进阶和自动化.docx

将本文的Word文档下载到电脑

推荐度:

下载

全部评论

请登录
产业新闻-明扬资讯网
科技资讯-明扬资讯网