Linux操作系统-shell编程之认识shell脚本
Linux Shell 编程实战让自动化替你“搬砖”引言如果你是一个运维工程师每天可能要重复执行几十次同样的命令——登录服务器、查日志、清缓存、重启服务……这些重复劳动不仅枯燥而且容易出错。如果你是一个开发人员每次部署都要手动敲一串命令环境稍微有点差异就报错排错半天。如果你是一个刚接触 Linux 的新手面对黑乎乎的终端不知道从哪里下手。其实这些问题都有一个共同的解决方案——Shell 脚本。Shell 脚本本质上是 Linux 命令的“批处理”文件。把你平时手动敲的那些命令按顺序写到一个文件里加上一点逻辑判断和循环它就变成了一个自动化工具。一次编写永久复用。可以说Shell脚本是Linux操作系统的精华不懂Shell那就没掌握Linux操作系统运维的精髓。今天这篇文章我们就来系统学习 Linux Shell 编程从基础语法到流程控制从函数封装到调试技巧再到几个拿来就能用的实战案例。读完这篇你就能写出自己的第一个生产级脚本。一、Shell 是什么为什么值得学Shell 是用户与 Linux 内核之间的桥梁。你在终端里敲的每一个命令都是 Shell 在帮你解释并调用内核执行。Shell 不仅仅是一个命令解释器它本身也是一门功能相当强大的编程语言易编写、易调试、灵活性强。Shell 脚本的核心价值在于自动化自动化处理把重复性操作写成脚本一键执行批量管理通过循环结构同时管理数百台设备资源高效低内存占用适合服务器环境灵活定制可组合命令构建复杂逻辑常见的 Shell 类型包括Bash最主流Linux 默认、Zsh、Ksh 等。Bash 凭借强大的脚本功能和广泛的社区支持成为生产环境的首选。一个真实的效率对比手动操作每小时仅能处理 5-10 台服务器而通过 Shell 脚本单台服务器操作时间可压缩至秒级配合循环结构可同时管理数百台设备效率提升数十倍。二、第一个 Shell 脚本Hello World在开始之前我们先写一个最简单的脚本感受一下流程。第一步创建脚本文件vimhello.sh第二步写入内容#!/bin/bash# 这是我的第一个 Shell 脚本echoHello, World!#!/bin/bash是shebang告诉系统用哪个解释器来执行这个脚本#开头的行是注释不会被执行echo是输出命令第三步赋予执行权限并运行chmodx hello.sh ./hello.sh输出Hello, World!小贴士脚本也可以用bash hello.sh或sh hello.sh直接运行无需执行权限。三、变量脚本的“记忆单元”变量是编程语言的基石。Shell 中的变量无需声明类型直接赋值即可使用。3.1 变量定义与引用# 定义变量等号两侧不能有空格nameLinux Shell# 字符串count100# 数字Shell 中默认也是字符串pi3.14# 浮点数# 引用变量加 $ 符号echo$nameecho${name}# 推荐用花括号避免歧义关键规则等号两侧不能有空格变量名不能以数字开头可以使用下划线不能使用破折号或标点符号变量引用需加$符号推荐使用${var}而非$var避免歧义3.2 单引号 vs 双引号字符串可以用单引号或双引号区别很大nameAliceechoHello, $name# 输出Hello, $name原样输出变量不解析echoHello,$name# 输出Hello, Alice变量被解析规则单引号原样输出里面的变量和转义字符都不生效双引号变量会被解析转义字符生效3.3 特殊变量非常重要Shell 提供了一些内置的特殊变量变量含义$0当前脚本的名称$1~$9脚本的第 1~9 个参数$#传递的参数个数$*所有参数作为一个整体字符串$所有参数每个作为独立字符串$?上一条命令的退出状态码0成功非0失败$$当前 Shell 的进程 ID示例#!/bin/bashecho脚本名:$0echo第一个参数:$1echo参数个数:$#echo所有参数:$运行./test.sh hello world# 输出# 脚本名: ./test.sh# 第一个参数: hello# 参数个数: 2# 所有参数: hello world3.4 数组Shell 支持数组下标从 0 开始# 定义数组fruits(applebananaorange)# 访问单个元素echo${fruits[0]}# 输出apple# 访问所有元素echo${fruits[]}# 输出apple banana orange# 获取数组长度echo${#fruits[]}# 输出3# 遍历数组forfruitin${fruits[]};doechoI like$fruitdone四、流程控制让脚本有“脑子”有了变量只能存数据有了流程控制脚本才能做判断、走分支、循环执行——这才是编程语言的灵魂。4.1 if 条件判断if 语句支持数值、字符串、文件状态等多种判断。基本语法if[条件];then# 条件成立时执行elif[另一个条件];then# 另一个条件成立时执行else# 所有条件都不成立时执行fi常见条件测试测试类型运算符含义数值比较-eq等于-ne不等于-gt大于-lt小于-ge大于等于-le小于等于文件测试-f存在且为普通文件-d存在且为目录-e文件存在-x文件可执行字符串相等!不等-z为空实战示例#!/bin/bash# 数值比较a10if[$a-gt5];thenecho$a大于 5fi# 文件存在性检查if[-f/etc/passwd];thenechopasswd 文件存在elseechopasswd 文件不存在fi# 字符串匹配推荐使用 [[ ]]if[[$name*error*]];thenecho包含错误信息fi推荐使用[[ ]]替代[ ]前者支持更丰富的模式匹配如通配符*。4.2 case 多分支选择当有多个分支时case比多个if-elif更清晰#!/bin/bashcase$1instart)echo启动服务...;;stop)echo停止服务...;;restart)echo重启服务...;;*)echo用法:$0{start|stop|restart};;esac4.3 for 循环for 循环用于遍历列表或范围# 遍历列表forfileinlog1.txt log2.txt log3.txt;doecho处理文件:$filedone# 遍历数组files(log1.txtlog2.txtlog3.txt)forfilein${files[]};doecho处理文件:$filedone# 数字序列循环foriin{1..5};doecho第$i次循环done# C 语言风格for((i1;i5;i));doecho第$i次循环done实战批量重命名文件# 将所有 .txt 文件重命名为 .backupforfilein*.txt;domv$file${file%.txt}.backupdone4.4 while 循环while 循环适合不确定次数的场景条件为真时持续执行#!/bin/bashcount5while[$count-gt0];doecho剩余次数:$countcount$((count-1))done4.5 until 循环until 和 while 相反条件为假时执行直到条件变为真count1until[$count-gt5];doecho计数:$countcount$((count1))done4.6 循环控制break 和 continue# break跳出整个循环foriin{1..10};doif[$i-eq5];thenbreakfiecho$idone# 输出1 2 3 4# continue跳过本次循环foriin{1..5};doif[$i-eq3];thencontinuefiecho$idone# 输出1 2 4 5五、函数让代码可复用函数是模块化编程的核心。把重复的逻辑封装成函数脚本更清晰、更易维护。5.1 定义与调用# 定义函数两种写法等价functiongreet(){echoHello,$1!}# 或者greet(){echoHello,$1!}# 调用函数不加括号greetWorld# 输出Hello, World!5.2 函数参数与返回值函数通过$1、$2等位置变量接收参数。返回值用return指定0-255$?获取#!/bin/bash# 带参数的函数backup_file(){localsrc$1localdest$2cp$src$destecho备份成功:$dest||echo备份失败}# 带返回值的函数check_number(){if[$1-gt0];thenreturn0# 表示成功/真elsereturn1# 表示失败/假fi}# 调用backup_file/var/log/syslog/backup/syslog.logcheck_number5if[$?-eq0];thenecho是正数fi5.3 局部变量用local关键字声明的变量只在函数内部有效不会污染全局#!/bin/bashmy_func(){localvar我是局部变量echo函数内:$var}my_funcecho函数外:$var# 输出为空因为 var 是局部变量六、调试技巧让 Bug 无处遁形Shell 脚本的调试是新手最容易卡住的地方。下面介绍几种实用的调试方法。6.1 echo 大法最基础在关键位置打印变量值是最简单直接的调试方式#!/bin/bashnameAliceechoDEBUG: name$name# 打印变量值6.2 set -x跟踪执行最推荐set -x会让 Shell 在执行每条命令前把命令打印出来#!/bin/bashset-x# 开启调试模式nameAliceechoHello,$namesetx# 关闭调试模式输出会显示每一条命令及其展开后的结果非常适合定位逻辑错误。6.3 set -e遇错即停默认情况下脚本中某条命令失败后会继续执行可能导致更严重的连锁问题。set -e让脚本在遇到任何错误时立即退出#!/bin/bashset-e# 任何命令失败就退出rm/nonexistent/file# 这行会报错脚本立即退出echo这行不会执行6.4 组合技set -euxo pipefail这是生产环境脚本的黄金组合#!/bin/bashset-euxopipefail各选项含义-e命令失败立即退出-u使用未定义变量时报错-x打印执行的每条命令-o pipefail管道中任何命令失败都算失败6.5 trap捕获错误与信号trap可以在脚本被中断或出错时执行清理操作#!/bin/bashcleanup(){echo脚本被终止执行清理...rm-f/tmp/temp_file}# 捕获 CtrlC 和脚本错误trapcleanup SIGINT SIGTERM ERR# 正常逻辑...6.6 语法检查bash -n不执行脚本仅检查语法错误bash-nscript.sh七、实战案例拿来就能用案例一批量检查服务器存活#!/bin/bash# check_servers.sh - 批量 Ping 检测服务器存活servers(192.168.1.1192.168.1.2192.168.1.3)foripin${servers[]};doifping-c1-W1$ip/dev/null21;thenecho✅$ip存活elseecho❌$ip不可达fidone案例二Nginx 服务管理脚本#!/bin/bash# nginx_ctl.sh - Nginx 服务管理NGINX_HOME/usr/local/nginxNGINX_PID$NGINX_HOME/logs/nginx.pidstart(){if[-f$NGINX_PID]kill-0$(cat$NGINX_PID)2/dev/null;thenechoNginx 已经在运行else$NGINX_HOME/sbin/nginxechoNginx 启动成功fi}stop(){if[-f$NGINX_PID];thenkill-QUIT$(cat$NGINX_PID)echoNginx 已停止elseechoNginx 未运行fi}reload(){if[-f$NGINX_PID];thenkill-HUP$(cat$NGINX_PID)echoNginx 配置已重载elseechoNginx 未运行fi}case$1instart)start;;stop)stop;;reload)reload;;restart)stopsleep1start;;*)echo用法:$0{start|stop|restart|reload};;esac案例三磁盘使用率监控告警#!/bin/bash# disk_monitor.sh - 磁盘使用率监控THRESHOLD80EMAILadminexample.comdf-h|grep-vE^Filesystem|tmpfs|devtmpfs|whilereadline;dousage$(echo$line|awk{print $5}|seds/%//)mount$(echo$line|awk{print $6})if[$usage-gt$THRESHOLD];thenecho警告:$mount使用率已达$usage%|mail-s磁盘告警$EMAILfidone八、生产环境最佳实践8.1 脚本头部模板#!/bin/bash#-------------------------------------------------------------------# 脚本名称: xxx.sh# 功能描述: xxx# 作者: xxx# 版本: 1.0# 创建日期: $(date %Y-%m-%d)#-------------------------------------------------------------------set-euopipefail8.2 使用绝对路径在脚本中尽量使用绝对路径避免因 PATH 环境变量不同导致命令找不到。8.3 变量加引号变量引用时始终加双引号避免空格或特殊字符导致意外解析# ❌ 不推荐file$1rm$file# ✅ 推荐file$1rm$file8.4 检查命令是否存在if!command-vnginx/dev/null;thenechonginx 命令不存在请先安装exit1fi8.5 函数封装避免重复重复的逻辑封装成函数提高可维护性。8.6 日志记录log(){echo[$(date%Y-%m-%d %H:%M:%S)]$*/var/log/my_script.log}log脚本开始执行总结Shell 编程是 Linux 运维和自动化领域的核心技能。从基础语法到流程控制从函数封装到调试技巧掌握这些知识你就能把日常的重复劳动交给脚本去完成。记住三句话Shell 是人与系统交互的桥梁掌握它你就掌握了 Linux 的“遥控器”。脚本的本质是命令的自动化组合把重复的事写成脚本让机器替你“搬砖”。先能跑再优化不要追求一开始就写出完美的脚本先写出能用的再逐步完善。Shell 脚本的威力在于——一次编写永久复用一处编写处处运行。希望这篇文章能帮你迈出 Shell 编程的第一步写出属于你自己的自动化工具本篇文章主要是让读者对shell编程有一个大致的认识和了解后续将带您更加深入的学习shell编程的相关知识如果你有任何问题或经验分享欢迎在评论区留言讨论