一、Shell脚本基础
1. 脚本开头
所有Shell脚本首行必须指定解释器,否则默认使用系统默认Shell(通常是bash),常用两种写法:
#!/bin/bash # 最常用,指定bash解释器(推荐)
#!/bin/sh # 兼容式写法,sh是bash的简化版,部分系统中与bash等价
补充:脚本执行前需添加执行权限:chmod +x 脚本名.sh,执行方式:./脚本名.sh 或bash 脚本名.sh(无需执行权限)。
2. 注释语法
Shell脚本中,单行注释用
#(开头加空格更规范),多行注释可使用<<EOF ... EOF包裹(EOF可替换为任意字符)
3. 变量定义与使用
运维脚本中变量用于存储路径、参数、结果等,核心规则:等号两边无空格,使用时加$符号。
bash
# 1. 基础变量定义(字符串、数字均可)
name="nginx"
port=80
path="/var/log/nginx"
# 2. 变量使用($变量名 或 ${变量名},推荐后者,避免歧义)
echo "服务名:$name"
echo "端口:${port}"
echo "日志路径:${path}/error.log"
# 3. 系统环境变量(可直接使用,常用)
echo "当前用户:$USER" # 登录用户
echo "当前目录:$PWD" # 当前工作目录
echo "系统路径:$PATH" # 命令搜索路径
echo "脚本名:$0" # 脚本自身文件名
echo "参数1:$1" # 脚本接收的第一个参数($2-$9对应第2-9个参数)
echo "参数总数:$#" # 脚本接收的参数个数
echo "所有参数:$*" # 所有参数(作为一个整体)
echo "所有参数:$@" # 所有参数(每个参数独立)
4.输入输出
bash
# 1. 输出(echo)
echo "正常输出" # 标准输出到终端
echo "错误信息" 1>&2 # 输出到错误流(终端也能看到,便于日志区分)
echo "不换行输出" | tr -d '\n' # 取消换行
# 2. 输入(read,用于交互获取参数)
read -p "请输入服务名:" service # 提示用户输入,将输入内容赋值给service变量
read -t 10 -p "请输入密码(10秒超时):" pwd # -t 超时时间(秒)
# 3. 重定向(核心,用于日志记录)
echo "脚本执行时间:$(date)" > 1.log # > 覆盖写入日志
echo "执行结果:成功" >> 1.log # >> 追加写入日志
./test.sh > test.log 2>&1 # 所有输出(标准+错误)都写入日志(运维必用)
二、Shell脚本流程控制
运维脚本中最常用的3种流程控制:条件判断、循环、分支,重点掌握语法简洁、不易出错的写法。
1. 条件判断(if-else)
核心用于:判断服务是否运行、文件是否存在、参数是否传入等,语法:if 条件; then ... fi(分号可替换为换行)
bash
# 示例1:判断服务是否运行(以nginx为例)
if ps -ef | grep -v grep | grep -q nginx; then
echo "nginx服务正在运行"
else
echo "nginx服务未运行,正在重启..."
systemctl restart nginx
fi
# 示例2:判断文件是否存在(日志文件)
log_path="/var/log/nginx/error.log"
if [ -f "$log_path" ]; then # -f 判断是否为普通文件
echo "日志文件存在,大小:$(du -sh $log_path)"
elif [ -d "$log_path" ]; then # -d 判断是否为目录
echo "路径是目录,不是日志文件"
else
echo "日志文件不存在,创建中..."
touch $log_path
fi
# 常用判断条件(必记)
# -f :文件存在且是普通文件
# -d :目录存在
# -e :文件/目录存在(不区分类型)
# -r :文件可读
# -w :文件可写
# -x :文件可执行
# -z :字符串为空
# -n :字符串非空
# $a -eq $b :a等于b(数字比较)
# $a -gt $b :a大于b
# $a -lt $b :a小于b
2. 循环(for、while)
用于批量处理:批量启动服务、批量删除文件、批量检查主机存活等。
bash
# 示例1:for循环(批量处理文件,删除7天前的日志)
log_dir="/var/log/nginx"
for log_file in $(ls $log_dir/*.log); do
# 判断文件是否超过7天
if [ $(find $log_file -mtime +7) ]; then
rm -f $log_file
echo "删除过期日志:$log_file"
fi
done
# 示例2:for循环(批量检查主机存活,ping测试)
for ip in 192.168.1.{1..10}; do # 批量生成IP(1-10)
ping -c 2 -W 1 $ip > /dev/null 2>&1 # ping 2次,超时1秒,不输出结果
if [ $? -eq 0 ]; then # $? 是上一条命令的返回值(0=成功,非0=失败)
echo "$ip 主机存活"
else
echo "$ip 主机宕机"
fi
done
# 示例3:while循环(持续监控服务,异常重启)
while true; do # 无限循环
if ! ps -ef | grep -v grep | grep -q nginx; then
systemctl restart nginx
echo "$(date):nginx异常,已重启" >> /var/log/nginx/monitor.log
fi
sleep 60 # 每隔60秒检查一次
done
3. 分支(case)
用于多条件分支,比如脚本接收不同参数,执行不同操作(如启动、停止、重启服务)。
bash
# 示例:服务管理脚本(接收start/stop/restart参数)
if [ $# -ne 1 ]; then # 判断参数个数是否为1
echo "用法:$0 {start|stop|restart}"
exit 1 # 退出脚本,返回非0状态(表示执行失败)
fi
case $1 in
start)
systemctl start nginx
echo "nginx启动成功"
;; # 分支结束
stop)
systemctl stop nginx
echo "nginx停止成功"
;;
restart)
systemctl restart nginx
echo "nginx重启成功"
;;
*) # 匹配所有未命中的参数
echo "错误:参数错误,仅支持 start|stop|restart"
exit 1
;;
esac
三、运维常用Shell脚本
1. 服务监控脚本(nginx为例,异常重启+日志记录)
bash
#!/bin/bash
# 脚本功能:监控nginx服务,异常自动重启,记录监控日志
# 作者:运维工程师
# 日期:2026-05-09
service="nginx"
log_path="/var/log/monitor_${service}.log"
# 写入脚本启动日志
echo "=====================================" >> $log_path
echo "监控脚本启动时间:$(date +'%Y-%m-%d %H:%M:%S')" >> $log_path
# 循环监控
while true; do
# 判断服务是否运行
if ! ps -ef | grep -v grep | grep -q $service; then
# 服务异常,重启
systemctl restart $service
# 记录重启日志
echo "$(date +'%Y-%m-%d %H:%M:%S'):${service}服务异常,已执行重启" >> $log_path
fi
# 每隔30秒检查一次
sleep 30
done
2. 日志清理脚本(删除指定目录下过期日志,防止磁盘爆满)
bash
#!/bin/bash
# 脚本功能:清理指定目录下7天前的日志文件,支持多目录
# 适用场景:/var/log、应用日志目录等
# 定义需要清理的日志目录(可添加多个)
log_dirs=("/var/log" "/var/log/nginx" "/var/log/mysqld")
# 定义过期时间(7天)
expire_days=7
# 循环清理每个目录
for dir in "${log_dirs[@]}"; do
# 判断目录是否存在
if [ ! -d "$dir" ]; then
echo "$(date):目录 $dir 不存在,跳过" >> /var/log/log_clean.log
continue
fi
# 删除过期日志(仅删除.log和.log-*后缀的文件)
find $dir -name "*.log" -o -name "*.log-*" -mtime +$expire_days -delete
echo "$(date):清理目录 $dir 下 ${expire_days}天前的日志完成" >> /var/log/log_clean.log
done
3. 磁盘空间监控脚本(满了自动报警,邮件/终端提示)
bash
#!/bin/bash
# 脚本功能:监控所有磁盘分区,使用率超过80%报警
# 报警方式:终端提示+日志记录(可扩展邮件报警)
# 定义报警阈值(80%,可根据需求修改)
threshold=80
# 遍历所有磁盘分区(排除tmpfs等虚拟文件系统,避免误监控)
# 核心解析:while read usage mount 用法详解
df -hl | grep -v tmpfs | grep -v squashfs | awk '{print $5,$6}' | while read usage mount; do
# 1. while read usage mount 核心作用:
# 读取管道前一条命令(awk输出)的每一行内容,将每行拆分为两个变量
# usage:接收awk输出的第1列(磁盘使用率,如85%)
# mount:接收awk输出的第2列(磁盘挂载点,如/、/data)
# 相当于"逐行读取、拆分变量",实现批量遍历每个磁盘分区
# 提取使用率数字(去掉%符号,将字符串转为数字,用于比较)
usage_num=${usage%\%}
# 2. 关键注意点(避坑):
# - read 会自动按空格拆分每行内容,若挂载点包含空格(如"/mnt/data 1"),会拆分失败
# - 解决方案:awk输出时用特殊分隔符(如|),read时指定分隔符,示例:
# df -hl | awk '{print $5"|"$6}' | while IFS="|" read usage mount; do
# 判断是否超过阈值(数字比较,需确保usage_num是纯数字)
if [ $usage_num -gt $threshold ]; then
# 终端报警(红色字体突出警告)
echo -e "\033[31m 警告:磁盘分区 $mount 使用率已达 $usage ,超过阈值 $threshold% \033[0m"
# 日志记录(包含时间戳,便于后续排查)
echo "$(date +'%Y-%m-%d %H:%M:%S'):警告:$mount 使用率 $usage ,超过阈值 $threshold%" >> /var/log/disk_monitor.log
fi
done
# 补充拓展:while read 其他常用场景(运维实战)
# 场景1:读取文件内容,逐行处理(如读取配置文件)
# cat /etc/nginx/nginx.conf | while read line; do
# echo "配置行:$line"
# done
# 场景2:批量处理文件路径(结合find命令)
# find /var/log -name "*.log" | while read log_file; do
# du -sh $log_file # 查看每个日志文件大小
# done
4. 批量主机存活检测脚本(ping测试,输出存活状态)
bash
#!/bin/bash
# 脚本功能:批量检测指定IP段的主机存活状态,输出结果到文件
# 定义IP段(可修改为自己的网段)
ip_prefix="192.168.1."
# 定义IP范围(1-20)
start=1
end=20
# 输出结果文件
result_file="/var/log/host_alive.log"
# 清空结果文件(可选)
> $result_file
echo "主机存活检测开始时间:$(date)" >> $result_file
echo "==============================" >> $result_file
for ((i=start; i<=end; i++)); do
ip="${ip_prefix}${i}"
# ping测试(2次,超时1秒)
ping -c 2 -W 1 $ip > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo "$ip :存活" >> $result_file
echo "$ip :存活"
else
echo "$ip :宕机" >> $result_file
echo "$ip :宕机"
fi
done
echo "检测完成时间:$(date)" >> $result_file
四、运维脚本实战技巧(避坑+优化)
1. 脚本避坑要点(重中之重)
-
变量加双引号 :避免变量中包含空格、特殊字符导致报错,例如
[ -f "$log_path" ],而非[ -f $log_path ]。 -
判断返回值 :用
$?判断上一条命令是否执行成功(0=成功),避免脚本"静默失败"。 -
避免使用rm -rf * :批量删除时,务必指定具体后缀或路径,防止误删(例如
rm -rf *.log,而非rm -rf *)。 -
添加日志记录:所有关键操作(重启服务、删除文件)必须写入日志,便于后续排查问题。
-
脚本开头加参数校验:接收参数的脚本,先判断参数个数、参数合法性,避免因参数错误导致脚本执行异常。
-
使用绝对路径 :脚本中所有命令、文件路径均使用绝对路径(例如
/usr/bin/ps、/var/log/nginx),避免环境变量问题导致命令找不到。
2. 脚本优化技巧
-
后台运行脚本 :监控类脚本需后台运行,添加 & 后缀,例如
./monitor.sh&,避免终端关闭后脚本停止。 -
加入开机自启 :将脚本路径添加到
/etc/rc.d/rc.local(CentOS)或/etc/rc.local(Ubuntu),确保服务器重启后脚本自动运行(需给rc.local加执行权限)。 -
简化命令 :使用awk、sed等工具简化命令,例如提取磁盘使用率:
df -hl | awk '{print $5}',避免多步管道操作。 -
添加错误处理 :使用
set -e(脚本遇到错误立即退出),或trap捕获信号,避免脚本继续执行导致更严重问题。
3. 常用辅助命令(脚本中高频使用)
bash
awk # 文本处理,提取字段(如提取IP、使用率)
sed # 文本替换、删除(如修改配置文件)
grep # 过滤关键字(如查找错误日志、进程)
find # 查找文件(如查找过期日志、指定类型文件)
date # 获取时间(如日志时间戳)
systemctl # 管理系统服务(启动、停止、重启)
ps # 查看进程
df/du # 查看磁盘空间
ping # 主机存活检测
curl/wget # 接口、文件下载(如监控接口可用性)
五、常见问题排查(脚本执行报错)
-
脚本无法执行 :未添加执行权限,执行
chmod +x 脚本名.sh;或首行解释器错误(如写错#!/bin/bash)。 -
变量报错:等号两边有空格,或变量未定义就使用,检查变量定义语法。
-
命令找不到:使用了相对路径,改为绝对路径(如将ps改为/usr/bin/ps)。
-
循环报错:for/while循环语法错误,检查do、done是否配对,分号是否正确。
-
权限不足 :脚本执行需要root权限,添加sudo(如
sudo ./脚本名.sh)。
六、进阶拓展(运维进阶必备)
-
函数封装:将重复代码封装为函数(如日志记录、服务检查),简化脚本,提高复用性。
-
邮件报警:在脚本中添加邮件发送功能(使用mailx命令),异常时自动发送报警邮件给运维人员。
-
参数解析:使用getopts命令解析长参数、短参数,让脚本更灵活(如支持--help、--threshold等参数)。
-
脚本调试 :使用
bash -x 脚本名.sh调试脚本,查看每一步执行过程,快速定位错误。 -
批量部署:结合expect脚本,实现无交互批量执行脚本(如批量部署服务、批量修改配置)。
1. Linux Shell函数详解
Shell函数是将一段可重复使用的代码块封装起来,起一个唯一名称,后续通过名称即可调用,核心作用是简化脚本、提高代码复用性、便于维护,尤其适合运维脚本中重复执行的操作(如日志记录、服务状态检查、错误处理等)。以下从语法、调用方式、实战案例、避坑要点全面解析。
1.1 函数基础语法
Shell函数无返回值类型,无需声明参数类型,语法简洁,优先推荐第一种标准写法,兼容性最好。
bash
# 写法1:标准写法(推荐,兼容所有bash版本)
函数名() {
# 函数体:要执行的命令、逻辑
命令1
命令2
# 可选:return 退出状态码(0=成功,非0=失败,默认返回最后一条命令的状态码)
return 0
}
# 写法2:带function关键字(兼容bash,不推荐sh环境)
function 函数名 {
函数体
}
# 写法3:简洁写法(适合简单函数,一行完成)
函数名() { 命令1; 命令2; }
# 示例:最简单的函数(输出提示信息)
hello() {
echo "欢迎使用Shell函数示例"
}
1.2 函数调用方式
函数定义后,直接通过「函数名」调用,可传递参数、接收返回值,核心注意:函数必须先定义,再调用(顺序不可颠倒)。
bash
# 1. 基础调用(无参数、无返回值)
hello() {
echo "欢迎使用Shell函数示例"
}
# 调用函数(直接写函数名)
hello
# 2. 函数传递参数(运维高频)
# 函数内通过$1、$2...$n接收参数,$#接收参数个数,$@接收所有参数
log_record() {
# 参数1:日志级别(info/error/warn),参数2:日志内容
local level=$1 # local关键字:定义局部变量,仅在函数内生效(推荐)
local content=$2
local time=$(date +'%Y-%m-%d %H:%M:%S')
# 写入日志文件
echo "[$time] [$level] $content" >> /var/log/script.log
}
# 调用函数(传递2个参数)
log_record "info" "脚本启动成功"
log_record "error" "nginx服务重启失败"
# 3. 函数接收返回值(3种常用方式)
# 方式1:return返回状态码(仅0-255,适合判断成功/失败)
check_service() {
local service=$1
# 检查服务是否运行
if ps -ef | grep -v grep | grep -q $service; then
return 0 # 成功返回0
else
return 1 # 失败返回1
fi
}
# 调用并判断返回值($?获取上一条命令/函数的返回值)
check_service "nginx"
if [ $? -eq 0 ]; then
echo "nginx服务正常"
else
echo "nginx服务异常"
fi
# 方式2:echo输出返回值(适合返回字符串、数字,最常用)
get_disk_usage() {
local mount=$1
# 提取指定挂载点的使用率(去掉%)
local usage=$(df -hl | grep $mount | awk '{print $5}' | sed 's/%//')
echo $usage # 输出结果,作为返回值
}
# 调用并接收返回值(用变量接收echo输出)
usage=$(get_disk_usage "/")
echo "根目录使用率:$usage%"
# 方式3:全局变量返回(不推荐,易污染全局变量)
global_var=""
set_var() {
global_var="hello" # 直接修改全局变量
}
set_var
echo $global_var # 输出hello
1.3 运维实战函数案例
案例1:日志记录函数(所有脚本通用)
bash
# 函数功能:统一记录日志,包含时间戳、日志级别、日志内容,支持自定义日志路径
log() {
# 参数1:日志级别(info/error/warn/debug)
# 参数2:日志内容
# 参数3:日志路径(可选,默认/var/log/script.log)
local level=$1
local content=$2
local log_path=${3:-/var/log/script.log} # 默认值:无第三个参数时使用默认路径
local time=$(date +'%Y-%m-%d %H:%M:%S')
# 判断日志目录是否存在,不存在则创建
local log_dir=$(dirname $log_path)
if [ ! -d "$log_dir" ]; then
mkdir -p $log_dir
fi
# 写入日志(不同级别可添加颜色,可选)
case $level in
error)
echo -e "[$time] \033[31m[$level]\033[0m $content" >> $log_path
;;
warn)
echo -e "[$time] \033[33m[$level]\033[0m $content" >> $log_path
;;
*)
echo "[$time] [$level] $content" >> $log_path
;;
esac
}
# 调用示例(融入磁盘监控脚本)
threshold=80
df -hl | grep -v tmpfs | grep -v squashfs | awk '{print $5,$6}' | while read usage mount; do
usage_num=${usage%\%}
if [ $usage_num -gt $threshold ]; then
log "error" "磁盘分区$mount使用率达$usage,超过阈值$threshold%"
echo -e "\033[31m 警告:磁盘分区 $mount 使用率已达 $usage ,超过阈值 $threshold% \033[0m"
else
log "info" "磁盘分区$mount使用率$usage,正常"
fi
done
案例2:服务状态检查函数(适配所有systemctl管理的服务)
bash
# 函数功能:检查服务状态,返回状态码(0=运行,1=未运行,2=服务不存在)
check_service_status() {
local service=$1
# 检查服务是否存在
if ! systemctl list-unit-files | grep -q "$service.service"; then
#systemctl list-unit-files:列出系统中所有已注册的服务单元文件(包含所有已安装 / 配置的服务)
#grep -q "$service.service":静默匹配(-q 不输出匹配结果,仅返回状态码),判断服务对应的单元文件是否存在;
#!:取反,即 "如果没有匹配到该服务的单元文件"(说明服务不存在)
log "error" "服务$service不存在"
return 2
fi
# 检查服务是否运行
if systemctl is-active --quiet $service; then
#systemctl is-active:用于检查系统服务的运行状态(active = 运行中,inactive = 未运行);
#--quiet:静默模式,不输出具体状态信息,仅返回状态码(0 = 运行中,非 0 = 未运行);
log "info" "服务$service正在运行"
return 0
else
log "warn" "服务$service未运行"
return 1
fi
}
# 调用示例(批量检查多个服务)
services=("nginx" "mysqld" "sshd")
for service in "${services[@]}"; do
check_service_status $service
case $? in
#$? 是 Linux Shell 的内置变量,代表「上一条命令 / 函数的返回值」
0)
continue # 服务正常,跳过
;;
#continue 解析:跳过当前循环的剩余代码,直接进入下一次循环,即 "服务正常就不做任何操作,继续检查下一个服务"
1)
log "info" "正在重启服务$service"
systemctl restart $service
;;
2)
log "error" "跳过服务$service,服务不存在"
;;
esac
done
案例3:错误处理函数(统一处理脚本异常)
bash
# 函数功能:脚本执行异常时,记录错误日志并退出脚本
error_handler() {
local error_msg=$1
local exit_code=${2:-1} # 默认退出码1
# 记录错误日志
log "error" "脚本执行异常:$error_msg"
# 输出终端提示
echo -e "\033[31m 错误:$error_msg \033[0m"
# 退出脚本
exit $exit_code
}
# 调用示例(脚本开头校验参数)
if [ $# -ne 1 ]; then
error_handler "参数错误,用法:$0 {start|stop|restart}" # 调用错误处理函数
fi
# 调用示例(执行命令失败时触发)
systemctl restart nginx || error_handler "nginx服务重启失败" # || 表示前一条命令失败时执行
${2:-1}:
这是 Shell 的参数默认值语法 ,核心逻辑:"如果函数接收了第二个参数(
$2),就用$2的值;如果没接收第二个参数($2为空),就用默认值1";拆解符号:
$2:代表函数接收的第二个参数 (error_handler函数中,第一个参数$1是错误提示信息,第二个参数$2就是自定义的退出码);:-:固定语法,用于 "判断参数是否为空,为空则使用后面的默认值";1:默认退出码(自定义规则,运维中常用1表示 "默认执行异常",可根据需求修改为其他非 0 数字,比如用2表示 "参数错误"、3表示 "服务异常")。
$#:
- Shell 内置变量,核心作用:获取脚本接收的 "参数总个数" 。
- 举例:执行
./service.sh start(脚本名 + 1 个参数),则$# = 1;执行./service.sh start nginx(2 个参数),则$# = 2;直接执行./service.sh(无参数),则$# = 0。
-ne:
- 条件判断中的 "比较运算符",表示 "不等于"(全称 not equal),仅用于 数字比较(此处比较参数个数,属于数字)。
- 补充常用数字运算符(运维必记):
-eq:等于(equal),如$# -eq 1(参数个数等于 1)-gt:大于(greater than),如$# -gt 1(参数个数大于 1)-lt:小于(less than),如$# -lt 1(参数个数小于 1)
案例4:磁盘空间检查函数(复用性强)
bash
# 函数功能:检查指定挂载点的磁盘使用率,超过阈值则报警
check_disk_usage() {
local mount=$1
local threshold=$2
# 提取使用率(处理异常情况:挂载点不存在)
local usage=$(df -hl | grep -w $mount | awk '{print $5}' | sed 's/%//')
if [ -z "$usage" ]; then
error_handler "挂载点$mount不存在"
fi
# 判断是否超过阈值
if [ $usage -gt $threshold ]; then
log "error" "挂载点$mount使用率$usage%,超过阈值$threshold%"
#调用前面日志函数
echo -e "\033[31m 警告:挂载点$mount使用率$usage%,超过阈值$threshold% \033[0m"
else
log "info" "挂载点$mount使用率$usage%,正常"
fi
}
# 调用示例(批量检查多个挂载点)
mounts=("/" "/data" "/var")
threshold=80
for mount in "${mounts[@]}"; do
check_disk_usage $mount $threshold
done
1.4 函数避坑要点(运维必看)
-
函数顺序:必须先定义函数,再调用函数,否则会报"command not found"错误(脚本从上到下执行)。
-
局部变量 :函数内使用
local定义变量,避免变量污染(局部变量仅在函数内生效,不影响全局变量)。 -
参数传递:函数参数通过1、2...传递,与脚本参数区分开(脚本参数是脚本外部传入,函数参数是调用时传入)。
-
返回值陷阱:return仅能返回0-255的状态码,无法返回字符串或大于255的数字;返回字符串/数字需用echo输出,再用变量接收。
-
函数命名:避免与系统命令重名(如ls、cd、ps等),建议加前缀(如log_、check_),例如log_record而非log。
-
递归调用:Shell函数支持递归(自己调用自己),但需设置终止条件,否则会陷入死循环,耗尽系统资源(运维脚本中极少使用)。
1.5 函数与脚本的结合技巧
-
将常用函数(如日志、错误处理)封装到单独的文件(如func.sh),其他脚本通过
source func.sh引入,实现函数复用(无需重复编写)。 -
复杂脚本中,将不同功能拆分到不同函数(如日志函数、检查函数、执行函数),脚本主逻辑仅负责调用函数,提高可读性和可维护性。
-
函数内可调用其他函数,实现功能嵌套(如错误处理函数调用日志函数,统一记录错误信息)。
2、实战脚本(批量备份+批量执行)
1. 简单批量备份脚本(生产常用)
功能:批量备份指定目录下的文件/文件夹,按日期命名备份包,自动清理7天前的旧备份(避免占用磁盘),适配大多数Linux系统。
bash
#!/bin/bash
# 批量备份脚本:备份指定目录,自动清理旧备份(生产环境简化版,直接修改参数即可使用)
# 核心功能:1. 批量备份多个目录 2. 按日期命名备份包 3. 自动清理过期备份 4. 输出执行结果提示
# 适用场景:日常运维中,对网站目录、配置文件、日志等重要数据的定时/手动备份
# 1. 配置备份参数(可根据实际需求灵活修改,无需改动后续执行逻辑)
# 多个目录用空格分隔,示例:"/home /root",可根据自身运维需求调整
BACKUP_DIR="/data/www /etc/nginx /var/log" # 需备份的目标目录集合
BACKUP_DEST="/backup" # 备份文件存放目录(建议选择磁盘空间充足的分区,避免备份失败)
RETENTION_DAYS=7 # 备份保留天数,超过该天数的备份将自动删除,避免占用过多磁盘
DATE=$(date +%Y%m%d_%H%M%S) # 日期格式(年-月-日_时-分-秒),用于备份包命名,防止不同时间的备份重复
# 2. 检查备份存放目录,不存在则自动创建(核心目的:防止因目录不存在导致备份任务中断)
if [ ! -d "$BACKUP_DEST" ]; then # 条件判断:检测BACKUP_DEST指定的目录是否存在
mkdir -p "$BACKUP_DEST" # 递归创建目录(-p参数可自动创建上级目录,避免手动创建的繁琐)
echo "创建备份目录:$BACKUP_DEST" # 输出提示信息,便于用户确认目录创建状态
fi
# 3. 批量备份核心逻辑:循环遍历每个需备份的目录,逐个执行压缩备份操作
for dir in $BACKUP_DIR; do # 循环遍历:将BACKUP_DIR中的每个目录依次赋值给变量dir,逐个处理
# 先判断当前遍历的目录是否存在,避免因目录不存在导致脚本报错、中断整个备份任务
if [ -d "$dir" ]; then # 条件判断:确认当前目录存在后,再执行备份操作
# 备份包命名规则:目录basename_日期.tar.gz(压缩格式,节省磁盘空间,同时便于区分不同备份)
# basename "$dir":提取目录的基础名称(例:/data/www → www),让备份包名更简洁易识别
BACKUP_NAME=$(basename "$dir")"_$DATE.tar.gz"
# 执行压缩备份操作:tar命令是Linux常用备份工具,参数含义如下
# -z:用gzip压缩备份包,节省磁盘空间;-c:创建新的备份包;-f:指定备份包的文件名及路径
tar -zcf "$BACKUP_DEST/$BACKUP_NAME" "$dir"
# 备份结果校验:判断上一条tar备份命令是否执行成功($? -eq 0 表示执行成功,非0则失败)
if [ $? -eq 0 ]; then
echo "✅ 备份成功:$dir → $BACKUP_DEST/$BACKUP_NAME"
else
echo "❌ 备份失败:$dir(常见原因:权限不足、目录被占用或磁盘空间不足)"
fi
else
# 若目录不存在,输出提示并跳过该目录,不中断整个批量备份任务,保证其他目录正常备份
echo "⚠️ 跳过:目录 $dir 不存在,无需执行备份操作"
fi
done
# 4. 自动清理过期备份:删除超过保留天数的备份文件,释放磁盘空间(避免长期备份占用过多存储)
# find命令用于查找符合条件的文件,各参数含义如下:
# -name "*.tar.gz":仅匹配后缀为.tar.gz的备份文件,避免误删其他文件
# -mtime +$RETENTION_DAYS:匹配修改时间超过RETENTION_DAYS天的文件(+表示"超过")
# -delete:直接删除匹配到的过期备份文件(无需手动确认,修改RETENTION_DAYS时需谨慎)
find "$BACKUP_DEST" -name "*.tar.gz" -mtime +$RETENTION_DAYS -delete
# 修正原脚本笔误(RETENTION → RETENTION_DAYS),确保清理逻辑正确,避免报错
echo "🗑️ 已清理 $RETENTION_DAYS 天前的旧备份,释放磁盘空间"
# 脚本执行完毕提示,便于用户快速确认批量备份任务的整体状态
echo "====== 批量备份任务执行完毕 ======"
使用说明:修改 BACKUP_DIR(要备份的目录)、BACKUP_DEST(备份存放路径)、RETENTION_DAYS(保留天数),添加执行权限后即可运行,可配合crontab设置定时备份(如每天凌晨2点执行)。
2. 简单批量执行脚本(多服务器/多任务)
- 单服务器批量执行命令
bash
#!/bin/bash
# 单服务器批量执行脚本:循环执行多个预设命令,适用于单台服务器的批量运维操作
# 核心功能:批量执行命令、输出每一步执行结果、快速排查失败原因,提升运维效率
# 适用场景:批量重启服务、批量修改文件权限、批量清理日志等重复性运维操作
# 定义需批量执行的命令(采用数组形式,每一行一个命令,可根据运维需求灵活添加、删除或修改)
# 数组的优势:命令管理更清晰,后续通过循环直接调用,无需重复编写执行逻辑
commands=(
"systemctl restart nginx" # 重启nginx服务(适用于web服务器,确保服务正常运行,保障业务可用)
"chmod 755 /data/www/*" # 批量修改文件权限(755权限:所有者可读可写可执行,其他用户可读可执行,兼顾安全与可用)
"find /var/log -name "*.log" -size +100M -delete" # 删除大于100M的日志文件,避免日志占用过多磁盘空间
"df -h" # 查看磁盘使用情况,验证日志删除后的磁盘空间释放效果,确认操作有效性
)
# 批量执行核心逻辑:循环遍历commands数组,逐个执行预设命令,同步输出执行结果
for cmd in "${commands[@]}"; do # "${commands[@]}":遍历数组中的每个命令,依次赋值给变量cmd
echo "📌 正在执行命令:$cmd" # 输出当前执行的命令,便于用户跟踪任务进度,快速定位执行环节
# 直接执行变量cmd对应的命令,同步输出命令执行结果,便于查看详细信息
$cmd
# 执行结果校验:$? 是上一条命令的执行状态码(0表示执行成功,非0表示执行失败)
if [ $? -eq 0 ]; then
echo "✅ 命令执行成功"
else
echo "❌ 命令执行失败(常见原因:权限不足、命令不存在或参数配置错误)"
fi
echo "-------------------------" # 分隔线,将不同命令的执行结果清晰区分,便于后续查看和排查问题
done
# 脚本执行完毕提示,告知用户所有预设命令已执行完成,便于确认批量操作整体状态
echo "====== 批量执行任务完毕 ======"
- 多服务器批量执行命令(免密登录版)
bash
#!/bin/bash
# 多服务器批量执行脚本:基于ssh免密登录,批量在多台服务器执行相同命令(简化版,适用于小型服务器集群)
# 核心功能:通过ssh免密登录,统一在多台服务器执行命令,减少重复操作,提升集群运维效率
# 适用场景:多台服务器批量检查状态、批量重启服务、批量更新配置等集群运维操作
# 1. 配置目标服务器信息(数组形式,格式:用户名@服务器IP,多个服务器用空格分隔)
# 注意事项:用户名需具备对应命令的执行权限(建议使用root用户,避免因权限不足导致命令执行失败)
servers=("root@192.168.1.10" "root@192.168.1.11" "root@192.168.1.12")
# 2. 定义需批量执行的命令(所有服务器统一执行,可根据需求修改)
# 多个命令用&&连接,按顺序执行;示例命令:检查sshd服务状态(确保ssh连接正常)+ 查看磁盘使用情况(检查服务器健康状态)
cmd="systemctl status sshd && df -h"
# 批量执行核心逻辑:循环登录每台目标服务器,执行预设命令,同步输出执行结果
for server in "${servers[@]}"; do # 遍历servers数组,将每个服务器地址依次赋值给变量server
echo "📌 正在连接服务器:$server" # 输出当前连接的服务器信息,便于用户跟踪集群操作进度
# ssh登录服务器并执行命令,输出详细执行结果,参数说明如下:
# -o StrictHostKeyChecking=no:跳过ssh首次连接的密钥确认步骤,避免脚本中断(需提前配置免密登录)
ssh -o StrictHostKeyChecking=no "$server" "$cmd"
# 执行结果校验:判断上一条ssh命令是否执行成功,同步提示用户
if [ $? -eq 0 ]; then
echo "✅ 服务器 $server 命令执行成功"
else
echo "❌ 服务器 $server 命令执行失败(常见原因:免密登录未配置、服务器未开机、IP地址错误或端口不通)"
fi
echo "-------------------------" # 分隔线,将不同服务器的执行结果清晰区分,便于排查单台服务器问题
done
# 脚本执行完毕提示,告知用户所有目标服务器的命令已执行完成,确认集群批量操作整体状态
echo "====== 多服务器批量执行完毕 ======"