【Linux运维极简教程】08-Shell脚本编程入门

📅 2026/7/15 21:50:02
【Linux运维极简教程】08-Shell脚本编程入门
08 - Shell 脚本编程入门一、Shell 脚本基础1.1 第一个脚本#!/bin/bash# 这是注释echoHello, World!echo当前用户$(whoami)echo当前时间$(date%Y-%m-%d %H:%M:%S)#!/bin/bash称为shebang指定解释器#开头为注释echo输出内容$()执行命令并获取输出1.2 执行脚本# 方法一添加执行权限后执行chmodx script.sh ./script.sh# 方法二用解释器执行不需要执行权限bashscript.sh# 方法三source 执行在当前 Shell 中执行变量不释放sourcescript.sh.script.sh# 等同于 source1.3 脚本规范#!/bin/bash## 脚本名称backup.sh# 功能描述备份指定目录# 作者运维团队# 创建日期2024-01-15# 使用方法./backup.sh 源目录 目标目录#set-e# 遇到错误即退出set-u# 使用未定义变量报错set-opipefail# 管道中任一命令失败即失败# 或合并写set -euo pipefail二、变量2.1 变量定义与使用# 变量定义等号两边不能有空格namealiceage25PI3.14159# 使用变量echo$nameecho我的名字是${name}# 推荐加花括号# 命令替换current_date$(date%Y%m%d)# 推荐写法current_datedate%Y%m%d# 反引号写法不推荐# 只读变量readonlyHOSTNAMEweb-01# 删除变量unsetname2.2 特殊变量变量含义$0脚本名称$1~$9第 1~9 个参数${10}第 10 个及以后的参数$#参数个数$*所有参数作为单个字符串$所有参数每个参数独立$?上一条命令的退出状态码$$当前脚本的 PID$!最近一个后台进程的 PID#!/bin/bashecho脚本名称$0echo参数个数$#echo第一个参数$1echo所有参数$echo上一条命令状态$?2.3 字符串操作strHello World# 字符串长度echo${#str}# 11# 子字符串从位置 0 开始取 5 个字符echo${str:0:5}# Hello# 字符串替换filebackup.tar.gzecho${file%.gz}# 去掉后缀backup.tarecho${file%%.*}# 去掉最长匹配backupecho${file#backup.}# 去掉前缀tar.gzecho${file##*.}# 取后缀gzecho${file/backup/restore}# 替换第一个匹配restore.tar.gzecho${file//a/A}# 替换所有匹配bAckup.tAr.gz2.4 默认值# 如果 var 为空使用默认值不赋值echo${var:-default}# 如果 var 为空赋值并使用默认值echo${var:default}# 如果 var 为空报错退出echo${var:?var is not set}三、条件判断3.1 if 语句# 基本格式if[$age-ge18];thenecho成年人elif[$age-ge13];thenecho青少年elseecho儿童fi3.2 条件测试# 数值比较[$a-eq$b]# 等于[$a-ne$b]# 不等于[$a-gt$b]# 大于[$a-lt$b]# 小于[$a-ge$b]# 大于等于[$a-le$b]# 小于等于# 字符串比较[$str1$str2]# 相等[$str1!$str2]# 不相等[-z$str]# 为空[-n$str]# 非空# 文件测试[-ffile]# 存在且是普通文件[-ddir]# 存在且是目录[-epath]# 存在[-rfile]# 可读[-wfile]# 可写[-xfile]# 可执行[-sfile]# 存在且非空[file1-ntfile2]# file1 比 file2 新[file1-otfile2]# file1 比 file2 旧# 逻辑运算[$a-gt0][$a-lt100]# 与[$a-lt0]||[$a-gt100]# 或[!-ffile]# 非# 推荐使用 [[ ]]bash 扩展支持通配符和正则[[$strh*]]# 通配符匹配[[$str~^[0-9]$]]# 正则匹配3.3 case 语句#!/bin/bashread-p请输入服务名: servicecase$serviceinnginx|httpd)echoWeb 服务器systemctl status$service;;mysql|mariadb)echo数据库服务systemctl status mysqld;;*)echo未知服务:$serviceecho可选: nginx, httpd, mysql, mariadb;;esac四、循环4.1 for 循环# 列表遍历foriin12345;doecho数字:$idone# 范围遍历foriin{1..10};doecho数字:$idoneforiin{1..10..2};do# 步长为 2echo数字:$idone# 遍历命令输出forfilein/etc/*.conf;doecho配置文件:$filedone# C 语言风格for((i0;i10;i));doecho索引:$idone# 遍历数组servers(web01web02db01)forserverin${servers[]};doecho服务器:$serverdone4.2 while 循环# 基本循环count0while[$count-lt5];doecho计数:$count((count))done# 读取文件whilereadline;doecho行内容:$linedone/etc/passwd# 无限循环whiletrue;doecho运行中...sleep5done4.3 循环控制# break - 跳出循环foriin{1..10};doif[$i-eq5];thenbreakfiecho$idone# continue - 跳过本次foriin{1..10};doif[$((i%2))-eq0];thencontinuefiecho奇数:$idone五、函数# 定义函数functiongreet(){localname$1# local 变量仅在函数内有效echo你好,$name!return0# 返回状态码0-255}# 调用函数greetalice# 函数返回值通过命令替换获取get_date(){date%Y%m%d}today$(get_date)echo今天是$today实用函数示例# 日志函数log_info(){echo[$(date%Y-%m-%d %H:%M:%S)] [INFO]$1}log_error(){echo[$(date%Y-%m-%d %H:%M:%S)] [ERROR]$12}log_info开始部署log_error配置文件不存在# 检查命令是否存在check_command(){if!command-v$1/dev/null;thenecho错误:$1未安装exit1fi}check_commanddockercheck_commandcurl六、数组# 定义数组fruits(applebananacherry)nums(12345)# 从命令输出创建数组files($(ls/etc/*.conf))# 访问元素echo${fruits[0]}# appleecho${fruits[]}# 所有元素echo${#fruits[]}# 数组长度# 遍历forfruitin${fruits[]};doecho$fruitdone# 添加元素fruits(orange)# 删除元素unsetfruits[1]# 删除索引 1 的元素七、输入输出7.1 用户输入# 读取输入read-p请输入用户名: usernameecho你好,$username# 读取密码不显示输入read-s-p请输入密码: passwordechoecho密码长度:${#password}# 读取多个值read-p输入姓名和年龄: name age# 限时读取read-t10-p10秒内输入: input7.2 重定向# 输出重定向commandfile# 覆盖写入commandfile# 追加写入# 错误重定向command2error.log# 只重定向错误commandall.log21# 标准输出和错误都重定向commandall.log# 同上bash 简写# 输入重定向commandinput.txt# /dev/null 黑洞command/dev/null21# 丢弃所有输出# Here DocumentcatEOF 这是多行文本 变量会被展开:$HOMEEOF# Here Stringgreppattern搜索这段文本八、实战脚本脚本一自动备份#!/bin/bash## 脚本名称auto_backup.sh# 功能备份指定目录保留最近 7 天的备份#set-euopipefailSOURCE_DIR/data/appBACKUP_DIR/backupKEEP_DAYS7DATE$(date%Y%m%d_%H%M%S)BACKUP_FILE${BACKUP_DIR}/app_backup_${DATE}.tar.gzlog(){echo[$(date%Y-%m-%d %H:%M:%S)]$1}# 检查源目录if[!-d$SOURCE_DIR];thenlog错误源目录$SOURCE_DIR不存在exit1fi# 创建备份目录mkdir-p$BACKUP_DIR# 执行备份log开始备份$SOURCE_DIR...tar-czf$BACKUP_FILE-C$(dirname$SOURCE_DIR)$(basename$SOURCE_DIR)if[$?-eq0];thenlog备份成功$BACKUP_FILE($(du-sh$BACKUP_FILE|awk{print $1}))elselog备份失败exit1fi# 清理旧备份log清理${KEEP_DAYS}天前的旧备份...find$BACKUP_DIR-nameapp_backup_*.tar.gz-mtime$KEEP_DAYS-deletelog备份完成脚本二批量检查服务器状态#!/bin/bash## 脚本名称check_servers.sh# 功能批量检查多台服务器的连通性和负载#SERVERS(192.168.1.10192.168.1.11192.168.1.12)PORT22TIMEOUT3echoecho 服务器状态检查 -$(date%Y-%m-%d %H:%M)echoprintf%-16s %-8s %-12s %-10s\nIP地址状态负载响应时间echo------------------------------------------forserverin${SERVERS[]};do# 检查端口ifnc-z-w$TIMEOUT$server$PORT2/dev/null;thenstatus在线# 获取负载通过 SSHload$(ssh-oConnectTimeout$TIMEOUT $servercat /proc/loadavg | awk {print \$1}2/dev/null)load${load:-N/A}# ping 响应时间rtime$(ping-c1-W$TIMEOUT $server2/dev/null|greptime|awk-Ftime{print $2}|awk{print $1})rtime${rtime:-N/A}mselsestatus离线load-rtime-fiprintf%-16s %-8s %-12s %-10s\n$server$status$load$rtimedoneecho脚本三服务部署脚本#!/bin/bash## 脚本名称deploy.sh# 功能部署 Web 应用#set-euopipefailAPP_NAMEwebappAPP_DIR/opt/$APP_NAMEAPP_USERwebappPORT3000VERSION${1:-latest}log(){echo[$(date%H:%M:%S)]$1;}# 前置检查forcmdinnodenpm;docommand-v$cmd/dev/null21||{echo错误:$cmd未安装;exit1;}done# 停止旧服务ifsystemctl is-active--quiet$APP_NAME;thenlog停止旧服务...systemctl stop$APP_NAMEfi# 备份当前版本if[-d$APP_DIR];thenlog备份当前版本...mv$APP_DIR${APP_DIR}_backup_$(date%Y%m%d_%H%M%S)fi# 创建目录mkdir-p$APP_DIRcd$APP_DIR# 拉取代码此处用 git 示例log拉取代码 (版本:$VERSION)...# git clone --branch $VERSION https://github.com/example/webapp.git .# 安装依赖log安装依赖...npminstall--production# 构建项目log构建项目...npmrun build# 配置 systemd 服务log配置系统服务...cat/etc/systemd/system/${APP_NAME}.serviceEOF [Unit] Description$APP_NAMEAfternetwork.target [Service] Typesimple User$APP_USERWorkingDirectory$APP_DIRExecStart/usr/bin/node$APP_DIR/server.js Restarton-failure EnvironmentPORT$PORTEnvironmentNODE_ENVproduction [Install] WantedBymulti-user.target EOFsystemctl daemon-reload systemctlenable$APP_NAMEsystemctl start$APP_NAME# 验证sleep2ifsystemctl is-active--quiet$APP_NAME;thenlog部署成功服务运行在端口$PORTsystemctl status$APP_NAME--no-pagerelselog部署失败journalctl-u$APP_NAME-n20--no-pagerexit1fi九、调试技巧# 方法一bash -x 显示执行的每条命令bash-xscript.sh# 方法二脚本内开启调试set-x# 开启调试setx# 关闭调试# 方法三只调试部分代码set-x# ... 需要调试的代码 ...setx# 方法四语法检查不执行bash-nscript.sh十、小结本篇介绍了 Shell 脚本编程的核心知识变量、条件判断、循环、函数、数组、输入输出重定向以及实战脚本示例。Shell 脚本是运维自动化的基础工具掌握后可以大幅提升工作效率。编写脚本时注意遵循set -euo pipefail规范让脚本更健壮。上一篇07 - 磁盘管理与 LVM下一篇09 - 定时任务与自动化运维