资讯详情

279个Shell脚本:日志切割、MySQL备份与安全加固的运维工具包

📅 2026/10/16 20:55:28 | 华诺云谱 👁 阅读
279个Shell脚本:日志切割、MySQL备份与安全加固的运维工具包
简介2024年新版Shell脚本集整理了279个开箱即用的脚本面向Linux/Unix系统运维、信息安全防护和自动化运维学习者。合集将常见服务器维护任务按场景分类从防御、监控到备份与分析均有对应脚本。内容覆盖DDoS攻击IP自动屏蔽、Linux系统邮件告警、MySQL数据库单循环与多循环备份、Nginx访问日志按天切割与访问分析、网卡实时流量查看等高频运维场景。每个脚本均给出可直接运行的示例并对日期获取、日志过滤、iptables封禁、mysqldump导出、错误记录与cron定时执行等关键技术点作了解释方便读者结合自身环境改写后投入生产。压缩包为单个PDF文档大小4.69MB内容以代码片段和注释讲解为主适合作为日常服务器管理的速查手册。目前已有154人学习下载适合希望快速掌握Shell实战技巧、提升自动化运维能力的初中级运维人员和开发者。1. 279个开箱即用的shell脚本运维场景覆盖最全的脚本工具包做运维或者兼职管服务器的开发者电脑里通常都躺着几十个自己写的脚本但真到新装一台机器、突然被攻击、数据库要备份的时候翻来找去还是那几招。这份脚本包把散落在各种博客、技术社区里的实用脚本收集成了一套279个可直接落地的工具集从Dos攻击IP自动封禁、MySQL备份、Nginx日志切割分析到系统初始化、磁盘监控、企业微信告警、FTP和SSH客户端操作基本覆盖了单机运维和中小团队服务器管理的绝大多数场景。适合刚接触Linux的开发者照着敲一遍理解管道和awk的组合方式也适合有经验的运维直接改参数丢进crontab里跑。脚本大多是纯Shell少量Python辅助拿到手改改路径就能用。2. 日志与安全三件套Nginx按天切割、访问分析、Dos攻击IP自动封禁服务器上最先要做好的事就是日志管理。Nginx日志默认都写进同一个文件跑上一个月文件体积会变得很大分析的时候一条命令能卡半天磁盘也被无谓占用。这个包里给的方案是三个独立脚本组合使用按天切割归档、定期分析访问情况、对异常高频IP做防火墙封禁。三者可以各自独立跑也能串成一条防御链路。2.1 日志按天切割mv加USR1信号才是标准姿势网上很多日志切割方案直接kill Nginx进程或者用logrotate前者会让正在处理的请求断开后者配置项多容易把日志目录权限搞乱。这套脚本的思路是先把旧日志改名归档再用USR1信号让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 kill -USR1 $(cat /var/run/nginx.pid)逻辑上先算好昨天的日期拼进归档文件名然后按月建目录最后通过kill -USR1通知Nginx重新打开日志文件。这个做法的关键是USR1信号Nginx收到后会优雅地关闭旧文件句柄并创建新的日志文件不需要重启服务。归档目录按年月组织比如/usr/local/nginx/logs/2024-11/default.access.log_2024-11-15后续做月度清理或压缩都很方便。LOG_FILE_LIST变量支持多个日志文件空格隔开如果需要切割error.log往列表里加就行。注意kill -USR1需要Nginx进程对执行脚本的用户可见一般用root或nginx用户跑crontab。2.2 访问日志分析awk四连统计UV、PV、状态码和热点页面日志切割解决了文件膨胀问题分析脚本则从另一个维度帮你发现异常。输入文件路径作为参数脚本会依次输出访问最多的10个IP、指定时间段内访问最多的IP、访问最多的10个页面以及状态码与页面组合的统计。#!/bin/bash LOG_FILE$1 echo 统计访问最多的10个IP awk {a[$1]}END{print UV:,length(a);for(v in a)print v,a[v]} $LOG_FILE |sort -k2 -nr |head -10 echo ---------------------- echo 统计时间段访问最多的IP awk $4[01/Dec/2018:13:20:25 $4[27/Nov/2018:16:20:49{a[$1]}END{for(v in a)print v,a[v]} $LOG_FILE |sort -k2 -nr|head -10 echo ---------------------- echo 统计访问最多的10个页面 awk {a[$7]}END{print PV:,length(a);for(v in a){if(a[v]10)print v,a[v]}} $LOG_FILE |sort -k2 -nr echo ---------------------- echo 统计访问页面状态码数量 awk {a[$7 $9]}END{for(v in a){if(a[v]5)print v,a[v]}}awk默认按空格拆分日志行$1是客户端IP$4是时间戳$7是请求路径$9是状态码。第一段统计把所有IP和出现次数放进数组aEND块里先打印UV不重复IP数再遍历数组输出每个IP的访问次数sort -k2 -nr按第二列数字逆序排列head -10取出前10。时间段过滤直接字符串比较$4字段Nginx时间格式统一比较逻辑成立。注意示例里的时间段写的是2018年的历史日期实际使用时改成你要分析的时间窗口。状态码统计把$7和$9拼成一个字段比如/api/login 404这样能直接看出哪些接口在报错。这个脚本对小白很友好第一遍看awk数组逻辑有点绕跑一次就能明白。2.3 自动封禁策略tail配合awk统计高频IP再交给iptables脚本监控Nginx访问日志统计当前小时内访问超过10次的IP逐个检查是否已经在防火墙规则里不在就写入iptables DROP规则并记录到日志。#!/bin/bash DATE$(date %d/%b/%Y:%H:%M) LOG_FILE/usr/local/nginx/logs/demo2.access.log ABNORMAL_IP$(tail -n5000 $LOG_FILE |grep $DATE |awk {a[$1]}END{for(i in a)if(a[i]10)print i}) for IP in $ABNORMAL_IP; do if [ $(iptables -vnL |grep -c $IP) -eq 0 ]; then iptables -I INPUT -s $IP -j DROP echo $(date %F_%T) $IP /tmp/drop_ip.log fi done先取日志最后5000行grep过滤出当前小时的内容awk统计每个IP出现次数超过10次就把IP交给for循环。iptables -vnL带v参数显示详细信息grep -c判断IP是否已存在规则里避免重复添加。-I INPUT -s $IP -j DROP把IP加在规则链最前面。阈值10次每小时是比较宽松的生产环境一般按秒计算比如10秒内超过20次就可以封。我一般会改成awk {a[$1]} END {for(i in a) if(a[i]20) print i}只统计最近1000行里出现超20次的IP。如果服务器前面有负载均衡或CDN$1拿到的是代理IP而不是真实客户端IP这种情况下用$http_x_forwarded_for字段替代脚本里注释已经提醒了这点。3. MySQL备份脚本的两种循环从整库导出到单表落盘数据库备份是最不能出错的脚本之一。这个包里MySQL相关脚本给了两种方案单循环适合中小库快速打包多循环适合大库分表管理。两者都有一个共同的问题在安全性上默认密码是硬编码的生产环境一定要改成从配置文件读取或环境变量注入否则数据库密码会跟着脚本一起泄露。3.1 单循环备份遍历全部数据库逐个dump脚本先拿到所有数据库列表过滤掉系统库然后循环调用mysqldump把每个库导出成独立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_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 donemysql命令的-s参数是silent模式去掉了表格边框线输出每一行只有一个数据库名方便后面循环处理。egrep -v正则在CentOS 7自带的MySQL 5.7上能正常工作过滤掉information_schema等系统库。mysqldump的-B参数表示备份整个数据库包括建库语句恢复时直接source就能完整还原。备份失败只打了一行echo没有退出机制如果磁盘满了或密码错误脚本会静默失败然后继续循环下一个库。改进写法是维护一个失败计数器最后汇总输出或者配合上一章提到的mailx在失败时发告警邮件。参数作用建议值--single-transactionInnoDB表备份时保持一致快照必须加--routines导出存储过程和函数必须加--triggers导出触发器必须加--set-gtid-purgedOFF不输出GTID信息避免导入到无GTID实例时报错视环境而定3.2 多循环备份每个表独立导出恢复时按需加载单循环的缺点是恢复时整库导入想恢复单张表还是得全量倒一遍。多循环方案给每个数据库建一个子目录每张表导出成一个独立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外层循环遍历数据库内层循环遍历表。备份目录结构是/data/db_backup/dbname_时间戳/表名.sql时间戳精确到秒即使一天跑多次备份也不会重名。mysqldump这里不带-B参数因为指定了库名和表名导出的SQL文件里只有表结构、数据和索引定义恢复时先建库再逐表导入。多循环备份的文件数量会很大如果数据库有几百张表一次备份产生几百个文件stat慢、传输慢、清理也麻烦。折中方案是大表超过5GB的单独按表备份小表合并成一个SQL打tar包。3.3 备份脚本落地的几个强制习惯备份脚本写出来只是第一步真正要跑起来必须配置cron定时任务并做恢复演练。常见的做法是每天凌晨2点全量备份周日凌晨做一次完整恢复验证确认备份文件不是坏的。# crontab配置示例每天凌晨2点执行备份脚本 0 2 * * * /usr/local/scripts/mysql_backup_single.sh /var/log/mysql_backup.log 21输出重定向到日志非常重要cron的错误默认只发到root的邮箱而很多服务器根本没配mail服务脚本报错根本看不到。备份完成后保留最近7天或14天的文件老的自动清理避免磁盘被SQL文件撑爆。有条件的把备份文件再传到异地存储或另一台机器防止服务器硬盘故障导致备份跟着一起丢。4. 服务器初始化与安全加固时区同步、SELinux、SSH和内核参数一键配置新服务器到手要做的事基本是重复劳动改时区、同步时间、关防火墙、禁用SELinux、调SSH配置、设置文件描述符上限、优化内核参数。这套初始化脚本把这堆操作串成一条命令跑完再手工检查一遍关键项就行。4.1 基础环境设置时区、时间和历史命令记录刚买的云服务器默认时区往往是UTC日志时间跟本地对不上排查问题时看时间戳就像在看玄学完全对不上号。脚本先把时区软链到Asia/Shanghai然后检查crontab里有没有ntpdate定时任务没有就添加每小时的自动同步。#/bin/bash # 设置时区并同步时间 ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime if ! crontab -l |grep ntpdate /dev/null ; then (echo * 1 * * * ntpdate time.windows.com /dev/null 21;crontab -l) |crontab fi # 历史命令显示操作时间 if ! grep HISTTIMEFORMAT /etc/bashrc; then echo export HISTTIMEFORMAT%F %T whoami /etc/bashrc fi时区软链是传统做法新版CentOS用timedatectl set-timezone更正规但脚本这样写兼容性更好。ntpdate用的时间源是time.windows.com国内服务器访问这个地址延迟较高可以换成ntp.aliyun.com或cn.pool.ntp.org同步稳定性更好。历史命令加时间戳是很多运维容易忽略但实际很重要的配置。事故排查时看到一行命令没时间戳根本不知道什么时候执行的只能猜。HISTTIMEFORMAT用反引号嵌套了whoami登录用户也会记录进去多用户共用的服务器上能看出是哪个人执行了什么操作。4.2 安全加固SELinux、防火墙、SSH和sudo权限新装系统SELinux默认是enforcing模式会导致很多服务行为异常NGINX读不了文件、PHP连不上数据库、NFS挂载失败而且报错不会直接说跟SELinux有关排查起来非常痛苦。# 禁用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 # 禁止root远程登录 sed -i s/#PermitRootLogin yes/PermitRootLogin no/ /etc/ssh/sshd_config # 禁止定时任务向发送邮件 sed -i s/^MAILTOroot/MAILTO/ /etc/crontabSELinux的sed替换只改配置文件改完需要重启才生效或者执行setenforce 0临时关闭。脚本没有做这步跑完初始化后当前会话还是enforcing状态新部署的服务依然会被拦截建议补上setenforce 0。关闭防火墙的分支判断按系统版本走CentOS 7以上用systemctl操作firewalld6.x用service和chkconfig操作iptables。生产环境其实不建议直接关闭防火墙正确做法是把要用的端口加白名单。如果这台机器还要对外提供服务关了firewalld等于裸奔。禁止root远程登录这里有个坑脚本直接改sshd_config里的PermitRootLogin如果之前配过公钥登录改完后root的SSH密钥认证并不会受影响只有密码登录被禁用。但如果你正在用root密码登录远程改这个脚本执行完重启sshd前一定要先开一个测试连接确认普通用户能登录否则自己把自己锁在外面。4.3 系统资源限制与内核参数调整文件描述符上限默认1024Nginx高并发时很快会触及这个门槛报too many open files错误。进程数上限、SWAP使用倾向也要在初始化阶段调好。# 设置最大打开文件数 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/swappinesslimits.conf里的*通配符对所有用户生效soft是软限制hard是硬限制普通用户不能超过hard值。这里设置65535对绝大多数场景足够如果做高并发网关可以再往上调。内核参数里tcp_syncookies开启SYN Cookie防御半连接攻击tcp_max_tw_buckets控制TIME_WAIT状态连接数上限超过后内核会回收旧连接。tcp_fin_timeout从默认60秒降到20秒减少处于FIN_WAIT_2状态的时间加快连接释放。这些参数对高并发Web服务器有实际效果改完要执行sysctl -p加载。swappiness写入/proc/sys/vm/swappiness是临时生效的重启就恢复默认值60需要写进/etc/sysctl.conf才能持久化。这个脚本只做了临时修改算是一个留白介意的人可以自己补一行配置。5. 这些脚本最容易翻车的五个坑踩过的人都懂脚本本身大多是能跑的但直接拿去生产环境用分分钟教你做人。整理几个最常见的翻车点都是我实际跑过之后总结出来的。5.1 脚本里的日志路径和实际Nginx路径对不上现象跑Dos攻击防范脚本提示/usr/local/nginx/logs/demo2.access.log不存在脚本直接报错退出。原因脚本写死的路径是从某台服务器上拷贝来的编译安装Nginx的路径通常是/usr/local/nginx/logs但用包管理器装的可能在/var/log/nginx路径对不上导致tail命令找不到文件。解决先确认实际日志路径find / -name *.access.log 2/dev/null扫一遍改掉脚本里的LOG_FILE变量。路径确认后先手动跑一遍切割和分析脚本确认没有报错再进crontab。5.2 mysqldump备份的密码硬编码和特殊字符现象数据库密码含有、#、等特殊字符脚本执行报错Access denied或者密码被截断。原因mysqldump命令行接密码时特殊字符会被Shell解释比如123.com没问题但换成abc123时之后的内容可能被当成主机名处理。解决密码写入/etc/my.cnf的[client]段或者~/.my.cnf脚本里只写-p不带密码让mysql客户端自动读取配置文件。另一种方案是用环境变量MYSQL_PWD但要注意ps能看到环境变量安全性一般。5.3 运维时区统一问题导致日志时间错乱现象Nginx访问日志按天切割脚本跑完归档文件名是昨天但文件里最新的记录是今天凌晨的。原因date -d yesterday拿到的日期取决于服务器本地时区如果时区是UTC北京时间早上8点之前切割会把昨天的记录切成今天的。解决先把时区统一成Asia/Shanghai再调试切割脚本否则必须提前一个小时跑切割任务来规避时差。我一般会把脚本里切割时间固定在凌晨4点执行避开零点访问高峰和时区混淆确认服务器时区是东八区后再部署。5.4 iptables规则被清空导致封禁失效现象Dos攻击防范脚本跑完短时间后又出现同一IP的高频访问。原因云服务器的安全组或自动恢复机制定期重置防火墙规则或者服务器重启后iptables规则没有被持久化保存。解决iptables-save /etc/sysconfig/iptables保存规则或者用云平台的安全组白名单/黑名单功能替代iptables。脚本里加一层自检跑之前先数一下当前iptables规则数量少于预期值时先加载备份规则再执行封禁逻辑。5.5 并发执行SSH批量命令时卡死现象多主机hostname获取脚本运行后卡住长时间无输出不得不CtrlC。原因SSH默认在终端交互模式下会等待密码输入脚本里没加BatchMode参数碰到目标主机密钥变更或认证失败直接hang住。解决ssh命令加-o BatchModeyes -o ConnectTimeout5 -o StrictHostKeyCheckingnoBatchMode禁止交互式输入密码失败立即返回ConnectTimeout控制连接超时时间避免耗尽等待StrictHostKeyCheckingno跳过首次连接确认批量部署时省去手动输入yes的操作。6. 把脚本从“能跑”改成“能维护”的三个习惯脚本拿过来直接跑只能算会用真正能长久运转下去还得靠几个维护习惯。第一个习惯所有脚本统一放在一个目录下比如/opt/ops_scripts按功能分子目录放数据库备份的、日志切割的、安全封禁的各自独立。改密码或路径时全局搜索一下不比东一个西一个找脚本容易遗漏。第二个习惯每个脚本开头写清楚三块信息脚本作用是什么、依赖哪些命令和工具、验收方式是什么。比如MySQL备份脚本开头注明依赖mysqldump和mysql客户端、需要备份用户的select和lock tables权限、验收方式是恢复验证脚本能跑通。这样三个月后回来看脚本不用从第一行开始推理逻辑。第三个习惯强制加set -euo pipefail。很多脚本坑都在于某个命令失败后继续往下跑备份失败了还在执行后续循环切割失败了还在发信号。set -e遇到第一个错误就退出set -u变量未定义时报错set -o pipefail管道中任何命令失败都会导致整体失败。脚本开头加上set -euo pipefail后面再写判断逻辑更放心。代价是某些命令正常返回非零值会导致脚本中断比如grep没匹配到内容这时用|| true或if grep ...; then规避。从那以后我每次部署脚本都强制走一遍这三步改路径变量、跑一遍验证输出、加set -euo pipefail。尤其是MySQL备份脚本和老旧主机名的批量探测脚本少了set -e会在密码错误时无提示地继续执行生成一堆空文件看着像正常其实是废数据。把这三个习惯养成279个脚本里的八成都可以直接当作生产工具长期使用剩下的两成需要按自己的网络环境和服务器状态调一调参数。把这些脚本全部过一遍后你会发现大部分问题都有现成的解法关键是要知道边界在哪参数怎么改踩坑点长什么样。希望这些能实实在在帮你少走几步弯路。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑