Bash Shell 脚本编程完整教程

Bash(Bourne Again Shell)是 Linux 系统上最常用的命令行解释器和脚本语言。掌握 Bash 脚本编程可以帮助你在搬瓦工 VPS 上实现服务器管理的自动化,从批量文件处理到系统监控、定时备份,几乎所有运维任务都能通过 Bash 脚本高效完成。本文将从基础语法讲起,系统讲解变量、条件判断、循环、函数、数组等核心知识点。

一、第一个 Bash 脚本

创建一个名为 hello.sh 的脚本文件:

#!/bin/bash
# 这是一个简单的 Bash 脚本
echo "Hello, 搬瓦工 VPS!"
echo "当前时间: $(date)"
echo "系统负载: $(uptime)"

第一行 #!/bin/bash 称为 Shebang,告诉系统使用 Bash 来解释执行这个脚本。赋予执行权限后即可运行:

chmod +x hello.sh
./hello.sh

二、变量与数据类型

2.1 变量定义与使用

Bash 中定义变量时等号两边不能有空格,引用变量使用 $ 符号:

#!/bin/bash
# 变量定义
NAME="搬瓦工"
PORT=22
DATA_DIR="/var/data"

# 使用变量
echo "服务器名称: $NAME"
echo "SSH 端口: ${PORT}"
echo "数据目录: ${DATA_DIR}"

# 只读变量
readonly SERVER_IP="192.168.1.1"

# 删除变量
unset PORT

2.2 特殊变量

Bash 提供了多种特殊变量用于脚本编程:

#!/bin/bash
echo "脚本名称: $0"
echo "第一个参数: $1"
echo "第二个参数: $2"
echo "所有参数: $@"
echo "参数个数: $#"
echo "上条命令返回值: $?"
echo "当前进程 PID: $$"
echo "后台进程 PID: $!"

2.3 字符串操作

Bash 中字符串操作非常灵活,支持拼接、截取、替换等功能:

#!/bin/bash
STR="Hello World Bash Scripting"

# 字符串长度
echo "长度: ${#STR}"

# 子串截取(从位置 6 开始,取 5 个字符)
echo "截取: ${STR:6:5}"

# 字符串替换
echo "替换: ${STR/World/Linux}"

# 删除前缀(最短匹配)
FILE="/var/log/syslog.log"
echo "文件名: ${FILE##*/}"

# 删除后缀(最短匹配)
echo "目录: ${FILE%/*}"

# 默认值
echo "未设置变量: ${UNDEFINED:-默认值}"

三、条件判断

3.1 if-else 语句

#!/bin/bash
DISK_USAGE=$(df / | tail -1 | awk '{print $5}' | tr -d '%')

if [ "$DISK_USAGE" -gt 90 ]; then
    echo "警告: 磁盘使用率超过 90%,当前 ${DISK_USAGE}%"
elif [ "$DISK_USAGE" -gt 70 ]; then
    echo "提示: 磁盘使用率较高,当前 ${DISK_USAGE}%"
else
    echo "正常: 磁盘使用率 ${DISK_USAGE}%"
fi

3.2 常用测试条件

文件测试和数值比较是脚本中最常用的条件判断:

#!/bin/bash
# 文件测试
FILE="/etc/nginx/nginx.conf"
if [ -f "$FILE" ]; then
    echo "配置文件存在"
fi

if [ -d "/var/log" ]; then
    echo "日志目录存在"
fi

if [ -r "$FILE" ]; then
    echo "文件可读"
fi

# 数值比较:-eq 等于, -ne 不等于, -gt 大于, -lt 小于, -ge 大于等于, -le 小于等于
A=10
B=20
if [ "$A" -lt "$B" ]; then
    echo "$A 小于 $B"
fi

# 字符串比较
STR1="hello"
STR2="world"
if [ "$STR1" != "$STR2" ]; then
    echo "字符串不相等"
fi

# 使用 [[ ]] 支持正则匹配
if [[ "$STR1" =~ ^hel ]]; then
    echo "匹配成功"
fi

3.3 case 语句

#!/bin/bash
read -p "请选择操作 (start/stop/restart): " ACTION

case "$ACTION" in
    start)
        echo "启动服务..."
        systemctl start nginx
        ;;
    stop)
        echo "停止服务..."
        systemctl stop nginx
        ;;
    restart)
        echo "重启服务..."
        systemctl restart nginx
        ;;
    *)
        echo "无效操作: $ACTION"
        echo "用法: start|stop|restart"
        exit 1
        ;;
esac

四、循环结构

4.1 for 循环

#!/bin/bash
# 遍历列表
for SERVER in web01 web02 web03 db01; do
    echo "正在检查: $SERVER"
    ping -c 1 "$SERVER" &>/dev/null && echo "  在线" || echo "  离线"
done

# C 风格 for 循环
for ((i=1; i<=10; i++)); do
    echo "第 $i 次循环"
done

# 遍历文件
for FILE in /var/log/*.log; do
    SIZE=$(du -sh "$FILE" | awk '{print $1}')
    echo "$FILE: $SIZE"
done

4.2 while 循环

#!/bin/bash
# 监控系统负载
while true; do
    LOAD=$(cat /proc/loadavg | awk '{print $1}')
    echo "$(date '+%Y-%m-%d %H:%M:%S') 负载: $LOAD"

    if (( $(echo "$LOAD > 5.0" | bc -l) )); then
        echo "警告: 系统负载过高!"
    fi

    sleep 60
done

4.3 逐行读取文件

#!/bin/bash
# 读取服务器列表并逐个检查
while IFS= read -r LINE; do
    IP=$(echo "$LINE" | awk '{print $1}')
    NAME=$(echo "$LINE" | awk '{print $2}')
    echo "检查服务器 $NAME ($IP)..."
    ssh -o ConnectTimeout=5 "$IP" uptime 2>/dev/null || echo "  连接失败"
done < server_list.txt

五、函数

#!/bin/bash
# 定义函数
log_message() {
    local LEVEL="$1"
    local MSG="$2"
    local TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
    echo "[$TIMESTAMP] [$LEVEL] $MSG" | tee -a /var/log/myscript.log
}

# 带返回值的函数
check_service() {
    local SERVICE="$1"
    if systemctl is-active --quiet "$SERVICE"; then
        return 0
    else
        return 1
    fi
}

# 调用函数
log_message "INFO" "脚本开始执行"

SERVICES="nginx mysql redis"
for SVC in $SERVICES; do
    if check_service "$SVC"; then
        log_message "INFO" "$SVC 运行正常"
    else
        log_message "ERROR" "$SVC 未运行,尝试启动..."
        systemctl start "$SVC"
    fi
done

log_message "INFO" "脚本执行完毕"

六、数组

#!/bin/bash
# 索引数组
SERVERS=("web01" "web02" "web03" "db01" "db02")

echo "第一个: ${SERVERS[0]}"
echo "全部: ${SERVERS[@]}"
echo "数量: ${#SERVERS[@]}"

# 遍历数组
for SVR in "${SERVERS[@]}"; do
    echo "服务器: $SVR"
done

# 关联数组(Bash 4+)
declare -A CONFIG
CONFIG[host]="127.0.0.1"
CONFIG[port]="3306"
CONFIG[user]="root"
CONFIG[db]="myapp"

for KEY in "${!CONFIG[@]}"; do
    echo "$KEY = ${CONFIG[$KEY]}"
done

七、实用脚本示例:自动备份

以下是一个完整的自动备份脚本,适合在搬瓦工 VPS 上定时运行:

#!/bin/bash
# VPS 自动备份脚本
set -euo pipefail

BACKUP_DIR="/root/backups"
DATE=$(date '+%Y%m%d_%H%M%S')
KEEP_DAYS=7
LOG_FILE="/var/log/backup.log"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

mkdir -p "$BACKUP_DIR"
log "开始备份..."

# 备份网站文件
if [ -d "/var/www" ]; then
    tar czf "${BACKUP_DIR}/www_${DATE}.tar.gz" /var/www/
    log "网站文件备份完成"
fi

# 备份数据库
if command -v mysqldump &>/dev/null; then
    mysqldump --all-databases | gzip > "${BACKUP_DIR}/mysql_${DATE}.sql.gz"
    log "MySQL 数据库备份完成"
fi

# 备份配置文件
tar czf "${BACKUP_DIR}/config_${DATE}.tar.gz" \
    /etc/nginx/ /etc/mysql/ /etc/redis/ 2>/dev/null || true
log "配置文件备份完成"

# 清理旧备份
find "$BACKUP_DIR" -type f -mtime +"$KEEP_DAYS" -delete
log "已清理 ${KEEP_DAYS} 天前的旧备份"

# 统计备份大小
TOTAL_SIZE=$(du -sh "$BACKUP_DIR" | awk '{print $1}')
log "备份完成,总大小: $TOTAL_SIZE"

将脚本添加到 crontab 实现每日自动执行:

crontab -e
# 添加以下行,每天凌晨 3 点执行备份
0 3 * * * /root/scripts/backup.sh

八、调试技巧

# 使用 -x 选项显示每条命令的执行过程
bash -x myscript.sh

# 在脚本中局部开启调试
set -x   # 开启
# ... 需要调试的代码 ...
set +x   # 关闭

# 使用 trap 捕获错误
trap 'echo "错误发生在第 $LINENO 行"; exit 1' ERR

# 严格模式(推荐在生产脚本中使用)
set -euo pipefail
# -e: 命令失败时立即退出
# -u: 使用未定义变量时报错
# -o pipefail: 管道中任何命令失败则整个管道失败

总结

Bash 脚本编程是 Linux 服务器管理的基础技能。通过本文介绍的变量、条件判断、循环、函数和数组等知识,你可以在搬瓦工 VPS 上编写各种自动化脚本,提升运维效率。建议结合 AWK 与 Sed 文本处理Grep 正则表达式 教程一起学习,掌握更强大的文本处理能力。选购搬瓦工 VPS 请参考全部方案,购买时使用优惠码 NODESEEK2026 可享受 6.77% 的循环折扣。

关于本站

搬瓦工VPS中文网(bwgvps.com)是非官方中文信息站,整理搬瓦工的方案、优惠和教程。我们不销售主机,不提供技术服务。

新手必读
搬瓦工优惠码

NODESEEK2026(优惠 6.77%)

购买时填入即可抵扣。