ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

25个Linux Bash脚本实例:从文件操作到系统监控的自动化实战

25个Linux Bash脚本实例:从文件操作到系统监控的自动化实战 说到Linux运维和开发bash脚本几乎是绕不开的一道坎。很多朋友一听到“脚本”两个字就觉得是程序员才需要学的其实完全不是这么回事。日常批量改文件名、压缩备份、看磁盘空间、定时执行某个命令这些重复劳动写成一个bash脚本一次写好后面敲两下键盘就能搞定。我自己的工作流里脚本帮我省下的时间累计起来足够刷好几部剧了。这篇文章把25个最常见的Linux bash脚本例子拿出来逐个拆解覆盖文件操作、文本处理、系统监控、备份网络这些日常最常用的场景。每个例子都给你可以直接复制运行的代码再配上我实际操作中的踩坑记录。不管你是刚接触Linux的小白还是已经写了几年脚本但没系统整理过的人这25个例子都能拿来即用或者当做一个“随时查阅的枕头书”。1. 为什么要用bash脚本先搞清楚它解决什么问题1.1 脚本不是程序员的专属有些人一听到bash脚本就头疼觉得语法奇怪、看起来像天书。换个角度看bash脚本其实就是把你在终端里一条一条敲的命令提前写进一个文件里。终端上会敲ls、cd、cp就会写脚本只是多了一层流程控制和变量而已。我见过很多做运维、做测试、甚至做数据分析的朋友他们不写Java也不写Python但靠几个bash脚本把日常工作自动化得明明白白。比如每天上班第一件事是检查服务器磁盘以前是登录服务器敲df -h再盯着看半天后来写成脚本自动把超过80%的分区用红字标出来。这就是脚本最有价值的地方把重复劳动交给机器把判断逻辑沉淀成可复用的资产。1.2 学完这25个例子你能得到什么这25个例子覆盖了bash脚本最核心的知识点变量与参数、条件判断、循环、函数、数组、文本处理、系统信息获取。学完后你会掌握三种能力看得懂别人写的脚本拿到新环境不至于抓瞎能把日常重复的命令组合打包成一个可执行的脚本加参数、做判断遇到脚本报错知道用哪些调试手段快速定位问题这比背一堆命令要有用得多。命令是砖头脚本是把砖头砌成墙的方式。2. 环境准备与最基础语法5分钟把地基打牢2.1 第一个脚本怎么写shebang、执行权限、运行方式先别急着看25个例子花5分钟把最基础的三个概念弄清楚后面会顺畅很多。第一个脚本内容如下#!/bin/bash echo hello world第一行#!/bin/bash叫做shebang它告诉系统用哪个解释器来执行这个脚本。虽然也有人写成#!/bin/sh但我在生产环境里一律推荐写#!/bin/bash因为bash功能更全语法兼容性更好避免在数组、[[ ]]这些高级特性上翻车。保存脚本后需要给它执行权限chmod x hello.sh然后可以这样运行./hello.sh这里有一个新手特别容易踩的坑直接输hello.sh会提示command not found。因为系统只在PATH里找命令当前目录通常不在PATH里。所以要么加上./要么把脚本放到/usr/local/bin这类目录下。我个人习惯是本地测试用./正式部署时放到/usr/local/bin并去掉.sh后缀用起来跟系统命令一样。2.2 变量、注释、退出码三个必须懂的概念变量是脚本的血肉。定义变量很简单name张三 echo 你好$name注意等号两边不能有空格写name 张三会报错。取值用$name或者${name}推荐用带花括号的写法避免变量名边界问题。比如${name}_backup如果写成$name_backup系统会去找name_backup这个变量结果就是空。注释用#开头只注释到行尾。脚本里注释多写几行三个月后回头看会感谢自己。退出码是脚本和调用方沟通的语言。一条命令执行成功返回0失败返回非0。脚本里可以用exit 1主动退出并带上错误码配合后面的逻辑判断能写出更健壮的脚本。判断上一条命令是否成功最直接的就是$?ls /tmp if [ $? -eq 0 ]; then echo 成功 else echo 失败 fi3. 25个常见bash脚本实例分四类逐个拆解3.1 文件与目录操作例子1-7文件操作是bash脚本最常干的活。每天面对服务器上的日志、归档、临时文件用好这几个例子能省下大量时间。例子1输出和日志#!/bin/bash echo 开始执行任务 echo 错误信息 22是把内容输出到标准错误。这点很多新手忽略但在脚本结合cron定时任务时很重要因为标准输出和标准错误可以分别写到不同日志里排查问题的时候一眼定位。例子2创建带时间戳的目录#!/bin/bash backup_dir/data/backup/$(date %Y%m%d_%H%M%S) mkdir -p $backup_dir echo 备份目录已创建$backup_dirdate %Y%m%d_%H%M%S生成形如20250614_153001的时间戳mkdir -p表示父目录不存在时自动创建。例子3遍历目录下的所有txt文件#!/bin/bash for file in /data/logs/*.txt; do echo 找到文件$file done如果/data/logs/*.txt一个都没匹配到bash会把通配符原样传给循环$file就会变成/data/logs/*.txt这个字符串。稳妥的做法是加一层判断for file in /data/logs/*.txt; do [ -e $file ] || continue echo 找到文件$file done例子4批量重命名备份#!/bin/bash for file in *.conf; do cp $file ${file}.bak.$(date %Y%m%d) done把当前目录所有.conf文件复制一份带日期的备份。这里花括号${file}是必需的如果不写变量名会跟后面的.bak连在一起解析结果完全不对。例子5删除指定天数前的临时文件#!/bin/bash find /tmp -type f -name *.tmp -mtime 7 -exec rm {} \;mtime 7表示修改时间超过7天。-exec rm {} \;是find的标准写法也可以用-delete代替find /tmp -type f -name *.tmp -mtime 7 -delete简洁很多。但是在删除之前先加-print预览一遍是个好习惯。例子6统计目录下文件数量#!/bin/bash count0 for file in /var/log/*.log; do [ -f $file ] count$((count1)) done echo 日志文件数量$count更高效的方式是ls -1 /var/log/*.log | wc -l但遇到目录名带空格的情况会出错所以循环遍历加-f判断更稳。例子7递归查看目录结构#!/bin/bash find . -type d | while read dir; do echo 目录$dir done这个例子展示了while read配合管道处理逐行输出的套路后续会在文本处理里频繁出现。3.2 文本处理与字符串例子8-13bash本身处理字符串能力不算强但配合grep、sed、awk这些命令整个系统都成了你的文本处理工具。例子8查找日志中的错误行#!/bin/bash grep -i error /var/log/app.log | awk {print $1, $2, $NF}awk {print $1, $2, $NF}打印第一列、第二列和最后一列常用于提取时间、级别和关键信息。这里的$NF是awk内置变量表示字段数量也即最后一列。例子9提取特定字段#!/bin/bash line2025-06-14 15:30:01 ERROR 连接超时 # 以空格为分隔符取第4列 echo $line | cut -d -f4cut -d -f4指定空格为分隔符取第4个字段。注意-d 要传一个空格容易手滑写成-d 那样是错的。例子10字符串替换#!/bin/bash strhello_world_2025 # 替换第一个下划线 echo ${str/_/-} # 替换所有下划线 echo ${str//_/-}${var/old/new}替换第一个${var//old/new}替换所有。这个语法在你写批量处理脚本时非常好用不用为了替换一个字符串去启动sed。例子11判断字符串是否为空#!/bin/bash input$1 if [ -z $input ]; then echo 第一个参数为空 exit 1 fi echo 参数内容$input-z判断字符串长度是否为0。这里也推荐用[[ ]]代替[ ]功能更强大比如模式匹配if [[ $input *.log ]]; then echo 以.log结尾 fi例子12循环读取文件每一行#!/bin/bash while IFS read -r line; do echo 读取到$line done /etc/hostsIFS表示不去掉行首行尾的空格-r表示反斜杠不转义。这两个细节如果不注意遇到带有特殊字符的配置文件时行内容会悄悄变化。我自己写过很多读取列表的脚本这个写法是唯一在各种环境都稳定工作的。例子13统计单词出现次数#!/bin/bash cat /tmp/words.txt | tr \n | sort | uniq -c | sort -nr | head -5这个组合命令经常被用来快速统计日志里某个词的出现频率。tr \n把空格换成换行sort排序uniq -c统计最后sort -nr按数字降序。别背理解每个工具的职责后你随时能自己拼出类似管道。3.3 系统信息与监控例子14-19在服务器上做巡检、告警bash脚本是最轻量的方案不需要装agent一个脚本加一条cron任务就能跑。例子14动态获取当前日期和时间#!/bin/bash today$(date %F) time_now$(date %T) echo 今天$today当前时间$time_now%F等价于%Y-%m-%d%T等价于%H:%M:%S。注意date %F返回的是字符串想把它作为数字比较比如比较哪天的目录新旧要转成时间戳date1$(date -d 2025-06-01 %s) date2$(date -d 2025-06-14 %s) if [ $date1 -lt $date2 ]; then echo 6月1号更早 fi例子15获取系统负载并判断#!/bin/bash load$(uptime | awk -Fload average: {print $2} | cut -d, -f1) echo 当前1分钟负载$load threshold2.0 if [ $(echo $load $threshold | bc) -eq 1 ]; then echo 负载过高注意检查 fibash不支持浮点运算echo $load $threshold | bc让bc做浮点比较返回1为真。这个点新手很容易卡住。例子16磁盘使用率告警#!/bin/bash df -h | grep -vE ^Filesystem | while read line; do usage$(echo $line | awk {print $5} | tr -d %) if [ $usage -ge 85 ]; then echo $line | awk {print 分区$NF使用率:$5 容量:$2} fi done这条命令会遍历所有挂载点找出使用率大于等于85%的分区。grep -vE ^Filesystem把表头行过滤掉tr -d %去掉百分号然后进行数值比较。例子17实时监控内存使用#!/bin/bash mem_total$(free -m | awk NR2{print $2}) mem_used$(free -m | awk NR2{print $3}) mem_rate$((mem_used * 100 / mem_total)) echo 内存使用率${mem_rate}%free -m第二行对应Memawk NR2{...}是取第二行。整数运算用$(( ))注意乘除的顺序$((mem_used * 100 / mem_total))如果不加括号bash会按从左到右算可能得到错误精度。例子18检查关键进程是否存活#!/bin/bash if pgrep -x nginx /dev/null; then echo nginx运行中 else echo nginx未运行尝试启动 systemctl start nginx fipgrep -x是精确匹配进程名。脚本里判断进程时不要用ps -ef | grep nginx | grep -v grep这种老方法容易误匹配pgrep更可靠。例子19获取本机IP地址#!/bin/bash ip_addr$(hostname -I | awk {print $1}) echo 本机IP$ip_addrhostname -I会输出所有IP取第一个通常是主IP。如果你的机器有多网卡用ip -4 addr show eth0 | grep inet | awk {print $2} | cut -d/ -f1更精准。3.4 备份、网络与用户交互例子20-25例子20带日期打包备份目录#!/bin/bash backup_dir/data/backup source_dir/var/www/html tar -czf $backup_dir/html_$(date %Y%m%d_%H%M%S).tar.gz -C /var/www html注意-C /var/www html的写法先切换到/var/www再打包html目录这样解压时不会把多级目录结构带出来。如果是直接tar -czf xxx.tar.gz /var/www/html那么解压后路径会变成var/www/html后面写恢复脚本时会很痛苦。例子21检查网站是否在线#!/bin/bash urlhttps://example.com if curl -s --head --max-time 10 $url | grep HTTP/; then echo 网站可以访问 else echo 网站异常 fi--max-time 10限制10秒超时避免curl卡死。只取响应头比下载整个页面轻量得多检查可用性完全够用。例子22批量ping检测主机#!/bin/bash for host in 192.168.1.1 192.168.1.2 192.168.1.3; do if ping -c 1 -W 1 $host /dev/null; then echo $host 在线 else echo $host 离线 fi done-c 1发1个包-W 1等待1秒。内网环境探测主机非常实用把IP列表换成文件逐行读取就是一套小型监控工具。例子23读取用户输入做交互菜单#!/bin/bash echo 请选择操作 echo 1) 备份 echo 2) 清理临时文件 echo 3) 退出 read -p 输入数字 choice case $choice in 1) echo 执行备份 ;; 2) echo 执行清理 ;; 3) echo 再见 exit 0 ;; *) echo 无效输入 exit 1 ;; esacread -p带提示符读取用户输入。case语法注意每个分支以)开头以;;结束*)是兜底分支。例子24脚本自动创建用户#!/bin/bash usernamedevuser if id $username /dev/null; then echo 用户已存在 else useradd -m -s /bin/bash $username echo 已创建用户 $username fiid $username /dev/null把成功和失败的输出都丢弃只关心返回码。为脚本用户名设置默认值再用参数覆盖是很常见的模式。例子25检查脚本是否有root权限#!/bin/bash if [ $(id -u) -ne 0 ]; then echo 请用root运行此脚本 exit 1 fi echo 当前是root继续执行id -u返回用户IDroot是0。这个检查放在脚本开头能避免一堆半路报错。4. 运行脚本时的常见问题与排查工具4.1 chmod后还是报Permission denied八成是路径问题我自己被这个问题坑过很多次所以先拿出来说。脚本明明加了执行权限但运行./myscript.sh还是提示Permission denied有这么几种可能脚本所在的文件系统以noexec挂载比如某些/home或/tmp。检查方式mount | grep noexec脚本的第一行(shebang)指向的解释器路径不对比如写了#!/bin/bash但系统里bash不在/bin而在/usr/bin。用which bash确认一下文件没有正确的执行权限ls -l看看是不是-rw-r--r--如果是chmod x即可解决路径问题还有个通用办法不用直接执行改用bash script.sh。这样不需要执行权限只要有读权限就能跑。调试阶段我经常这么干省得反复chmod。4.2 bash -x、set -e这些调试开关怎么用脚本运行不如预期时第一件事不是盯着代码看而是用调试模式跑一遍bash -x script.sh-x会在每条命令执行前把命令打印到标准错误前面带号。配合PS4变量自定义前缀可以把行号打出来PS4${BASH_SOURCE}:${LINENO}: bash -x script.sh这样能瞬间定位到具体行。另外几个常用开关set -e脚本中任何一条命令失败立刻退出脚本避免带病继续执行set -u使用未定义的变量直接报错而不是当空字符串处理set -x和在脚本内部开bash -x等效set -o pipefail管道命令中只要一个命令失败整个管道返回失败我通常写脚本时正式环境会加set -euo pipefail三件套但在调试写一半的脚本时会暂时去掉因为可能一条命令没准备好整个脚本全挂。生产脚本必须有这是底线。4.3 if语句的常见翻车现场bash的if语句经常有人翻车比如这样if [ $var yes ]; then echo 相等 fi如果$var是空的命令展开后变成[ yes ]直接报语法错误。解决方案是给变量加双引号if [ $var yes ]; then还有比较数字用-eq比较字符串用这是两个体系混用会报错。更推荐用[[ ]]替代[ ]双中括号是bash关键字行为更稳定if [[ $var yes ]]; then echo 相等 fi在[[ ]]里也能正常用。而且[[ ]]支持正则、模式匹配越来越成为主流写法。注意[[ ]]不是所有sh都支持但既然用了bash就不需要纠结这一点。5. 让脚本更健壮学完例子后进阶的3个习惯5.1 引用参数防空格分裂看过很多脚本因为没加引号导致文件路径带空格时代码崩溃。比如复制文件cp $source $backup如果$source的值是/data/my file.txtbash会把路径拆成两个词结果复制失败。几乎所有涉及变量的场景加双引号都是更安全的cp $source $backup而$代表所有参数时建议用$而不是$*前者能保留每个参数的边界。我用一个真实例子说明假设文件名是a b.txt脚本里用$传给cat能正确读取用$*就会爆炸。5.2 函数化减少重复25个例子全是平铺直叙但实际生产脚本超过50行后最好把逻辑拆成函数。这是我推荐的骨架#!/bin/bash log() { echo [$(date %Y-%m-%d %H:%M:%S)] $* } check_disk() { local threshold$1 df -h | awk -v t$threshold NR1 {gsub(%,,$5); if($50 t0) print $0} } main() { log 开始巡检 check_disk 85 log 巡检结束 } main $函数的好处是每个模块可以单独复用到别的脚本里。local声明局部变量防止变量污染。脚本从上到下先定义函数最后统一在main里调用结构一目了然。5.3 日志和错误处理脚本写完能跑只是及格上线跑起来不出幺蛾子才叫合格。我给生产脚本统一加三样东西第一日志函数统一记录到指定文件log() { echo $(date %F %T) $* /var/log/myscript.log }第二关键步骤的错误检查if ! cp $source $backup; then log 备份复制失败 exit 1 fi第三临时文件去重。如果你用了$$生成PID做临时文件名记得在脚本结束用trap清理tmpfile/tmp/myscript_$$.tmp trap rm -f $tmpfile EXITtrap ... EXIT保证脚本正常退出或异常退出都会执行清理临时垃圾文件是脚本最常见的残留物。6. 写在最后的一些实在话写了这么多年脚本最深的体会是bash脚本的门槛不在于语法难而在于细心。一个空格、一个引号、一个换行符都可能让脚本行为完全不同。上面这些例子都是我从真实需求里抽出来的你可以直接复制运行改一改路径和参数就能用。但在部署到生产环境前务必先在测试目录跑一遍加上set -euo pipefail再用bash -x跟踪一遍逻辑。最后分享一个我自己坚持的习惯脚本一定要写版本注释。在文件头部加# Version: 1.0和# Last modified: 2025-06-14当哪天发现脚本行为不对能快速追溯到改动历史。别嫌麻烦等脚本变成几十行、上百行被同事改了三轮之后你会感谢这几行注释。
返回列表