Linux运维必备Shell脚本:日志过滤、健康检查与自动化实战
2026/9/17 17:21:37 网站建设 项目流程

简介:Linux运维工程师日常工作中常涉及日志排查、服务巡检、备份清理、远程分发等重复性任务,这份PDF以实战脚本为载体,系统整理了日志过滤与错误统计、ping连通性检查、旧文件删除、目录备份压缩、多文件循环处理、scp远程传输、用户home目录校验、日志实时监控、自动化创建用户、进程查杀与服务器初始化配置等10余类高频场景,并给出可直接改造使用的命令片段,适合有一定Shell基础或希望提升自动化效率的运维人员查阅。资源为单一PDF文档,体积仅100KB,便于快速下载与离线阅读;目前已有1164人学习/下载。文档中的脚本示例贴近生产实际,既包含基础条件判断、循环与文件操作,也有从日志中提取异常、检查服务健康、批量巡检多台机器等完整思路,可帮助读者减少手工操作、规范日常运维流程并沉淀属于自己的脚本工具箱。

1. 日志过滤与服务健康检查:先把手头的 grep 和 ping 用出工程味

服务器半夜告警、业务接口超时、磁盘被日志写满——运维接到消息的第一反应通常是跳上机器跑几条命令,而不是打开监控大屏找根因。grep过滤日志、ping探活、ps找进程,这些命令单独看都不难,但要把它们组合成能反复执行、输出可读、异常能第一时间暴露的脚本,就得在退出码、输出重定向和循环结构上较真。这份《Linux运维必备工作常用shell脚本》笔记,覆盖日志处理、服务健康检查、文件清理与备份、用户创建、进程管理、系统初始化、资源利用率和告警这几条主线,适合有一点 shell 基础、想直接拿脚本改改就用的运维工程师,也适合刚接手服务器、打算建立自己脚本库的新人。下面按排查顺序把脚本拆开讲。

2. 文件与备份自动化:find、tar 与 for 循环的边界

2.1 find + mtime 定位过期文件,-exec 才是关键

清理日志和临时文件是运维每周都要做的事,手动一条条lsrm效率太低。用find-mtime参数按修改时间过滤是最通用的做法:

# 找出 90 天前修改的普通文件,只列不删 find /home/soyoger/logs -type f -mtime +90 -exec ls {} \; # 找到并删除 find /home/soyoger/logs -type f -mtime +90 -exec rm {} \; # 找到后改名,留作归档 find /home/soyoger/logs -type f -mtime +90 -exec mv {} {}.old \;

三条命令参数完全一致,只有-exec后面的动作不同,所以验证时先用ls跑一遍,确认匹配范围没有误伤再换rm。参数说明:-type f限定普通文件,避免匹配到目录;-mtime +90表示修改时间在 90 天以前,不加+号则精确匹配 90 天当天;-exec把前面找到的每个文件路径替换进{}\;-exec子句的结束符,必须转义,否则 shell 会把它当成后台执行符号。

实际使用中要注意两点。第一,rm动作一旦执行不可回退,建议先用find ... | wc -l统计数量,确认没有把nginx.piderror.log这类还在写的文件误纳进来。第二,如果文件名带空格或换行,默认的-exec会出问题,需要配合-print0xargs -0,日常日志文件很少出现这种命名风格,先不过度设计。我一般会在清理脚本里加一个-mtime +90 ! -name "*.lock"排除掉锁文件,避免删掉正在占用的文件句柄。

2.2 打包压缩:tar 与 gzip 的分工

清理旧文件之前,通常先做一轮压缩备份。笔记里给的是两步式写法:

tar cvf log_bak.rar ./log gzip log_bak.rar

tar负责把./log目录打成单个归档文件,参数c创建归档、v显示处理的文件名、f指定归档文件名。gzip再对这个归档做压缩,得到log_bak.rar.gz。分两步写的好处是能看清楚每一阶段产物,排错时知道是打包失败还是压缩失败。日常也可以直接用tar czvf log_bak.tar.gz ./log一步到位,z参数让 gzip 在 tar 内部完成。

一点注意:扩展名.rar只是个名字,tar 并不会真的生成 rar 格式文件,真正格式由 gzip 决定,所以看到log_bak.rar.gz不要惊讶。备份文件建议同时带上日期,例如log_bak_$(date +%F).tar.gz,否则第二天执行同样的备份脚本会把前一天的归档覆盖掉,这个坑在 crontab 场景里非常常见。

命令功能典型参数
find按条件查找文件-type f、-mtime +90、-exec
tar打包归档c 创建、v 显示过程、f 指定文件名
gzip压缩单个文件无参数或 -9 最大压缩
xargs把标准输入转成命令参数-I{} 指定替换符、-0 配合 null 分隔

2.3 for 循环批量操作:花括号展开、seq 与 read 的组合

笔记里批量建文件给了两种写法,适用于不同场景:

# 方式一:花括号展开,适合固定数量 for i in {1..20} do touch test.$i done # 方式二:交互输入数量,配合 seq 生成序列 echo "please input num your want to create?" read num for i in $(seq 1 $num) do touch test.$i done

方式一的花括号{1..20}是 bash 内置展开,不支持变量,所以数量固定时用。方式二用read读入变量num,再用$(seq 1 $num)动态生成序列,适合不确定场景。seq还能指定步长,例如$(seq 1 2 20)生成 1、3、5……用来给一组端口或分片编号做操作很顺手。read默认按回车结束,读入的字符串里如果有空格会被拆成多个变量,只读数量不涉及这个问题,但建议加-p参数给出提示。

对已有文件的批量改名,笔记里用的是先判断存在再mv

for file_name in *.txt do if [ ! -e "$file_name" ]; then echo "$file_name is not exist." else mv "$file_name" "${file_name}.bak" fi done

这个脚本正确的地方在于先做-e存在性判断,避免mv时报 no such file。但注意*.txt没有匹配时,bash 会把字面量*.txt传给循环,此时[ ! -e "*.txt" ]会误判。稳妥做法是在脚本开头加shopt -s nullglob,让没有匹配时循环直接跳过。变量在判断和mv时都加了双引号,防止路径中含空格被拆分,这一点比很多生产环境里的脚本都严谨。

2.4 scp 远程拷贝与多 IP 巡检脚本

跨机器分发文件,scp是最直接的工具:

scp some_files user@host:/tmp

参数含义:some_files是本地文件,user@host是目标机器账号和地址,/tmp是远端存放路径,目录复制需要加-rscp走 ssh 协议,所以目标机要能通过 ssh 认证。实际脚本里建议先ssh-keyscan把主机指纹加入known_hosts,否则首次连接会卡在 yes/no 交互上,crontab 里跑这类脚本尤其容易忽略这一点。

巡检多台机器时,笔记里的做法是把 IP 列表放进文件,再逐行 ping:

hosts_file="/home/soyoger/ip_list" for ip in $(cat "$hosts_file") do ping -c1 "$ip" >& /dev/null if [ $? -eq 0 ]; then echo "$ip is passed." else echo "$ip is faild." fi done

这里关键在>& /dev/null:把 ping 的正常输出和错误输出全部丢弃,$?才能纯粹反映 ping 的退出码。ping -c1指定只发一个包,适合脚本探活,真实环境建议加-W 2限制超时为 2 秒,避免 DNS 解析或网络异常时整个脚本卡几分钟。读取方式上,$(cat "$hosts_file")会按空白字符切分,如果 IP 文件里有注释或空行,需要先grep -v '^#'过滤。用while read逐行读取比for更稳,能正确处理无结尾换行的文件,这点在日志监控章节再展开。

3. 用户与进程管理:安全创建、随机密码与 kill 管道

3.1 从 /etc/passwd 判断用户是否存在的细节

批量建用户之前,第一件事是确认目标用户是否已存在。笔记里的做法值得保留:

cd /home for dir in * do is_exist=$(grep -c "/home/${dir}" /etc/passwd) if [ $is_exist -ge 1 ]; then echo "$dir user is already existed." else echo "$dir user is not exist." fi done

这段逻辑是:遍历/home下的目录名,到/etc/passwd里数一数匹配/home/目录名的行数,大于等于 1 就认为用户存在。grep -c输出的是匹配行数,不是布尔值,所以用-ge 1判断。但这里存在一个误报场景:grep "/home/soyoger"会把/home/soyoger_bak也匹配上,因为 grep 是子串匹配。更精确的做法是匹配用户行中 home 字段的完整路径,即grep -c ":/home/${dir}:" /etc/passwd,因为 passwd 每行最后一个字段就是家目录,前后都有冒号分隔。没有家目录的系统账号不会被误判,这也是为什么先跑一遍ls /home再对照 passwd 是合理的。

3.2 useradd 参数拆解与整套创建流程

确认不存在之后,创建用户和设置密码可以写成一段带状态输出的脚本:

echo "please input the new user name:" read name is_exist=$(grep -c ":/home/${name}:" /etc/passwd) if [ $is_exist -ge 1 ]; then echo "${name} user is already existed." else useradd -s /bin/bash -d /home/${name} -m ${name} if [ $? -eq 0 ]; then echo "123456" | passwd --stdin ${name} fi fi

useradd参数:-s /bin/bash指定登录 shell;-d /home/name指定家目录路径;-m表示目录不存在时自动创建。passwd --stdin从标准输入读取密码,专为脚本设计,交互式敲passwd无法自动化。注意--stdin是 Linux 发行版通用选项,但个别 Unix 系统不支持,用之前先man passwd确认。密码用固定字符串只是演示,生产环境必须随机生成并落盘,见下一节。创建完成后用id namels -ld /home/name验证,顺便检查家目录属主是否对。

3.3 随机密码生成:$RANDOM、md5sum 与 cut 的配合

批量创建 100 个用户时,人工设密码不现实,笔记里用一段三连管道生成随机密码:

pwd=$(echo $RANDOM | md5sum | cut -c 1-8) echo "${pwd}" | passwd --stdin ${name} echo "${name} ${pwd}" >> user_list.txt

命令从左到右拆解:$RANDOM是 bash 内置变量,每次展开产生一个 0 到 32767 的随机整数;md5sum把随机数计算成 32 位十六进制摘要;cut -c 1-8截取前 8 个字符作为密码。整个过程完成前密码不落盘,只有输出到 passwd 和user_list.txt时才有密码出现。

可以替换的空间很大:更安全的做法是用openssl rand -base64 12直接生成 12 位带大小写和特殊字符的密码;想避免特殊字符在登录时被转义,就保持 md5sum 方案。无论哪种,密码必须追加写入user_list.txt,并尽快把权限改成 600,否则批量创建完成后连自己都不知道密码是什么,排查时很被动。删除用户对应一条命令:userdel -r name-r同时删除家目录和 mail spool,属于不可逆操作,确认用户数据已备份再执行。

3.4 检查进程并 kill:管道组合与 grep 自匹配问题

进程管理脚本是psgrepawkxargs四个命令的经典管道:

ps -ef | grep xxx | grep -v grep | awk '{print $2}' | xargs -I{} kill {}

流水线含义:ps -ef输出全格式进程列表;第一层grep xxx筛出目标进程;第二层grep -v grep把 grep 命令自身产生的进程行剔除,否则会误杀执行 grep 的进程;awk '{print $2}'取第二列 PID;xargs -I{}把每个 PID 依次传给 kill。grep -v grep是新手最容易漏的一步,丢了它你会在杀掉目标进程的同时收到 grep 进程被 kill 的报错,虽然不影响目标进程处理,但脚本会返回非零状态,如果后面跟了set -e就会提前退出。

更稳的替代方案是pgrep -f xxx,直接返回匹配 PID,一行搞定且不会匹配自身。kill 常规用 TERM 信号,进程不响应再kill -9,脚本里可以加个超时等待:先 kill,sleep 2,再用kill -0判断进程是否还活着。

提示:kill -0 pid不会真的发信号,只检查进程是否存在,是判断进程是否存活的低成本手段。

4. 系统初始化与资源监控:时区、SSH 配置与 vmstat 取值

4.1 新机器交付前的初始化脚本

接管一台新服务器,时区、历史命令、SSH 配置、swap 策略这几项是标配。笔记里给了可直接用的片段:

# 设置时区并同步时间 ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime ntpdate ntp1.aliyun.com # 历史命令显示时间戳 if ! grep HISTTIMEFORMAT /etc/bashrc; then echo 'export HISTTIMEFORMAT="%F %T `whoami` "' >> /etc/bashrc fi # SSH 空闲超时 if ! grep "TMOUT=600" /etc/profile &> /dev/null; then echo "TMOUT=600" >> /etc/profile fi # 减少 swap 使用,0 表示优先回收内存页 echo "0" > /proc/sys/vm/swappiness

时区部分用符号链接指向 Shanghai 时区文件,覆盖原有/etc/localtimentpdate是一次性同步,生产环境建议改用 chronyd 持续同步。历史命令时间戳通过export HISTTIMEFORMAT="%F %T whoami "实现,%F是日期、%T是时间、whoami记录执行用户,最外层是单引号,保证变量在写入 bashrc 时才展开,写成双引号会被立即展开成当前时间,这是易错点。TMOUT=600表示用户空闲 600 秒自动登出,写入/etc/profile对所有用户生效。swappiness写入/proc/sys/vm/swappiness只对当前内核生效,重启后失效,要永久化需写入/etc/sysctl.conf,对应配置项vm.swappiness=0

SSH 安全配置部分,笔记里有一条 sed:

sed -i 's/#PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config

这个替换用于把注释状态的 PermitRootLogin 从 yes 改为 no,但有个坑:如果原文件里本身就是PermitRootLogin yes(没有注释符号#),这条 sed 匹配不上。笔记原句还多了一个字母,写成PerimitRootLogin,这是典型的复制粘贴手误。稳妥写法是sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no/'^#\?匹配行首可有可无的#,通配后面的内容整体替换。改完必须systemctl reload sshdrestart sshd才生效。

4.2 资源利用率函数:vmstat 与 free 的 awk 取值

一键查看服务器资源利用率是生产环境最常被问到的需求,笔记里的函数写法值得逐行拆:

function cpu() { util=$(vmstat | awk '{if(NR==3) print $13+$14}') iowait=$(vmstat | awk '{if(NR==3) print $16}') echo "CPU-使用率:${util}%,等待磁盘IO响应使用率:${iowait}%." }

vmstat不带参数输出两行数据:第一行是自开机以来的平均值,第二行才是当前采样值,所以 awk 用NR==3取当前这一行(标题行 NR=1,平均值行 NR=2,当前行 NR=3)。

vmstat 列含义本题用法
$13 (us)用户态 CPU 时间占比与 sy 相加得 CPU 使用率
$14 (sy)内核态 CPU 时间占比与 us 相加得 CPU 使用率
$16 (wa)等待磁盘 IO 完成的时间占比单独输出,判断 IO 瓶颈

这里有个细节:原文 echo 里写的是iostat,但上面赋值的变量名是iowait,bash 对未定义变量默认展开为空,所以会输出空值。这种变量名不统一的 bug 在复制粘贴时很常见,拿到任何脚本先通读一遍变量引用,比运行时排错省事。CPU 使用率也可以直接top -bn1 | grep Cpu取,但 vmstat 的输出字段更适合 awk 自动化处理,脚本里保持一致即可。

内存部分用 free 取值:

function memory() { total=$(free -m | awk 'NR==2 {printf "%.1f", $2/1024}') used=$(free -m | awk 'NR==2 {printf "%.1f", ($2-$NF)/1024}') available=$(free -m | awk 'NR==2 {printf "%.1f", $NF/1024}') echo "MEMORY-总大小:${total}, 已使用:${used}, 剩余:${available}." }

free -m以 MB 为单位,awkNR==2取 Mem 行。$2是 total 内存,$NF是最后一列 available,新版 free 的最后一列就是可用内存,直接取这一列避免自己算 cache 和 buffer。除以 1024 换算成 GB,printf "%.1f"保留一位小数。注意$NF在不同 free 版本里语义略有差异,Debian/Ubuntu 的 free 是 available,老版 CentOS 可能没有这一列,脚本跨发行版跑之前先free -m看一眼列结构。

4.3 系统分析工具安装与平台差异

初始化脚本里工具安装分两个发行版:

# centos yum install gcc make autoconf vim sysstat net-tools iftop iotop lrzsz -y # ubuntu apt-get update; apt-get install gcc make sysstat net-tools iftop iotop lrzsz

两边都装了sysstat(提供 iostat、sar)、net-toolsiftopiotoplrzsz。CentOS 的 yum 装最新版往往滞后,遇到工具版本老到影响排查时,可以加 EPEL 源;Ubuntu 侧apt-get update必须在 install 之前执行,否则本地索引缺失会直接报 404。iotop依赖内核 taskstats 特性,部分云厂商内核可能缺失,运行报错时先确认内核版本和内核配置,不要一上来就重装。

5. 日志监控与告警:tail -fn0 到 mailx 的落地细节

5.1 tail -fn0 与 while read 的取行姿势

监控日志最简单的方式是tail -fn0配合while read

tail -fn0 /var/log/message | while read line do echo "$line" | egrep -i "error|faild|shut|down" if [ $? -eq 0 ]; then echo "$line" >> /tmp/error.log fi done

tail -fn0-f表示持续跟踪文件新增内容,-n0表示从文件末尾开始、不读取已有历史行,这样脚本启动前的老日志不会重复处理。管道把每一行新日志交给while readread按行读取,行尾换行符会被自动去掉。egrep -i用扩展正则且忽略大小写,匹配 error、faild、shut、down 任一关键词,$?为 0 表示匹配成功,追加写入/tmp/error.log

这里有一个 shell 管道陷阱:管道右侧的while在子 shell 中执行,循环内定义的变量在循环结束后不可见。如果脚本后面要统计本轮告警条数并用于邮件正文,需要把计数逻辑放进循环内,或者改用进程替换while read line; do ...; done < <(tail -fn0 /var/log/message),这样 while 在当前 shell 执行,变量能带到循环外。

5.2 结合 mailx 的告警链路与守护运行

有了错误日志,下一步是发告警邮件。先配/etc/mail.rc,内容大致是:

set smtp=smtp.example.com:25 set smtp-auth=login set smtp-auth-user=monitor@example.com set smtp-auth-password=your-password set from=monitor@example.com

参数说明:smtp指定邮件服务器地址和端口,smtp-auth声明认证方式,smtp-auth-usersmtp-auth-password是认证凭据,from决定发件人显示。配置好后,检测到新错误时发送:

grep -i error /tmp/error.log | tail -n 5 | mailx -s "Prodcution Error Alert" ops@example.com

-s指定主题,收件人地址支持多个,空格分隔,正文从标准输入读取,所以通常把 grep 结果管道给 mailx。生产环境不建议脚本里明文写密码,可以把/etc/mail.rc权限改成 600,或改用 sendmail 方式绕过认证。

这套链路跑在后台可以用nohup,但更规范的是写成 systemd service,配合Restart=always保证日志监控进程挂掉后自动拉起。告警邮件去重是另一个细节:连续相同的错误行在短时间内只发一封,常见做法是在写入/tmp/error.log前维护一个最近 5 分钟的指纹列表,用md5sum对行内容取哈希再比对,重复的不进邮件。把tail -fn0的监控频率和邮件发送频率解耦,能有效避免日志刷屏时邮件服务器被拖垮。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询