尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Linux运维自动化:279个Shell脚本从Dos封禁到备份监控

Linux运维自动化:279个Shell脚本从Dos封禁到备份监控 简介279个开箱即用的Shell脚本2024年新版是一份面向Linux/Unix运维人员、系统管理员与安全爱好者的实用合集属于软件/插件类运维工具覆盖自动化任务、安全防护、日志分析和数据库备份等高频场景。文档详细展示了Dos攻击防范脚本能通过分析Nginx日志自动屏蔽异常IPLinux系统发送告警脚本可在风险出现时调用mailx发送邮件通知MySQL数据库备份单循环与多循环两套方案分别满足整库备份和逐表细粒度备份需求还有Nginx访问日志按天切割、访问日志统计分析与网卡实时流量查看等脚本。脚本内容涉及iptables防火墙规则、awk日志统计、mysqldump备份、cron定时任务等核心命令读者既可快速复制部署也能按生产环境二次调整。资源包共1个文件为4.69MB的PDF文档适合离线查阅。已有154人学习该合集适合希望通过Shell简化服务器维护、提高故障响应效率的初中级运维人员。1. 279 个 shell 脚本到底能省多少事凌晨两点被监控电话叫醒Nginx 日志里同一个 IP 正在疯狂刷请求这是每个搞过线上运维的人都经历过的场景。我当时手动执行封禁命令等到把 IP 加进 iptables攻击流量已经持续了快一个小时。后来我把这套判断和封禁流程固化成了 shell 脚本交给 crontab 每分钟跑一次再也没在半夜被叫醒过。这套 279 个开箱即用的 shell 脚本就是把运维日常里最高频的操作全部脚本化Dos 攻击 IP 自动封禁、MySQL 数据库备份、Nginx 日志切割与分析、服务器初始化加固、批量服务器监控、企业微信告警基本覆盖了一台 Linux 服务器从上线到日常维护的所有阶段。适合刚入门的运维新手照着抄作业也适合想摆脱重复手工操作的熟手直接拿走改改用。2. Dos 攻击防范与告警从日志特征识别到 iptables 自动封禁2.1 先看清攻击特征从 Nginx 访问日志里数出异常 IP判断一个 IP 是不是在攻击最直接的办法不是看流量而是看它短时间内发起请求的频率。正常用户一秒点几下页面顶天了攻击脚本一秒能刷几十个请求。脚本里用了一条经典的管道命令组合把当天日志里每五分钟内请求数超过阈值的 IP 挑出来。# 获取当前日期格式为 01/Dec/2024:14:30 DATE$(date %d/%b/%Y:%H:%M) # 指定 Nginx 日志文件路径 LOG_FILE/usr/local/nginx/logs/demo2.access.log # 取日志最后 5000 行过滤出当前时间段的记录按 IP 统计访问次数 ABNORMAL_IP$(tail -n5000 $LOG_FILE | grep $DATE | awk {a[$1]}END{for(i in a)if(a[i]10)print i})这段逻辑拆开看并不复杂。tail -n5000只取日志最后 5000 行避免在大日志文件上执行全量扫描时消耗太多 IOgrep $DATE过滤出当前这一分钟内的请求DATE变量的格式是%d/%b/%Y:%H:%M对应日志里[01/Dec/2024:14:30:12]这种时间戳的前 16 位正好能匹配到分钟粒度最后的awk以 IP 为 key 做计数a[i]10是触发阈值也就是一小时内同一个 IP 请求数超过 10 次就判定为异常。这里有一个值得注意的选型细节为什么用tail -n5000而不是直接grep整个文件因为线上 Nginx 日志一天轻松到几百 MB全量扫描会把脚本的执行时间从秒级拖到分钟级攻击 IP 早就换了好几轮了。只扫描最后 5000 行绝大多数攻击流量都能命中。脚本的触发阈值 10 次也不是拍脑袋定的真实场景里要根据业务调——静态资源站、API 接口、图片站差异很大我一般先用下面的分析脚本跑一天看正常 IP 的请求分布再定阈值。2.2 封禁动作与幂等判断iptables 规则怎么加不重复筛选出异常 IP 只是第一步真正动手封禁的是下面这段。这里最关键的设计是加了一个幂等判断iptables -vnL | grep -c $IP检查这个 IP 是否已经在防火墙规则里避免重复追加导致规则表膨胀。# 循环处理每个异常 IP for IP in $ABNORMAL_IP; do # 检查 iptables 的 INPUT 链中是否已存在该 IP 的封禁规则 if [ $(iptables -vnL |grep -c $IP) -eq 0 ]; then # 追加 DROP 规则丢弃来自该 IP 的所有数据包 iptables -I INPUT -s $IP -j DROP # 记录封禁时间和 IP 到日志 echo $(date %F_%T) $IP /tmp/drop_ip.log fi doneiptables -I INPUT -s $IP -j DROP是把规则插入到 INPUT 链的头部-I和-A的区别要搞清楚-I插到最前面匹配优先级更高适合封禁这种需要立刻生效的场景-A追加到末尾如果前面有 ACCEPT 规则先命中封禁就失效了。封禁记录写到/tmp/drop_ip.log方便事后溯源。这段脚本配合 crontab 每分钟执行一次就能做到攻击 IP 从出现到封禁的延迟在一分钟以内。但脚本本身有个隐藏问题它只封了 DROP 规则没有做持久化。iptables的规则存在内存里重启后全部丢失。我自己的习惯是把规则单独保存一份# 保存当前 iptables 规则到文件 iptables-save /etc/iptables.rules # 在 rc.local 里加一行开机自动恢复 echo iptables-restore /etc/iptables.rules /etc/rc.local2.3 邮件告警mailx 的安装、配置与触发时机封禁动作做完得让运维知道发生了什么。脚本里用 mailx 发告警邮件安装和配置分两步走。yum install mailx装包后编辑/etc/mail.rc配置外部 SMTP 服务器这里踩过一个坑网易和腾讯的 SMTP 都要求使用授权码不能用邮箱登录密码。# 编辑 /etc/mail.rc 追加以下配置 set fromyourname163.com set smtpsmtp.163.com set smtp-auth-useryourname163.com set smtp-auth-passwordyour-authentication-code set smtp-authlogin配置里的smtp-auth-password填的是 SMTP 授权码不是邮箱密码。有人在这直接填了邮箱密码结果 mailx 报SMTP: 535 Error: authentication failed排查了半天才发现是授权码的问题。告警脚本的触发点放在iptables -I INPUT执行成功之后因为echo $(date %F_%T) $IP /tmp/drop_ip.log只是本地记录邮件才是真正通知到人的手段。完整链路是crontab 定时跑 → 检测到异常 IP → iptables 封禁 → 写入日志 → 邮件通知。这个脚本放线上跑之前建议先在测试环境拿一条自己的出口 IP 试试封禁和告警流程是否都通别等被攻击的时候才发现 mailx 的 SMTP 配置是错的。3. MySQL 自动备份与 Nginx 日志切割数据安全和日志轮转的正确姿势3.1 MySQL 数据库备份单循环适合中小库的快速方案数据库备份脚本的核心逻辑是遍历数据库列表逐个执行mysqldump导出。脚本用了mysql命令查询数据库列表再排除掉系统自带库只备份业务库。这个脚本我在小公司用的时候很顺手库不多、单库不大跑一次也就几分钟。#!/bin/bash # 当前时间作为备份文件名的一部分 DATE$(date %F_%H-%M-%S) HOSTlocalhost USERbackup PASS123.com BACKUP_DIR/data/db_backup # 查询所有数据库排除系统库 DB_LIST$(mysql -h$HOST -u$USER -p$PASS -s -e show databases; 2/dev/null | egrep -v Database|information_schema|mysql|performance_schema|sys) # 逐个数据库执行备份 for DB in $DB_LIST; do BACKUP_NAME$BACKUP_DIR/${DB}_${DATE}.sql # 导出单库失败时输出日志 if ! mysqldump -h$HOST -u$USER -p$PASS -B $DB $BACKUP_NAME 2/dev/null; then echo $BACKUP_NAME 备份失败! fi done-B $DB参数是--databases的简写导出的 SQL 文件里会自带CREATE DATABASE和USE语句恢复的时候不用手动建库直接mysql backup.sql就能还原。2/dev/null把错误信息丢弃了这是当时写脚本的一个草率决定虽然代码里给了失败提示但看不到具体报错原因后面真正遇到备份失败时排查全靠猜。现在我会改成2 /var/log/db_backup_error.log把错误落盘失败时至少有据可查。这个方案适合中小库的另一个原因是备份粒度粗。一个库一个文件恢复的时候要么全量恢复要么手动sed切分数据表非常痛苦。库的数量多、单库几百张表的业务就得看多循环方案了。3.2 MySQL 数据库备份多循环大库分表备份的精细方案多循环脚本在单循环的基础上多套了一层表的循环。每个数据库单独建一个目录目录下每张表一个 SQL 文件。这样恢复单表时只需要找到对应文件不用把整个库都导进来。#!/bin/bash DATE$(date %F_%H-%M-%S) HOSTlocalhost USERbackup PASS123.com BACKUP_DIR/data/db_backup # 获取数据库列表 DB_LIST$(mysql -h$HOST -u$USER -p$PASS -s -e show databases; 2/dev/null | egrep -v Database|information_schema|mysql|performance_schema|sys) # 外层循环遍历数据库 for DB in $DB_LIST; do # 按库名创建备份目录 BACKUP_DB_DIR$BACKUP_DIR/${DB}_${DATE} [ ! -d $BACKUP_DB_DIR ] mkdir -p $BACKUP_DB_DIR /dev/null # 查询当前库的所有表 TABLE_LIST$(mysql -h$HOST -u$USER -p$PASS -s -e use $DB;show tables; 2/dev/null) # 内层循环逐表备份 for TABLE in $TABLE_LIST; do BACKUP_NAME$BACKUP_DB_DIR/${TABLE}.sql # 导出单表失败时输出提示 if ! mysqldump -h$HOST -u$USER -p$PASS $DB $TABLE $BACKUP_NAME 2/dev/null; then echo $BACKUP_NAME 备份失败! fi done done内层循环的mysqldump命令少了-B参数改成直接指定库名和表名这样导出的 SQL 里不包含建库语句恢复单表时直接指定库即可。备份目录结构是db_backup/库名_时间戳/表名.sql找某一张表的备份只需要知道库名和表名ls一下目录就定位了。对比项单循环方案多循环方案备份粒度整个库一个文件每张表一个文件恢复效率全量恢复快单表恢复慢单表恢复快全量恢复需拼接文件数量库的数量所有表的总数适合场景库多表少、单库不大单库表多、需要单表恢复两个脚本都用for循环遍历这正是 shell 处理批量重复任务的典型写法。如果你对 for 循环的语法还不熟这个脚本里能看到完整的模式for 变量 in 列表; do ... done内层循环嵌套外层循环时用 tab 缩进区分层级脚本跑起来看输出就能确认执行到了哪一层。3.3 Nginx 访问日志按天切割mv 旧日志加 kill -USR1Nginx 日志切割是个经典需求因为access.log如果不切分一年下来能长到几十 GBgrep查一次日志要等半天。切割脚本的核心操作就两步把当天的日志文件改名归档然后让 Nginx 重新打开新的日志文件。#!/bin/bash LOG_DIR/usr/local/nginx/logs YESTERDAY_TIME$(date -d yesterday %F) LOG_MONTH_DIR$LOG_DIR/$(date %Y-%m) LOG_FILE_LISTdefault.access.log # 按月份归档不存在则创建目录 for LOG_FILE in $LOG_FILE_LIST; do [ ! -d $LOG_MONTH_DIR ] mkdir -p $LOG_MONTH_DIR # 把昨天的日志改名移动到归档目录 mv $LOG_DIR/$LOG_FILE $LOG_MONTH_DIR/${LOG_FILE}_${YESTERDAY_TIME} done # 向 Nginx 主进程发送 USR1 信号重新打开日志文件 kill -USR1 $(cat /var/run/nginx.pid)mv $LOG_DIR/$LOG_FILE $LOG_MONTH_DIR/${LOG_FILE}_${YESTERDAY_TIME}是把昨天的日志改名为default.access.log_2024-12-01按年-月目录归档。date -d yesterday %F取的是昨天日期配合 crontab 每天凌晨 0 点跑正好把前一天的日志收走。kill -USR1 $(cat /var/run/nginx.pid)是切割脚本的灵魂所在。Nginx 主进程收到 USR1 信号后会重新打开日志文件如果不发这个信号Nginx 的文件句柄还指向旧文件mv 之后日志照旧往旧文件里写虽然文件名变了但文件没变切割就失败了。cat /var/run/nginx.pid读的是 Nginx 的 pid 文件如果 Nginx 不是通过默认路径启动的这个路径要改成你自己的 pid 文件位置。日志归档的目录结构是logs/2024-12/default.access.log_2024-12-01我见过有人把归档目录建成了logs/20241201这种按天建的一个月后目录数量就是 30 个不如按年-月建目录再在文件名上带日期查询时ls logs/2024-12/一眼能看全一个月的文件。4. 服务器系统初始化与批量监控新机器到手后的第一件事4.1 新机器系统配置时区、SELinux、防火墙与历史命令时间戳每次拿到一台新服务器重复这些初始化操作实在浪费时间。这套脚本把安全基线的配置固化了下来CentOS 6 和 CentOS 7 的差异也做了判断处理。#!/bin/bash # 设置时区并同步时间 ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime # 配置每小时 NTP 时间同步 if ! crontab -l |grep ntpdate /dev/null; then (echo * 1 * * * ntpdate time.windows.com /dev/null 21;crontab -l) |crontab fi # 禁用 SELinux sed -i /SELINUX/{s/permissive/disabled/} /etc/selinux/config # 按系统版本关闭防火墙 if egrep 7.[0-9] /etc/redhat-release /dev/null; then systemctl stop firewalld systemctl disable firewalld elif egrep 6.[0-9] /etc/redhat-release /dev/null; then service iptables stop chkconfig iptables off fi # 历史命令显示操作时间 if ! grep HISTTIMEFORMAT /etc/bashrc; then echo export HISTTIMEFORMAT%F %T whoami /etc/bashrc fi # SSH 会话超时时间设置 if ! grep TMOUT600 /etc/profile /dev/null; then echo export TMOUT600 /etc/profile fi # 禁止 root 远程登录 sed -i s/#PermitRootLogin yes/PermitRootLogin no/ /etc/ssh/sshd_config # 禁止定时任务向 root 发送邮件 sed -i s/^MAILTOroot/MAILTO/ /etc/crontabegrep 7.[0-9] /etc/redhat-release这个判断很实用同一套脚本要在 CentOS 6 和 CentOS 7 上都跑通防火墙命令完全不同。CentOS 7 用systemctl管理 firewalldCentOS 6 得用service iptables stop加chkconfig iptables off。很多新手在 CentOS 7 上执行service iptables stop会得到iptables: unrecognized service就是因为两个版本的防火墙体系不一样。export HISTTIMEFORMAT%F %T \whoami 这行让 history 命令输出带上时间和用户。等真出事了要审计是谁在什么时候执行了什么命令时这条配置能省去很多扯皮。TMOUT600是设置 SSH 会话空闲 10 分钟自动断开防止有人挂着终端忘了退出。这两条都是改了全局配置文件的执行完记得source /etc/profile 或重新登录才生效。sed -i s/#PermitRootLogin yes/PermitRootLogin no/ /etc/ssh/sshd_config把 root 远程登录禁用了这个操作建议在所有初始化脚本确认能正常登录后再执行否则一旦 sshd restart 前配置写错远程就连不上了。稳妥做法是先备份一份cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak真出问题时还能恢复。4.2 内核参数与文件描述符高并发场景的系统层面设置文件描述符和内核参数这两个配置优化的是系统在高并发下的承载能力。limits.conf控制单进程能打开的文件数sysctl.conf里几个 TCP 参数调整了连接回收和队列深度。# 设置最大打开文件数 if ! grep * soft nofile 65535 /etc/security/limits.conf /dev/null; then cat /etc/security/limits.conf EOF * soft nofile 65535 * hard nofile 65535 EOF fi # 系统内核参数优化 cat /etc/sysctl.conf EOF net.ipv4.tcp_syncookies 1 net.ipv4.tcp_max_tw_buckets 20480 net.ipv4.tcp_max_syn_backlog 20480 net.core.netdev_max_backlog 262144 net.ipv4.tcp_fin_timeout 20 EOF # 减少 SWAP 使用 echo 0 /proc/sys/vm/swappiness这几个内核参数选的都是对日常 Web 服务影响最大的一批。net.ipv4.tcp_syncookies 1开启 SYN cookies能缓解 SYN Flood 攻击tcp_max_tw_buckets 20480限制了 TIME_WAIT 状态的 socket 数量避免大量短连接时 TIME_WAIT 耗尽内存tcp_max_syn_backlog 20480增大半连接队列长度高并发握手场景下减少丢包。vm.swappiness 0让内存优先使用物理内存、尽量少用 swap避免 swap 进出造成性能抖动。这里有个边界要讲清楚tcp_fin_timeout 20缩短了 FIN_WAIT_2 状态的等待时间对短连接频繁的业务效果明显但不适合长连接业务因为连接可能还没发完数据就被回收了。sysctl.conf的修改执行sysctl -p生效limits.conf的修改对新登录会话生效已经在线的进程不会立刻生效。swappiness那个 echo 只是临时生效重启后要重新设置正确做法是写入sysctl.conf。4.3 并发获取批量服务器 hostname用后台任务加wait汇总监控 100 台服务器的磁盘利用率如果一台一台 ssh 过去执行命令串行跑完一轮要十几分钟监控脚本根本没意义。项目里给了一个并发思路用for循环配合后台执行最后wait等所有后台进程结束。#!/bin/bash # 读取主机信息文件格式: IP 用户名 端口 HOST_INFOhost.info # 遍历所有主机 IP for IP in $(awk /^[^#]/{print $1} $HOST_INFO); do # 按 IP 关联取用户名和端口 USER$(awk -v ip$IP ip$1{print $2} $HOST_INFO) PORT$(awk -v ip$IP ip$1{print $3} $HOST_INFO) TMP_FILE/tmp/disk.tmp # 远程执行 df -h 获取磁盘使用率 ssh -p $PORT $USER$IP df -h $TMP_FILE # 解析使用率/dev/ 开头的是真实分区 USE_RATE_LIST$(awk BEGIN{OFS}/^\/dev/{print $NF,int($5)} $TMP_FILE) # 逐分区检查是否超过阈值 for USE_RATE in $USE_RATE_LIST; do PART_NAME${USE_RATE%*} USE_RATE${USE_RATE#*} # 使用率超 80% 输出告警 if [ $USE_RATE -ge 80 ]; then echo Warning: $PART_NAME Partition usage $USE_RATE%! fi done done这个脚本用了文件来中转数据ssh的结果写到TMP_FILE再用awk从临时文件解析分区名和使用率。USE_RATE_LIST$(awk ...)里print $NF,int($5)用做分隔符外层循环再用${USE_RATE%*}和${USE_RATE#*}做字符串切分分别拿到分区名和使用率。获取 hostname 并计时的脚本用到了 shell 并发的一个经典范式{ ... } 把耗时操作丢到后台wait等待全部完成。这种做法比xargs -P更直观适合固定主机列表的场景。#!/bin/bash # 所有主机 IP 列表 ALL_HOSTS(IP1 IP2 IP3) # 并发 ssh 获取 hostname 并记录耗时 for host in ${ALL_HOSTS[*]}; do { start_time$(date %s) ssh $host hostname /dev/null sleep 2 stop_time$(date %s) time_consuming$((stop_time-start_time)) # 结果写入文件 echo $host: $time_consuming hostname.txt } done wait # 按耗时排序取最短的机器 host$(sort -n -k 2 hostname.txt | head -1 | awk -F: {print $1}) # 输出这台机器 top 结果 ssh $host top -b -n 1两个脚本都具备实际可用的形态但有个共同的问题把 SSH 的密码交给了ssh命令实际运行时需要通过密钥认证登录或者借助sshpass传密码。纯手动运维 100 台服务器不现实建议配好 SSH key 后再跑这类脚本否则每台机器都会停在密码输入等人工操作wait会等上一整轮。5. 排查与避坑五个把脚本改成事故现场的操作5.1 日志切割后 Nginx 还在写旧文件现象按天切割脚本跑了几天后发现归档目录里昨天的日志文件在持续变大新的请求还在往里写。原因mv只是把文件名改了Nginx 进程的文件句柄还指向原来的 inode。不发送kill -USR1信号Nginx 根本不知道日志文件已经被挪走继续往同一个 inode 写数据。解决切割脚本最后必须执行kill -USR1 $(cat /var/run/nginx.pid)。验证方法很简单执行完切割后ls -l /usr/local/nginx/logs/看是否生成了新的access.log文件再tail -f看新请求是否落到了新文件里。如果 Nginx 长时间没有新请求可以用curl访问一下你的站点触发一条访问日志。5.2 iptables 封禁规则重启后全部丢失现象服务器重启后之前被封禁的攻击 IP 又能访问了防火墙规则表一片空白。原因iptables的规则保存在内核内存中不落盘的话重启即失。用iptables -vnL查看规则时显示的规则都是临时的。解决执行iptables-save /etc/sysconfig/iptables保存规则CentOS 6 上重启后iptables服务会自动恢复CentOS 7 上恢复规则需要安装iptables-services包或者把iptables-restore /etc/sysconfig/iptables写入/etc/rc.local开机执行。还有个细节/etc/rc.local在 CentOS 7 上默认没有执行权限要chmod x /etc/rc.local一次。5.3 mysqldump 备份文件是空文件现象备份目录里生成了 SQL 文件但文件大小是 0命令行手动执行同样的命令能正常导出。原因脚本里的2/dev/null把 mysqldump 的报错吞了很典型的场景是密码变量里带了特殊字符比如PASSMyPass在执行mysql -p$PASS时被 shell 解析成了别的含义。另一个常见原因是crontab执行时的环境变量和手动执行不同PATH里没有 mysql 的安装路径。解决把2/dev/null改成2/var/log/db_backup_error.log记录错误日志。密码变量用mysql --defaults-extra-file/etc/mysql_backup.cnf的方式传递把账号密码写进配置文件并chmod 600避免特殊字符被 shell 转义搞挂。脚本里统一写绝对路径/usr/local/mysql/bin/mysqldump杜绝 crontab 环境变量不一致的问题。5.4 awk 统计把内网 IP 和 CDN 节点全封了现象Dos 防范脚本跑完发现 iptables 里封禁了一大串内网 IP外网攻击 IP 反而漏掉了。原因awk {a[$1]}统计的是访问来源 IP如果 Nginx 前面挂了 CDN 或负载均衡脚本封的是 CDN 节点的 IP因为 CDN 转发请求时默认不改写$remote_addrNginx 看到的全是 CDN 的回源 IP。解决统计前先用grep -v排除内网网段和已知 CDN IP。常见做法是改成这样# 过滤掉内网 IP 和 CDN 回源 IP ABNORMAL_IP$(tail -n5000 $LOG_FILE | grep $DATE | \ grep -v -E ^(10\.|192\.168\.|172\.16\.|127\.) | \ awk {a[$1]}END{for(i in a)if(a[i]10)print i})或者让 Nginx 在日志里记录$http_x_forwarded_for用真实客户端 IP 做统计。依赖 CDN 回源 IP 做封禁是没有意义的因为 CDN 节点 IP 本来就是共享的、合法的。5.5 企业微信告警脚本的 access_token 失效现象企业微信告警脚本跑了几天后突然不推送了查看日志发现gettoken接口返回invalid credential。原因企业微信的access_token有效期是 7200 秒脚本里每次实例化DLF类都会重新请求一次 token但如果代码长时间运行没有重新实例化token 就过期了。另一个原因是corpsecret配置错了应用密钥或者应用被删除导致 token 获取失败。解决token 获取逻辑单独抽出来每次发消息前检查 token 的剩余有效时间剩余不足 600 秒时重新获取。最简单的方式是每次发送前都调一次_get_token()虽然多一次 HTTP 请求但避免过期问题。corpsecret去企业微信后台对应应用的「企业可信 IP」里核对应用被删除或密钥重置后旧密钥会立即失效脚本也要同步更新。6. 进阶用法把 279 个脚本变成自己的工具库脚本拿回来直接用会踩很多配置上的坑因为每台服务器的环境不一样。我的习惯是先把脚本按功能重新组织一下安全防御单独一个目录数据库备份单独一个目录日志处理一个目录监控告警一个目录。然后给每个脚本加一个统一的参数入口比如备份脚本改成可以通过命令行传入数据库账号和备份目录#!/bin/bash # 用法: ./db_backup.sh -u backup_user -p password -d /data/backup while getopts u:p:d:h opt; do case $opt in u) DB_USER$OPTARG;; p) DB_PASS$OPTARG;; d) BACKUP_DIR$OPTARG;; h) echo Usage: $0 -u user -p pass -d backup_dir; exit 0;; esac done统一了参数入口后再写一个总控脚本去调用它们配合 crontab 做成定时任务。定时任务的写法有个注意点统一把脚本路径和日志路径都写成绝对路径避免环境变量差异导致的“手动执行正常、crontab 执行就挂”的玄学问题。# crontab 配置示例 # 每分钟检测一次异常 IP * * * * * /usr/local/bin/dos_check.sh /var/log/dos_check.log 21 # 每天凌晨 2 点备份数据库 0 2 * * * /usr/local/bin/db_backup.sh -u backup -p 你的密码 -d /data/backup /var/log/db_backup.log 21 # 每天凌晨 0 点切割 Nginx 日志 0 0 * * * /usr/local/bin/nginx_log_cut.sh /var/log/nginx_cut.log 21还有个习惯值得养成每个脚本开头都用set -e加上trap记录异常退出时的行号线上排查问题时能直接定位到挂在哪一行。从那以后我每次部署新脚本都强制走一遍“手动执行 → 故意制造错误 → 看错误日志 → 挂到 crontab → 观察三天”的流程血泪经验告诉我跳过任何一步都会在某个凌晨付出代价。这套 279 个脚本的合集里不是所有脚本都适合直接上线但把它们当参考模板、按自己的业务改造成工具库能少写很多重复代码。希望帮到你。本文还有配套的精品资源点击获取
返回列表