Shell流程控制

流程控制是Shell脚本编程的核心,通过条件判断和循环结构,可以实现复杂的逻辑控制。本章将详细介绍Shell脚本中的流程控制语句。


一、条件判断

1.1 if语句

1.1.1 基本if语句

语法格式

if [ 条件 ]; then
    命令
fi

示例

#!/bin/bash

age=18

# 基本if语句
if [ $age -ge 18 ]; then
    echo "成年人"
fi

# 检查文件是否存在
if [ -f "/etc/passwd" ]; then
    echo "文件存在"
fi

# 检查目录是否存在
if [ -d "/home" ]; then
    echo "目录存在"
fi

# 检查命令是否成功
if ls /etc &> /dev/null; then
    echo "命令执行成功"
fi

东巴文提示if语句以fi结尾,这是Shell脚本的特色。

1.1.2 if-else语句

语法格式

if [ 条件 ]; then
    命令1
else
    命令2
fi

示例

#!/bin/bash

score=75

# if-else语句
if [ $score -ge 60 ]; then
    echo "及格"
else
    echo "不及格"
fi

# 检查用户是否存在
username="testuser"
if id "$username" &>/dev/null; then
    echo "用户 $username 已存在"
else
    echo "用户 $username 不存在"
fi

# 检查服务是否运行
service="nginx"
if systemctl is-active --quiet "$service"; then
    echo "$service 正在运行"
else
    echo "$service 未运行"
fi

1.1.3 if-elif-else语句

语法格式

if [ 条件1 ]; then
    命令1
elif [ 条件2 ]; then
    命令2
elif [ 条件3 ]; then
    命令3
else
    命令4
fi

示例

#!/bin/bash

score=85

# 成绩等级判断
if [ $score -ge 90 ]; then
    echo "优秀"
elif [ $score -ge 80 ]; then
    echo "良好"
elif [ $score -ge 70 ]; then
    echo "中等"
elif [ $score -ge 60 ]; then
    echo "及格"
else
    echo "不及格"
fi

# 系统类型判断
if [ -f /etc/debian_version ]; then
    echo "Debian/Ubuntu系统"
elif [ -f /etc/redhat-release ]; then
    echo "RedHat/CentOS系统"
elif [ -f /etc/arch-release ]; then
    echo "Arch Linux系统"
else
    echo "未知系统"
fi

# 时间段判断
hour=$(date +%H)
if [ $hour -lt 6 ]; then
    echo "凌晨"
elif [ $hour -lt 12 ]; then
    echo "上午"
elif [ $hour -lt 14 ]; then
    echo "中午"
elif [ $hour -lt 18 ]; then
    echo "下午"
else
    echo "晚上"
fi

东巴文理解elifelse if的缩写,用于多条件判断。

1.2 条件测试

1.2.1 test命令

test命令等价于[]

#!/bin/bash

# 使用test命令
if test -f "/etc/passwd"; then
    echo "文件存在"
fi

# 等价于
if [ -f "/etc/passwd" ]; then
    echo "文件存在"
fi

# test命令选项
test -z "$var"        # 字符串长度为0
test -n "$var"        # 字符串长度不为0
test "$a" = "$b"      # 字符串相等
test "$a" != "$b"     # 字符串不相等
test "$a" -eq "$b"    # 整数相等
test "$a" -ne "$b"    # 整数不相等
test -f "$file"       # 文件存在且为普通文件
test -d "$dir"        # 目录存在
test -r "$file"       # 文件可读
test -w "$file"       # 文件可写
test -x "$file"       # 文件可执行

1.2.2 [[]]测试

[[]][]更强大

#!/bin/bash

# [[]]支持&&和||
if [[ -f "/etc/passwd" && -r "/etc/passwd" ]]; then
    echo "文件存在且可读"
fi

# [[]]支持模式匹配
name="test.txt"
if [[ $name == *.txt ]]; then
    echo "这是文本文件"
fi

# [[]]支持正则表达式
email="user@example.com"
if [[ $email =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
    echo "邮箱格式正确"
fi

# [[]]不需要转义特殊字符
if [[ $name != "test" ]]; then
    echo "名称不是test"
fi

# 对比:[]需要转义
if [ "$name" != "test" ]; then
    echo "名称不是test"
fi

东巴文推荐:使用[[]],功能更强大,更安全。

1.2.3 (())测试

(())用于数值比较

#!/bin/bash

a=10
b=20

# 使用(())进行数值比较
if (( a < b )); then
    echo "$a 小于 $b"
fi

# 使用C风格语法
if (( a > 5 && b < 30 )); then
    echo "条件成立"
fi

# 自增自减
count=0
if (( count++ == 0 )); then
    echo "count初始值为0"
fi
echo "count现在为:$count"

# 复杂运算
if (( (a + b) > 25 )); then
    echo "a + b 大于 25"
fi

1.3 case语句

1.3.1 基本语法

语法格式

case 变量 in
    模式1)
        命令1
        ;;
    模式2)
        命令2
        ;;
    *)
        默认命令
        ;;
esac

示例

#!/bin/bash

# 菜单选择
echo "请选择操作:"
echo "1. 显示日期"
echo "2. 显示日历"
echo "3. 显示用户"
read -p "请输入选项(1-3):" choice

case $choice in
    1)
        echo "当前日期:$(date +%Y-%m-%d)"
        ;;
    2)
        cal
        ;;
    3)
        who
        ;;
    *)
        echo "无效选项"
        ;;
esac

东巴文理解case语句以esac结尾,每个模式以;;结束。

1.3.2 模式匹配

使用通配符

#!/bin/bash

# 文件类型判断
filename="test.txt"

case $filename in
    *.txt)
        echo "文本文件"
        ;;
    *.sh)
        echo "Shell脚本"
        ;;
    *.jpg|*.png|*.gif)
        echo "图片文件"
        ;;
    *)
        echo "未知文件类型"
        ;;
esac

# 系统判断
case $(uname -s) in
    Linux)
        echo "Linux系统"
        ;;
    Darwin)
        echo "macOS系统"
        ;;
    CYGWIN*|MINGW*)
        echo "Windows系统"
        ;;
    *)
        echo "未知系统"
        ;;
esac

# 服务管理
action="start"

case $action in
    start|restart)
        echo "启动服务"
        ;;
    stop)
        echo "停止服务"
        ;;
    status)
        echo "查看状态"
        ;;
    *)
        echo "用法:$0 {start|stop|restart|status}"
        ;;
esac

1.3.3 实战案例

服务管理脚本

#!/bin/bash
# 服务管理脚本

SERVICE="nginx"

case "$1" in
    start)
        echo "启动 $SERVICE..."
        systemctl start $SERVICE
        ;;
    stop)
        echo "停止 $SERVICE..."
        systemctl stop $SERVICE
        ;;
    restart)
        echo "重启 $SERVICE..."
        systemctl restart $SERVICE
        ;;
    status)
        systemctl status $SERVICE
        ;;
    *)
        echo "用法:$0 {start|stop|restart|status}"
        exit 1
        ;;
esac

用户输入验证

#!/bin/bash

read -p "请输入yes或no:" answer

case $answer in
    y|Y|yes|YES|Yes)
        echo "您选择了YES"
        ;;
    n|N|no|NO|No)
        echo "您选择了NO"
        ;;
    *)
        echo "无效输入"
        ;;
esac

二、循环结构

2.1 for循环

2.1.1 列表循环

语法格式

for 变量 in 列表; do
    命令
done

示例

#!/bin/bash

# 遍历列表
for i in 1 2 3 4 5; do
    echo "数字:$i"
done

# 遍历字符串
for name in "东巴文" "张三" "李四"; do
    echo "姓名:$name"
done

# 遍历文件
for file in *.txt; do
    echo "文件:$file"
done

# 遍历目录
for dir in /home/*; do
    if [ -d "$dir" ]; then
        echo "目录:$dir"
    fi
done

# 遍历命令输出
for user in $(cat /etc/passwd | cut -d: -f1); do
    echo "用户:$user"
done

# 遍历数组
arr=("apple" "banana" "orange")
for fruit in "${arr[@]}"; do
    echo "水果:$fruit"
done

2.1.2 C风格for循环

语法格式

for (( 初始化; 条件; 更新 )); do
    命令
done

示例

#!/bin/bash

# 基本循环
for (( i=1; i<=5; i++ )); do
    echo "计数:$i"
done

# 倒序循环
for (( i=10; i>=1; i-- )); do
    echo "倒计时:$i"
done

# 步长循环
for (( i=0; i<=10; i+=2 )); do
    echo "偶数:$i"
done

# 嵌套循环
for (( i=1; i<=3; i++ )); do
    for (( j=1; j<=3; j++ )); do
        echo "i=$i, j=$j"
    done
done

# 使用变量
start=1
end=5
for (( i=$start; i<=$end; i++ )); do
    echo "数字:$i"
done

东巴文理解:C风格for循环更灵活,适合数值迭代。

2.1.3 实战案例

批量创建文件

#!/bin/bash

# 创建100个文件
for i in {1..100}; do
    filename="file_${i}.txt"
    touch "$filename"
    echo "创建文件:$filename"
done

echo "批量创建完成"

批量重命名

#!/bin/bash

# 重命名所有.txt文件为.bak
for file in *.txt; do
    if [ -f "$file" ]; then
        newname="${file%.txt}.bak"
        mv "$file" "$newname"
        echo "重命名:$file -> $newname"
    fi
done

批量压缩

#!/bin/bash

# 压缩所有目录
for dir in */; do
    if [ -d "$dir" ]; then
        dirname=${dir%/}
        tar -czf "${dirname}.tar.gz" "$dirname"
        echo "压缩:$dirname"
    fi
done

2.2 while循环

2.2.1 基本语法

语法格式

while [ 条件 ]; do
    命令
done

示例

#!/bin/bash

# 基本计数
count=1
while [ $count -le 5 ]; do
    echo "计数:$count"
    ((count++))
done

# 读取文件
while read line; do
    echo "行内容:$line"
done < /etc/passwd

# 读取文件并分割字段
while IFS=: read -r username password uid gid info home shell; do
    echo "用户:$username, UID:$uid"
done < /etc/passwd

# 无限循环
while true; do
    echo "按Ctrl+C退出"
    sleep 1
done

# 用户输入循环
while true; do
    read -p "请输入数字(q退出):" input
    if [ "$input" = "q" ]; then
        break
    fi
    echo "您输入的是:$input"
done

2.2.2 实战案例

监控脚本

#!/bin/bash

# 监控CPU使用率
threshold=80

while true; do
    cpu_usage=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print 100 - $1}')
    
    if (( $(echo "$cpu_usage > $threshold" | bc -l) )); then
        echo "[警告] CPU使用率:${cpu_usage}%"
        # 发送告警邮件
        # echo "CPU使用率过高" | mail -s "CPU告警" admin@example.com
    fi
    
    sleep 5
done

倒计时脚本

#!/bin/bash

# 倒计时
seconds=10

while [ $seconds -gt 0 ]; do
    echo -ne "倒计时:$seconds秒\r"
    sleep 1
    ((seconds--))
done

echo -e "\n时间到!"

进度条

#!/bin/bash

# 进度条
total=100
current=0

while [ $current -le $total ]; do
    percent=$((current * 100 / total))
    bar=$(printf "%-${percent}s" "#" | tr ' ' '#')
    printf "\r[%-50s] %d%%" "$bar" "$percent"
    sleep 0.1
    ((current++))
done

echo -e "\n完成!"

2.3 until循环

2.3.1 基本语法

语法格式

until [ 条件 ]; do
    命令
done

示例

#!/bin/bash

# until循环(条件为假时执行)
count=1
until [ $count -gt 5 ]; do
    echo "计数:$count"
    ((count++))
done

# 等待文件创建
file="/tmp/test.txt"
until [ -f "$file" ]; do
    echo "等待文件创建..."
    sleep 1
done

echo "文件已创建"

# 等待服务启动
until systemctl is-active --quiet nginx; do
    echo "等待nginx启动..."
    sleep 2
done

echo "nginx已启动"

东巴文理解until循环与while相反,条件为假时执行循环。

2.3.2 实战案例

等待网络连接

#!/bin/bash

# 等待网络连接
until ping -c 1 8.8.8.8 &> /dev/null; do
    echo "等待网络连接..."
    sleep 2
done

echo "网络已连接"

等待进程结束

#!/bin/bash

# 等待进程结束
pid=12345

until ! kill -0 $pid 2>/dev/null; do
    echo "等待进程 $pid 结束..."
    sleep 1
done

echo "进程已结束"

2.4 select循环

2.4.1 基本语法

语法格式

select 变量 in 列表; do
    命令
done

示例

#!/bin/bash

# 菜单选择
PS3="请选择操作系统:"  # 设置提示符

select os in "Ubuntu" "CentOS" "Debian" "Arch" "退出"; do
    case $os in
        "Ubuntu")
            echo "您选择了Ubuntu"
            ;;
        "CentOS")
            echo "您选择了CentOS"
            ;;
        "Debian")
            echo "您选择了Debian"
            ;;
        "Arch")
            echo "您选择了Arch Linux"
            ;;
        "退出")
            break
            ;;
        *)
            echo "无效选项"
            ;;
    esac
done

东巴文理解select循环自动生成菜单,适合交互式选择。

2.4.2 实战案例

服务管理菜单

#!/bin/bash

# 服务管理菜单
PS3="请选择操作:"

select action in "启动服务" "停止服务" "重启服务" "查看状态" "退出"; do
    case $action in
        "启动服务")
            systemctl start nginx
            echo "nginx已启动"
            ;;
        "停止服务")
            systemctl stop nginx
            echo "nginx已停止"
            ;;
        "重启服务")
            systemctl restart nginx
            echo "nginx已重启"
            ;;
        "查看状态")
            systemctl status nginx
            ;;
        "退出")
            break
            ;;
        *)
            echo "无效选项"
            ;;
    esac
done

三、循环控制

3.1 break语句

3.1.1 基本用法

break:跳出整个循环

#!/bin/bash

# break示例
for i in {1..10}; do
    if [ $i -eq 5 ]; then
        echo "遇到5,跳出循环"
        break
    fi
    echo "数字:$i"
done

echo "循环结束"

# 输出:
# 数字:1
# 数字:2
# 数字:3
# 数字:4
# 遇到5,跳出循环
# 循环结束

3.1.2 跳出多层循环

使用break n跳出n层循环

#!/bin/bash

# 跳出多层循环
for i in {1..3}; do
    for j in {1..3}; do
        if [ $i -eq 2 ] && [ $j -eq 2 ]; then
            echo "跳出所有循环"
            break 2  # 跳出2层循环
        fi
        echo "i=$i, j=$j"
    done
done

echo "所有循环结束"

东巴文理解break n可以跳出n层嵌套循环。

3.2 continue语句

3.2.1 基本用法

continue:跳过本次循环,继续下一次

#!/bin/bash

# continue示例
for i in {1..10}; do
    if [ $((i % 2)) -eq 0 ]; then
        continue  # 跳过偶数
    fi
    echo "奇数:$i"
done

# 输出:
# 奇数:1
# 奇数:3
# 奇数:5
# 奇数:7
# 奇数:9

3.2.2 跳过多层循环

使用continue n跳过n层循环的本次迭代

#!/bin/bash

# 跳过多层循环
for i in {1..3}; do
    for j in {1..3}; do
        if [ $j -eq 2 ]; then
            continue 2  # 跳过外层循环的本次迭代
        fi
        echo "i=$i, j=$j"
    done
done

# 输出:
# i=1, j=1
# i=2, j=1
# i=3, j=1

3.3 exit语句

3.3.1 基本用法

exit:退出整个脚本

#!/bin/bash

# exit示例
for i in {1..10}; do
    if [ $i -eq 5 ]; then
        echo "遇到5,退出脚本"
        exit 1  # 退出脚本,返回状态码1
    fi
    echo "数字:$i"
done

echo "这行不会执行"

# 输出:
# 数字:1
# 数字:2
# 数字:3
# 数字:4
# 遇到5,退出脚本

3.3.2 错误处理

使用exit进行错误处理

#!/bin/bash

# 检查文件是否存在
file="/etc/passwd"

if [ ! -f "$file" ]; then
    echo "错误:文件 $file 不存在"
    exit 1
fi

# 检查文件是否可读
if [ ! -r "$file" ]; then
    echo "错误:文件 $file 不可读"
    exit 2
fi

# 检查命令是否存在
if ! command -v grep &> /dev/null; then
    echo "错误:grep命令未找到"
    exit 3
fi

echo "所有检查通过"
exit 0

东巴文最佳实践:使用不同的退出码表示不同的错误类型。

3.4 return语句

3.4.1 函数返回值

return:从函数返回

#!/bin/bash

# 定义函数
function check_file() {
    local file="$1"
    
    if [ ! -f "$file" ]; then
        return 1  # 文件不存在
    fi
    
    if [ ! -r "$file" ]; then
        return 2  # 文件不可读
    fi
    
    return 0  # 成功
}

# 调用函数
check_file "/etc/passwd"

case $? in
    0)
        echo "文件检查通过"
        ;;
    1)
        echo "文件不存在"
        ;;
    2)
        echo "文件不可读"
        ;;
esac

东巴文理解return只能在函数中使用,用于返回函数状态。


四、函数

4.1 函数定义

4.1.1 基本语法

方式一:使用function关键字

function 函数名() {
    命令
}

方式二:省略function关键字

函数名() {
    命令
}

示例

#!/bin/bash

# 方式一
function hello() {
    echo "Hello, World!"
}

# 方式二
bye() {
    echo "Goodbye!"
}

# 调用函数
hello
bye

东巴文推荐:使用方式一,更清晰。

4.1.2 函数参数

函数参数传递

#!/bin/bash

# 定义带参数的函数
function greet() {
    local name="$1"
    local age="$2"
    
    echo "姓名:$name"
    echo "年龄:$age"
}

# 调用函数并传递参数
greet "东巴文" 25

# 输出:
# 姓名:东巴文
# 年龄:25

参数处理

#!/bin/bash

# 参数处理函数
function show_params() {
    echo "函数名:$0"
    echo "第一个参数:$1"
    echo "第二个参数:$2"
    echo "参数个数:$#"
    echo "所有参数:$@"
    
    # 遍历所有参数
    for param in "$@"; do
        echo "参数:$param"
    done
}

# 调用函数
show_params a b c d

东巴文提示:函数内的$1$2等表示函数参数,不是脚本参数。

4.2 函数返回值

4.2.1 使用return

return返回状态码

#!/bin/bash

# 检查文件是否存在
function file_exists() {
    local file="$1"
    
    if [ -f "$file" ]; then
        return 0  # 存在
    else
        return 1  # 不存在
    fi
}

# 调用函数
if file_exists "/etc/passwd"; then
    echo "文件存在"
else
    echo "文件不存在"
fi

4.2.2 使用echo

echo返回字符串

#!/bin/bash

# 获取文件大小
function get_file_size() {
    local file="$1"
    
    if [ -f "$file" ]; then
        local size=$(du -h "$file" | cut -f1)
        echo "$size"
    else
        echo "0"
    fi
}

# 调用函数并获取返回值
size=$(get_file_size "/etc/passwd")
echo "文件大小:$size"

东巴文理解return返回状态码,echo返回字符串。

4.3 局部变量

4.3.1 local关键字

使用local定义局部变量

#!/bin/bash

# 全局变量
global_var="全局变量"

function test_local() {
    # 局部变量
    local local_var="局部变量"
    
    echo "函数内 - global_var: $global_var"
    echo "函数内 - local_var: $local_var"
}

# 调用函数
test_local

echo "函数外 - global_var: $global_var"
echo "函数外 - local_var: $local_var"  # 空,因为局部变量在函数外不可见

# 输出:
# 函数内 - global_var: 全局变量
# 函数内 - local_var: 局部变量
# 函数外 - global_var: 全局变量
# 函数外 - local_var:

东巴文最佳实践:函数内的变量尽量使用local,避免污染全局命名空间。

4.4 递归函数

4.4.1 递归示例

计算阶乘

#!/bin/bash

# 计算阶乘
function factorial() {
    local n=$1
    
    if [ $n -le 1 ]; then
        echo 1
    else
        local prev=$(factorial $((n - 1)))
        echo $((n * prev))
    fi
}

# 调用函数
result=$(factorial 5)
echo "5! = $result"

# 输出:5! = 120

斐波那契数列

#!/bin/bash

# 斐波那契数列
function fibonacci() {
    local n=$1
    
    if [ $n -le 1 ]; then
        echo $n
    else
        local a=$(fibonacci $((n - 1)))
        local b=$(fibonacci $((n - 2)))
        echo $((a + b))
    fi
}

# 调用函数
for i in {0..10}; do
    result=$(fibonacci $i)
    echo "fib($i) = $result"
done

东巴文提示:递归函数要注意终止条件,避免无限递归。

4.5 函数库

4.5.1 创建函数库

创建函数库文件

#!/bin/bash
# 函数库:utils.sh

# 日志函数
function log_info() {
    echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') $1"
}

function log_error() {
    echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') $1" >&2
}

# 检查文件
function check_file() {
    local file="$1"
    
    if [ ! -f "$file" ]; then
        log_error "文件不存在:$file"
        return 1
    fi
    
    if [ ! -r "$file" ]; then
        log_error "文件不可读:$file"
        return 2
    fi
    
    log_info "文件检查通过:$file"
    return 0
}

# 备份文件
function backup_file() {
    local file="$1"
    local backup_dir="${2:-/backup}"
    local backup_file="${backup_dir}/$(basename $file).$(date +%Y%m%d_%H%M%S)"
    
    if [ ! -d "$backup_dir" ]; then
        mkdir -p "$backup_dir"
    fi
    
    cp "$file" "$backup_file"
    log_info "备份完成:$backup_file"
}

4.5.2 使用函数库

在脚本中引用函数库

#!/bin/bash

# 引入函数库
source ./utils.sh

# 或使用点命令
. ./utils.sh

# 使用函数库中的函数
log_info "脚本开始执行"

check_file "/etc/passwd"
if [ $? -eq 0 ]; then
    backup_file "/etc/passwd"
fi

log_info "脚本执行完成"

东巴文最佳实践:将常用函数封装成库,提高代码复用性。


五、实战案例

5.1 系统监控脚本

#!/bin/bash
#===============================================================================
# 脚本名称:monitor.sh
# 脚本功能:系统监控脚本
# 作者:东巴文
# 创建日期:2024-01-01
#===============================================================================

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

# 阈值设置
CPU_THRESHOLD=80
MEM_THRESHOLD=80
DISK_THRESHOLD=80

# 日志函数
function log() {
    echo -e "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}

# 检查CPU使用率
function check_cpu() {
    local cpu_usage=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print 100 - $1}')
    
    if (( $(echo "$cpu_usage > $CPU_THRESHOLD" | bc -l) )); then
        log "${RED}[警告] CPU使用率:${cpu_usage}%${NC}"
        return 1
    else
        log "${GREEN}[正常] CPU使用率:${cpu_usage}%${NC}"
        return 0
    fi
}

# 检查内存使用率
function check_memory() {
    local mem_usage=$(free | awk '/Mem/{printf("%.1f"), $3/$2*100}')
    
    if (( $(echo "$mem_usage > $MEM_THRESHOLD" | bc -l) )); then
        log "${RED}[警告] 内存使用率:${mem_usage}%${NC}"
        return 1
    else
        log "${GREEN}[正常] 内存使用率:${mem_usage}%${NC}"
        return 0
    fi
}

# 检查磁盘使用率
function check_disk() {
    local disk_usage=$(df -h / | awk 'NR==2{print $5}' | sed 's/%//')
    
    if [ $disk_usage -gt $DISK_THRESHOLD ]; then
        log "${RED}[警告] 磁盘使用率:${disk_usage}%${NC}"
        return 1
    else
        log "${GREEN}[正常] 磁盘使用率:${disk_usage}%${NC}"
        return 0
    fi
}

# 检查服务状态
function check_service() {
    local service="$1"
    
    if systemctl is-active --quiet "$service"; then
        log "${GREEN}[正常] 服务 $service 正在运行${NC}"
        return 0
    else
        log "${RED}[警告] 服务 $service 未运行${NC}"
        return 1
    fi
}

# 主函数
function main() {
    log "开始系统监控..."
    
    # 检查CPU
    check_cpu
    
    # 检查内存
    check_memory
    
    # 检查磁盘
    check_disk
    
    # 检查关键服务
    check_service "nginx"
    check_service "mysql"
    
    log "系统监控完成"
}

# 执行主函数
main

5.2 批量处理脚本

#!/bin/bash
#===============================================================================
# 脚本名称:batch_process.sh
# 脚本功能:批量处理文件
# 作者:东巴文
# 创建日期:2024-01-01
#===============================================================================

# 颜色定义
GREEN='\033[0;32m'
NC='\033[0m'

# 统计变量
total_files=0
success_count=0
fail_count=0

# 日志函数
function log() {
    echo -e "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}

# 处理单个文件
function process_file() {
    local file="$1"
    
    # 检查文件是否存在
    if [ ! -f "$file" ]; then
        log "文件不存在:$file"
        return 1
    fi
    
    # 处理文件(示例:统计行数)
    local lines=$(wc -l < "$file")
    log "处理文件:$file (${lines}行)"
    
    return 0
}

# 批量处理
function batch_process() {
    local dir="$1"
    local pattern="$2"
    
    log "开始批量处理..."
    log "目录:$dir"
    log "模式:$pattern"
    
    # 遍历文件
    for file in "$dir"/$pattern; do
        if [ -f "$file" ]; then
            ((total_files++))
            
            if process_file "$file"; then
                ((success_count++))
                echo -e "${GREEN}[成功]${NC} $file"
            else
                ((fail_count++))
                echo -e "${RED}[失败]${NC} $file"
            fi
        fi
    done
    
    # 显示统计
    echo
    log "处理完成"
    echo "总文件数:$total_files"
    echo "成功:$success_count"
    echo "失败:$fail_count"
}

# 主函数
function main() {
    local target_dir="${1:-.}"
    local file_pattern="${2:-*.txt}"
    
    batch_process "$target_dir" "$file_pattern"
}

# 执行主函数
main "$@"

5.3 用户管理脚本

#!/bin/bash
#===============================================================================
# 脚本名称:user_manager.sh
# 脚本功能:用户管理脚本
# 作者:东巴文
# 创建日期:2024-01-01
#===============================================================================

# 检查root权限
if [ "$EUID" -ne 0 ]; then
    echo "请使用root用户执行此脚本"
    exit 1
fi

# 颜色定义
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m'

# 创建用户
function create_user() {
    local username="$1"
    local password="$2"
    
    # 检查用户是否存在
    if id "$username" &>/dev/null; then
        echo -e "${RED}用户 $username 已存在${NC}"
        return 1
    fi
    
    # 创建用户
    useradd -m -s /bin/bash "$username"
    
    # 设置密码
    echo "$username:$password" | chpasswd
    
    # 设置首次登录修改密码
    chage -d 0 "$username"
    
    echo -e "${GREEN}用户 $username 创建成功${NC}"
    return 0
}

# 删除用户
function delete_user() {
    local username="$1"
    
    # 检查用户是否存在
    if ! id "$username" &>/dev/null; then
        echo -e "${RED}用户 $username 不存在${NC}"
        return 1
    fi
    
    # 删除用户
    userdel -r "$username"
    
    echo -e "${GREEN}用户 $username 删除成功${NC}"
    return 0
}

# 修改密码
function change_password() {
    local username="$1"
    local password="$2"
    
    # 检查用户是否存在
    if ! id "$username" &>/dev/null; then
        echo -e "${RED}用户 $username 不存在${NC}"
        return 1
    fi
    
    # 修改密码
    echo "$username:$password" | chpasswd
    
    echo -e "${GREEN}用户 $username 密码修改成功${NC}"
    return 0
}

# 列出用户
function list_users() {
    echo "系统用户列表:"
    echo "----------------------------------------"
    printf "%-20s %-10s %-10s\n" "用户名" "UID" "Shell"
    echo "----------------------------------------"
    
    while IFS=: read -r username _ uid _ _ _ shell; do
        if [ "$uid" -ge 1000 ]; then
            printf "%-20s %-10s %-10s\n" "$username" "$uid" "$(basename $shell)"
        fi
    done < /etc/passwd
}

# 主菜单
function show_menu() {
    clear
    echo "================================"
    echo "    用户管理脚本"
    echo "================================"
    echo "1. 创建用户"
    echo "2. 删除用户"
    echo "3. 修改密码"
    echo "4. 列出用户"
    echo "5. 退出"
    echo "================================"
}

# 主函数
function main() {
    while true; do
        show_menu
        read -p "请选择操作(1-5):" choice
        
        case $choice in
            1)
                read -p "请输入用户名:" username
                read -s -p "请输入密码:" password
                echo
                create_user "$username" "$password"
                ;;
            2)
                read -p "请输入用户名:" username
                delete_user "$username"
                ;;
            3)
                read -p "请输入用户名:" username
                read -s -p "请输入新密码:" password
                echo
                change_password "$username" "$password"
                ;;
            4)
                list_users
                ;;
            5)
                echo "退出系统"
                exit 0
                ;;
            *)
                echo -e "${RED}无效选项${NC}"
                ;;
        esac
        
        echo
        read -p "按回车继续..."
    done
}

# 执行主函数
main

六、本章小结

6.1 核心要点

✅ 掌握条件判断语句 ✅ 熟练使用循环结构 ✅ 理解循环控制语句 ✅ 学会函数定义与使用

6.2 验证清单

完成本章学习后,请确认您能够:

  • 使用if/case进行条件判断
  • 使用for/while/until循环
  • 使用break/continue控制循环
  • 定义和调用函数

东巴文(db-w.cn) - 让Linux学习更简单