ARTICLE DETAIL

资讯详情

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

Shell脚本实战:从变量到三剑客,构建自动化运维工具箱

Shell脚本实战:从变量到三剑客,构建自动化运维工具箱 这次我们来看一个面向运维和开发初学者的 Shell 脚本实战教程。Shell 脚本是 Linux/Unix 系统自动化的基石无论是日常文件处理、批量任务调度还是复杂的系统监控和部署都离不开它。很多初学者觉得 Shell 语法琐碎但掌握核心的变量、循环、函数和文本处理“三剑客”grep、sed、awk就能解决 80% 的自动化需求。这篇文章不讲空泛的理论直接带你从零开始通过实战案例构建一套可复用的脚本工具箱。我们将重点关注 Shell 脚本的“实战能力”如何定义变量避免踩坑如何用循环高效处理批量任务如何用函数封装可复用的逻辑以及如何用“三剑客”精准处理文本数据。对于运维工程师和开发者来说这套技能是提升效率、实现自动化运维的必备武器。本文会提供大量可直接运行的代码示例并附上关键的风险提示和调试技巧确保你学完就能用。1. 核心能力速览能力项说明学习目标掌握 Shell 脚本核心语法能够独立编写自动化脚本解决实际问题。核心知识点变量定义与引用、流程控制条件判断、循环、函数封装、文本处理三剑客grep, sed, awk。硬件/环境门槛极低。任何安装 Linux 发行版如 Ubuntu, CentOS或 macOS 的机器即可Windows 可通过 WSL 或 Git Bash 运行。无需独立显卡或高算力。启动/运行方式通过终端Terminal直接执行脚本文件。主要功能文件批量操作、日志分析、系统监控、数据清洗、定时任务调度、自动化部署等。适合场景Linux 系统日常运维、后端服务部署与监控、CI/CD 流水线中的脚本任务、数据处理自动化。关键风险提示路径空格、未引用的变量、权限问题、脚本注入风险、循环中的意外无限循环。2. 适用场景与使用边界Shell 脚本最适合解决那些重复、有规律、需要与操作系统底层文件、进程、网络打交道的任务。它非常适合以下场景批量文件操作重命名大量文件、批量转换格式、清理过期日志。日志分析与监控定时抓取日志关键词统计错误次数并发送告警。系统健康检查一键检查磁盘空间、内存使用率、服务状态并生成报告。自动化部署配合 Git、Docker 等工具完成代码拉取、构建、重启服务等系列操作。数据预处理对 CSV、日志等文本文件进行过滤、提取、格式化。它的使用边界也很明确不适合复杂计算对于密集的数学运算、复杂数据结构处理应使用 Python、Java 等高级语言。不适合图形界面Shell 脚本本质是命令行工具不擅长构建 GUI 应用。谨慎处理用户输入脚本中直接使用未经验证的参数如$1可能存在命令注入风险。注意跨平台兼容性不同 Linux 发行版或不同 Shellbash, zsh, sh之间语法可能有细微差异写通用脚本时需测试。安全与合规提醒脚本通常运行在具有较高权限的环境下。务必避免在脚本中硬编码敏感信息如密码、密钥应使用环境变量或配置文件。对于处理用户数据的脚本要确保操作符合隐私政策。3. 环境准备与前置条件开始之前只需要一个能运行 Shell 的环境。绝大多数 Linux 发行版和 macOS 都默认安装了bash。基础环境检查打开终端在 Linux 或 macOS 中搜索“Terminal”。在 Windows 上建议安装 Windows Subsystem for Linux (WSL) 或使用 Git Bash。检查 Shell 类型通常默认是bash。echo $SHELL # 输出类似 /bin/bash创建学习目录避免在系统目录胡乱操作。mkdir -p ~/shell_learning cd ~/shell_learning文本编辑器推荐使用 VSCode、Vim 或 Nano 来编写脚本。关键概念准备脚本文件以.sh为扩展名的文本文件第一行通常是#!/bin/bash称为 shebang用于指定解释器。执行权限脚本文件必须具有可执行权限 (x) 才能直接运行。运行方式bash script.sh直接指定解释器运行无需执行权限。./script.sh需要先给脚本添加执行权限 (chmod x script.sh)然后通过路径执行。4. 变量脚本的“记忆单元”与常见坑变量是存储数据的容器。Shell 中定义和使用变量看似简单但隐藏着不少“坑”。4.1 变量的定义与引用#!/bin/bash # 定义变量等号两边不能有空格 nameShell Learner count100 files_list$(ls ~) # 将命令执行结果赋值给变量 # 引用变量建议始终用双引号包裹防止字符串分割和通配符扩展。 echo Hello, $name # 正确双引号内变量会被替换 echo Total count: ${count} # 使用大括号明确变量边界推荐 echo Files at home: $files_list # 错误示例 wrong value # 等号两边有空格这是错误的 echo $name # 简单情况可以但如果变量值包含空格或特殊字符会出问题。核心要点赋值无空格VARvalue不是VAR value。引用加引号总是使用$VAR或${VAR}避免因变量值中的空格导致命令参数被错误拆分。命令替换使用$(command)或反引号command将命令输出存入变量。4.2 环境变量、局部变量与特殊变量#!/bin/bash # 局部变量仅在当前脚本或函数中有效 local_varIm local # 导出为环境变量子进程如脚本中调用的其他脚本、命令可以继承 export GLOBAL_VARIm global # 特殊变量 echo 脚本名称: $0 echo 第一个参数: $1 echo 所有参数: $ echo 参数个数: $# echo 上一条命令的退出状态: $? # 0 表示成功非0表示失败 echo 当前进程PID: $$4.3 变量使用的“天坑”与排查结合网络热词中提到的shell中常见坑这里总结几个高频问题问题现象可能原因排查与解决执行./script.sh test但$1为空或不对。脚本没有执行权限或用sh script.sh执行sh可能不是bash对某些语法支持不同。用chmod x script.sh添加权限并用./script.sh执行。用echo $0确认解释器。变量赋值后在循环或函数中值丢失或未改变。可能在子 Shell 中修改变量。管道 、命令替换$(...)或后台执行 会创建子 Shell。脚本报错command not found但命令明明存在。路径中包含空格或特殊字符的变量未被引号包裹被 Shell 拆分了。始终使用双引号$my_path。想检查变量是否为空用了if [ $var ]空变量时报语法错误。[ $var ]在var为空时展开为[ ]语法不正确。将变量用双引号括起来if [ -n $var ](非空) 或if [ -z $var ](为空)。5. 流程控制让脚本学会“判断”与“重复”5.1 条件判断 (if/else)Shell 使用test命令或其别名[ ]、[[ ]]进行条件判断。[[ ]]是 bash 的扩展更安全支持正则匹配。#!/bin/bash read -p 请输入一个数字: num # 使用 [[ ]] 进行数值和字符串比较 if [[ -z $num ]]; then echo 输入不能为空 elif [[ ! $num ~ ^[0-9]$ ]]; then # 正则匹配是否为纯数字 echo 请输入有效的数字 elif (( num 100 )); then # 双括号 (( )) 用于算术比较 echo 数字大于100 elif [[ $num -eq 100 ]]; then # -eq 等于-ne 不等于-gt 大于-lt 小于 echo 数字等于100 else echo 数字小于100 fi # 文件测试 file./test.log if [[ -f $file ]]; then echo $file 是一个普通文件。 elif [[ -d $file ]]; then echo $file 是一个目录。 fi if [[ -r $file -w $file ]]; then echo 并且可读可写。 fi5.2 循环处理批量任务的利器网络热词中shell脚本for循环、while循环搜索量很高这是自动化批量操作的核心。for 循环遍历列表或范围#!/bin/bash # 遍历值列表 for fruit in apple banana orange; do echo 水果: $fruit done # 遍历当前目录下所有 .txt 文件 for file in *.txt; do # 一定要判断文件是否存在因为如果*.txt匹配不到file的值就是*.txt这个字符串 if [[ -f $file ]]; then echo 处理文件: $file # 例如重命名在文件名前加前缀 mv $file backup_$file fi done # 类似 C 语言的循环 for (( i0; i5; i )); do echo 数字: $i donewhile 循环条件满足时持续执行#!/bin/bash # 读取文件每一行 while IFS read -r line; do # IFS 防止行首尾空格被修剪-r 防止反斜杠转义 echo 行内容: $line done /etc/passwd # 输入重定向将文件内容传递给while循环 # 无限循环直到条件满足 count0 while true; do ((count)) echo 等待第 $count 秒... if (( count 5 )); then echo 等待结束 break # 跳出循环 fi sleep 1 doneuntil 循环条件为假时执行#!/bin/bash # 直到某个服务端口监听成功 until nc -z localhost 8080; do echo 端口 8080 未就绪等待... sleep 2 done echo 服务已启动循环中的关键陷阱处理带空格的文件名必须在变量引用上加双引号如$file。避免意外无限循环确保循环条件最终会改变尤其是使用while true时必须有break逻辑。管道与循环echo a\nb\nc | while read line; do ... done循环体在子 Shell 中修改外部变量无效。6. 函数封装可复用的代码块函数让脚本模块化更易维护和调试。#!/bin/bash # 函数定义 function log_info() { local timestamp$(date %Y-%m-%d %H:%M:%S) # local 关键字声明局部变量 echo [INFO] $timestamp - $1 # $1 是函数的第一个参数 } # 另一种定义方式 error_exit() { log_info ERROR: $1 exit 1 # 退出脚本并返回状态码1表示错误 } # 函数调用 log_info 脚本开始执行。 # 带返回值的函数 check_disk_usage() { local usage$(df / | tail -1 | awk {print $5} | sed s/%//) echo $usage # 通过 echo 输出返回值而不是 return } current_usage$(check_disk_usage) # 命令替换捕获函数输出 log_info 根分区使用率: ${current_usage}% if [[ current_usage -gt 90 ]]; then error_exit 磁盘空间不足 fi log_info 脚本执行完毕。函数最佳实践使用local声明局部变量避免污染全局命名空间。函数名清晰使用动词-名词形式如create_backup,send_alert。通过echo输出结果用命令替换$(...)捕获使用return仅返回退出状态码0-255。在函数开头进行参数校验避免后续命令因参数缺失而失败。7. 文本处理“三剑客”grep, sed, awk这是 Shell 脚本处理文本数据的核心武器也是运维面试常考点。7.1 grep全局搜索正则表达式并打印用于在文件或流中查找匹配模式的行。# 在 nginx.log 中查找包含 “404” 的行 grep 404 nginx.log # 显示不匹配的行 (-v) grep -v GET nginx.log # 忽略大小写 (-i) grep -i error system.log # 显示匹配行的行号 (-n) grep -n exception app.log # 递归搜索目录下所有文件 (-r) grep -r TODO /path/to/project/ # 使用扩展正则表达式 (-E)匹配多个模式 grep -E (ERROR|FATAL) logfile7.2 sed流编辑器用于过滤和转换文本擅长对文本行进行替换、删除、插入等操作。# 将文件中的 “foo” 全部替换为 “bar”并输出到屏幕 sed s/foo/bar/g input.txt # 直接修改原文件 (-i)备份原文件为 input.txt.bak sed -i.bak s/foo/bar/g input.txt # 删除包含 “debug” 的行 sed /debug/d input.txt # 删除第 10 到 20 行 sed 10,20d input.txt # 在每一行行首添加注释 “# ” sed s/^/# / config.txt # 处理变量替换使用双引号 old_strhello new_strworld sed s/$old_str/$new_str/g file.txt7.3 awk强大的文本分析工具支持编程将文件视为由字段组成的记录非常适合处理表格数据如 CSV、日志。# 打印 /etc/passwd 文件的第一列用户名和第三列用户ID awk -F: {print $1, $3} /etc/passwd # -F: 指定冒号为字段分隔符 # 打印文件行数 (NR) 和列数 (NF) awk {print Line, NR, has, NF, fields.} data.txt # 条件处理打印第二列大于 100 的行 awk $2 100 {print $0} data.csv # 计算第一列数值的总和 awk {sum $1} END {print Total:, sum} numbers.txt # 使用 BEGIN 块初始化处理多个文件 awk BEGIN {FS,; OFS\t} {print $1, $3} file1.csv file2.csv“三剑客”组合使用示例分析 Nginx 访问日志统计每个 IP 的访问次数并按次数降序排列。# 假设日志格式包含客户端IP在每行开头 awk {print $1} nginx.log | sort | uniq -c | sort -rn | head -10 # 分解 # 1. awk {print $1} 提取第一列IP地址 # 2. sort 排序为 uniq 做准备 # 3. uniq -c 统计重复行次数 # 4. sort -rn 按数字(-n)逆序(-r)排序 # 5. head -10 显示前10行8. 实战案例构建一个系统监控脚本现在我们将变量、循环、函数和文本处理结合起来写一个实用的系统监控脚本。#!/bin/bash # sys_monitor.sh - 简易系统监控脚本 LOG_FILE./system_monitor.log ALERT_THRESHOLD_CPU80 ALERT_THRESHOLD_MEM85 ALERT_THRESHOLD_DISK90 # 函数记录日志 log_message() { echo $(date %Y-%m-%d %H:%M:%S) - $1 | tee -a $LOG_FILE } # 函数检查 CPU 使用率 (Linux) check_cpu_usage() { local cpu_usage # 使用 top 命令获取最近1分钟的负载解析出CPU空闲百分比然后计算使用率 cpu_usage$(top -bn1 | grep Cpu(s) | awk {print 100 - $8}) local cpu_usage_int${cpu_usage%.*} # 取整数部分 log_message CPU 使用率: ${cpu_usage}% if [[ $cpu_usage_int -gt $ALERT_THRESHOLD_CPU ]]; then log_message 警告CPU 使用率超过 ${ALERT_THRESHOLD_CPU}% # 这里可以集成邮件、钉钉、企业微信等告警 # send_alert CPU High Usage: ${cpu_usage}% fi } # 函数检查内存使用率 (Linux) check_mem_usage() { local mem_info mem_info$(free | grep Mem) local total_mem$(echo $mem_info | awk {print $2}) local used_mem$(echo $mem_info | awk {print $3}) local mem_usage$(( used_mem * 100 / total_mem )) log_message 内存使用率: ${mem_usage}% if [[ $mem_usage -gt $ALERT_THRESHOLD_MEM ]]; then log_message 警告内存使用率超过 ${ALERT_THRESHOLD_MEM}% fi } # 函数检查磁盘使用率 check_disk_usage() { # 检查根分区 / local disk_usage disk_usage$(df / | tail -1 | awk {print $5} | sed s/%//) log_message 根分区磁盘使用率: ${disk_usage}% if [[ $disk_usage -gt $ALERT_THRESHOLD_DISK ]]; then log_message 警告磁盘使用率超过 ${ALERT_THRESHOLD_DISK}% fi } # 函数检查指定进程是否存在 check_process() { local process_name$1 if pgrep -x $process_name /dev/null; then log_message 进程 $process_name 正在运行。 else log_message 错误进程 $process_name 未运行 fi } # 主函数 main() { log_message 系统监控开始 check_cpu_usage check_mem_usage check_disk_usage check_process nginx # 示例检查 nginx 进程 # check_process mysql log_message 系统监控结束 echo } # 执行主函数 main如何运行与测试将上述代码保存为sys_monitor.sh。赋予执行权限chmod x sys_monitor.sh。运行脚本./sys_monitor.sh。查看日志cat system_monitor.log。你可以通过crontab设置定时任务让这个脚本每隔 5 分钟或 1 小时自动运行一次实现自动化监控。# 编辑当前用户的 crontab crontab -e # 添加一行每5分钟运行一次请使用脚本的绝对路径 */5 * * * * /home/yourname/shell_learning/sys_monitor.sh9. 脚本调试与错误处理编写脚本难免出错掌握调试技巧至关重要。1. 启用调试模式bash -x script.sh在运行前加上-x参数会打印出脚本执行的每一行命令及其展开后的参数非常适合追踪变量值和执行流程。在脚本内部启用在脚本开头添加set -x结尾添加set x。2. 使用set -euo pipefail强烈推荐在脚本开头加上这行“安全咒语”可以大幅提升脚本的健壮性。#!/bin/bash set -euo pipefail # set -e: 任何命令失败返回非零状态就立即退出脚本。 # set -u: 遇到未定义的变量时视为错误。 # set -o pipefail: 管道中任何一个命令失败整个管道返回失败状态。3. 手动打印调试信息在关键位置使用echo或printf输出变量值。echo DEBUG: 变量 name 的值为: $name4. 检查命令退出状态$?在执行可能失败的命令后立即检查其状态。cp important.txt backup/ if [[ $? -ne 0 ]]; then echo 备份失败 exit 1 fi # 或者更简洁的写法 if ! cp important.txt backup/; then echo 备份失败 exit 1 fi10. 最佳实践与安全建议脚本开头务必包含 shebang (#!/bin/bash) 和set -euo pipefail。变量使用总是用双引号引用变量如$var使用${var}明确边界。路径处理对文件路径变量使用双引号并考虑使用realpath或dirname/basename处理相对路径。输入验证对所有外部输入如脚本参数、用户输入、文件内容进行验证和清理防止命令注入。错误处理预测可能失败的点如文件不存在、网络不通并提供友好的错误信息和退出码。日志记录重要的操作和错误信息应记录到日志文件便于事后排查。代码风格使用有意义的变量名和函数名添加必要的注释保持代码缩进。权限最小化不要用 root 权限运行所有脚本。只为必要的操作申请 sudo。敏感信息切勿在脚本中硬编码密码、API密钥。使用环境变量或加密的配置文件。测试在非生产环境充分测试脚本特别是涉及删除 (rm)、移动 (mv) 大量文件的操作。从变量定义到循环控制再到函数封装和强大的文本处理Shell 脚本的入门核心就是这些。真正的熟练来自于实践。建议你从身边的小任务开始写一个自动备份目录的脚本一个清理指定天数前日志的脚本或者一个批量下载网页图片的脚本。在解决实际问题的过程中你会遇到各种“坑”而每一次填坑都是对知识的巩固。把本文的示例代码和排查方法当作手册遇到问题随时回来查阅。当你能够流畅地运用这些工具组合解决运维难题时Shell 脚本就从一门技术变成了你的生产力倍增器。
返回列表