ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Shell脚本入门指南:从零开始掌握自动化运维核心技能

Shell脚本入门指南:从零开始掌握自动化运维核心技能 1. 为什么必须学 Shell一个运维工程师每天都在用的技能很多刚开始接触 Linux 的同学都会有这样的困惑Linux 命令我会敲不少cd、ls、cp、rm 都挺熟练为什么还要专门学 Shell 脚本这个问题的答案在真实的生产环境中非常清晰。假设你管理着 50 台 Linux 服务器每天要做的事情包括检查磁盘空间、清理过期日志、备份数据库文件、定时同步配置。如果你一台一台登录逐条命令去敲每天光重复操作可能就要耗费两三个小时。而如果你把这些操作写成 Shell 脚本配合 crontab 定时任务所有工作会在每天凌晨自动完成早上打开电脑只需要看一份运行日志即可。Shell 脚本就是这样一个“把命令变成程序”的工具。它不是一门新的编程语言而是把 Linux 命令按逻辑组织起来配合变量、循环、判断、函数等编程能力让服务器自动完成重复性工作。除了提高效率Shell 脚本还是自动化运维的基石。现在企业招聘运维工程师和 DevOps 工程师Shell 几乎是必考技能。不管是写自动化部署脚本、监控脚本、日志采集脚本还是编写容器启动入口脚本都离不开 Shell。即便是开发岗位掌握 Shell 也能让你在处理构建、发布、排查线上问题时更从容。从 CSDN 后台和招聘平台的热搜数据也能看出shell脚本入门、linux常用命令教程、自动化运维 shell这类关键词常年处于高位。这说明 Shell 不是过时的技术而是 Linux 生态中最基础、最持久的能力之一。本文将会从零开始不假设你有任何编程基础逐步讲清楚 Shell 脚本的核心语法、常用命令组合、完整实战案例和常见坑点。读完并动手练习后你就能独立编写自己的第一个自动化运维脚本。2. 环境准备开始前的几个必要检查Shell 脚本的学习和运行必须有一个 Linux 环境。这里不要求你一定购买云服务器本地虚拟机、Windows 下的 WSLWindows Subsystem for Linux都可以。2.1 选择操作系统与 Shell 版本目前市面上主流的 Linux 发行版有两大家族家族代表系统常用软件包管理常见环境Debian 系Ubuntu、Debian、Deepinapt开发测试、个人学习RedHat 系CentOS、Rocky Linux、Alibaba Cloud Linuxyum / dnf企业服务器、生产环境不同发行版之间命令的基本语法一致区别主要在软件包管理器和默认目录结构上。本文中的 Shell 语法和大部分命令在各发行版上是通用、可以直接运行的。Shell 本身也有多种实现常见的有bashBourne Again ShellLinux 默认 Shell功能最全面也是本文主讲的版本。shBourne Shell早期的 Shell在一些脚本中会看到#!/bin/sh。zshmacOS 默认 Shell兼容 bash交互体验更好。dashDebian 系系统中/bin/sh的实际指向体积小、执行快但缺少一些 bash 特性。版本需要根据你的项目实际情况调整。如果你用的是 CentOS 7 或 Ubuntu 18.04 以上的系统默认 bash 版本基本都在 4.x 以上学习本文内容没有任何障碍。2.2 检查当前环境打开终端执行以下命令查看当前 Shell 和版本echo $SHELL bash --version输出示意/bin/bash GNU bash 版本 4.2.46(2)-release (x86_64-redhat-linux-gnu)如果你的系统没有 bash可以使用系统的软件包管理器安装# Debian/Ubuntu sudo apt update sudo apt install -y bash # CentOS/RHEL sudo yum install -y bash2.3 创建实验目录与第一个脚本为了接下来的学习建议单独建立一个目录避免测试文件散落各处mkdir -p ~/shell-lab cd ~/shell-lab然后创建一个最简单的脚本文件vim hello.sh输入以下内容#!/bin/bash echo Hello, Shell!保存退出后有两种执行方式# 方式一直接指定解释器执行不要求脚本有执行权限 bash hello.sh # 方式二给脚本添加执行权限后直接运行 chmod x hello.sh ./hello.sh运行结果Hello, Shell!这里需要注意脚本第一行的#!/bin/bash称为 shebang它告诉系统应该用哪个解释器来执行这个脚本。虽然写不写有时候都能运行但建议每个脚本都写上这样可以避免很多莫名其妙的兼容性问题。3. Shell 核心语法拆解从变量到函数学 Shell 脚本不需要死记硬背“知识点大全”更有效的方式是按“我需要表达什么逻辑”去掌握对应的语法要素。下面我们把 Shell 编程需要的基础语法按功能逐一拆解。3.1 变量命名、赋值、引用与特殊变量Shell 变量和编程语言中的变量类似用来临时存储数据。定义一个变量的语法非常简单namezhangsan num100使用变量时在变量名前加$符号即可echo $name echo 欢迎 $name 登录系统注意一个高频误区等号两边不能有空格。name zhangsan是错误的写法会被 Shell 解析成执行一个叫作name的命令然后传入和zhangsan参数。变量名的规范是由字母、数字、下划线组成不能以数字开头。赋值内容如果包含空格需要用双引号或单引号包裹full_nameZhang San双引号和单引号是有区别的双引号内的$变量名会被解析成变量值。单引号内的内容会原样输出不解析任何变量。namezhangsan echo hello, $name # 输出 hello, zhangsan echo hello, $name # 输出 hello, $name除了自定义变量Shell 还内置了一批位置变量和特殊变量在编写自动化脚本时非常常用变量含义$0当前脚本的文件名$1、$2...脚本的第 1 个、第 2 个参数$#参数的个数$所有参数列表每个参数独立$*所有参数列表整体作为单个字符串$?上一条命令的退出状态码0 表示成功$$当前 Shell 进程的 PID写一个args.sh来验证特殊变量#!/bin/bash echo 脚本名称: $0 echo 第一个参数: $1 echo 参数个数: $# echo 所有参数: $ echo 上条命令退出码: $?运行bash args.sh hello world输出脚本名称: args.sh 第一个参数: hello 参数个数: 2 所有参数: hello world 上条命令退出码: 0另外Shell 中还有shift这个常用命令它可以把参数列表左移一位也就是丢弃掉当前的$1原来的$2变成$1。这个命令在编写需要循环处理大量参数的脚本时非常实用后面实战案例中会用到。3.2 字符串处理截取、替换、判断长度在自动化脚本中字符串处理是最频繁的操作之一例如处理日志文件路径、解析配置项、拼接日期等。字符串截取的基本语法strhello-shell-world echo ${str:0:5} # 从第 0 个字符开始截取 5 个字符hello echo ${str:6} # 从第 6 个字符截取到末尾shell-world echo ${#str} # 字符串长度17字符串替换echo ${str/-/_} # 替换第一个匹配hello_shell-world echo ${str//-/} # 替换全部匹配helloshellworld字符串判断是否为空if [ -z $str ]; then echo str 为空 else echo str 不为空 fi在真实场景中判断变量是否为空的场景非常常见比如检查用户传参是否完整。建议养成习惯变量在比较时加上双引号例如[ -z $var ]而不是[ -z $var ]这样可以避免变量内容为空或包含空格时产生语法错误。3.3 条件判断if、case 与 test条件判断是让脚本拥有“智能逻辑”的基础。Shell 中最常见的是if语句配合[ ]或test命令进行判断。基本结构#!/bin/bash score$1 if [ $score -gt 90 ]; then echo 优秀 elif [ $score -gt 60 ]; then echo 及格 else echo 不及格 fi注意[ ]的左右括号和里面的表达式之间必须有空格否则会报语法错误。为了方便记忆可以把[看作test命令的别名。文件相关的判断是脚本中最高频的操作[ -f file.txt ] # 判断是否为普通文件 [ -d dir ] # 判断是否为目录 [ -e path ] # 判断路径是否存在 [ -r file.txt ] # 判断是否可读 [ -w file.txt ] # 判断是否可写 [ -x file.sh ] # 判断是否可执行数值判断和字符串判断用的运算符也容易混淆判断类型运算符示例数值相等-eq[ $a -eq $b ]数值不等-ne[ $a -ne $b ]数值大于-gt[ $a -gt $b ]数值小于-lt[ $a -lt $b ]字符串相等或[ $s1 $s2 ]字符串不等![ $s1 ! $s2 ]字符串为空-z[ -z $s1 ]字符串非空-n[ -n $s1 ]表示“并且”||表示“或者”。下面这个写法在脚本中很常见if [ -z $1 ] || [ -z $2 ]; then echo 请传入两个参数 exit 1 fi当分支情况比较多时case语句可读性比一连串的if-elif更好#!/bin/bash case $1 in start) echo 启动服务 ;; stop) echo 停止服务 ;; restart|reload) echo 重启服务 ;; *) echo 用法: $0 {start|stop|restart} exit 1 ;; esac*)是默认分支对应其他所有未匹配的情况。每个分支结束时用两个分号;;收尾最后用esaccase 反写结束整个语句。3.4 for 循环、while 循环与实战场景循环是脚本最提效的语法之一。比如批量处理日志文件、批量创建用户、批量重命名文件靠的是循环。for循环最常见的有三种写法。写法一遍历列表for ip in 192.168.1.1 192.168.1.2 192.168.1.3; do echo Ping $ip ping -c 1 $ip /dev/null 21 echo $ip 可达 || echo $ip 不可达 done写法二使用seq生成数字序列for i in $(seq 1 5); do echo 第 $i 次执行 done写法三C 语言风格for ((i1; i5; i)); do echo number: $i donewhile循环同样常用重点是“当满足条件时持续执行”。例如持续检查某个进程是否存在#!/bin/bash counter0 while [ $counter -lt 5 ]; do echo counter$counter counter$((counter 1)) done这里$((...))是 Shell 的算术运算语法也可以简写为((counter))。处理参数列表时for配合$是一种标准模式#!/bin/bash for arg in $; do echo 处理参数: $arg done再结合前面说的shift命令可以在while循环中逐步消费参数#!/bin/bash while [ $# -gt 0 ]; do echo 当前参数: $1 shift done3.5 函数封装复用逻辑当一个脚本开始变得复杂就应该把重复的逻辑封装成函数。Shell 函数定义很灵活但有几个规范建议务必遵守。函数定义的推荐写法是写清函数名和空括号函数体用花括号包住#!/bin/bash # 定义一个日志函数 log_info() { echo $(date %Y-%m-%d %H:%M:%S) [INFO] $1 } log_error() { echo $(date %Y-%m-%d %H:%M:%S) [ERROR] $1 } # 业务函数 check_disk() { local max80 local usage$(df -h / | awk NR2 {print $5} | tr -d %) if [ $usage -gt $max ]; then log_error 根分区使用率已达 $usage% return 1 else log_info 根分区使用率正常$usage% return 0 fi } log_info 开始检查磁盘空间 check_disk if [ $? -eq 0 ]; then echo 磁盘状态正常 else echo 需要处理磁盘空间 fi关于函数的几个关键点local关键字用于声明局部变量。如果不加local函数里定义的变量都是全局的外部也可能被误改。函数通过return返回状态码范围是 0-2550 表示成功非 0 表示失败。如果要返回字符串结果可以用echo输出然后用$(函数名)捕获。函数可以先定义再调用但一定要确保调用时函数已经被定义不能像 C 语言那样声明前置。3.6 常用符号与命令组合的坑Shell 中有几个容易混淆的低频符号在阅读他人脚本时经常遇到符号含义示例;分号命令分隔符不关心前面命令是否成功cd /tmp; ls只有前一条命令成功才执行后一条mkdir test cd test后台执行命令./start.sh 重定向输出到文件覆盖echo 123 a.log重定向输出到文件追加echo 123 a.log21将标准错误重定向到标准输出command a.log 21:Shell 内置的空操作命令永远返回 0while :; do echo ok; done特别提一下:这个命令它不显眼但很实用。前面热词中也出现了: 这是一个 shell 内置的空操作命令。它常用于充当死循环的条件while :占位避免脚本结构不完整if [ -z $1 ]; then :; else echo 有参数; fi调试时临时注释掉某些逻辑又不想破坏结构。4. 自动化运维实战三个可直接使用的脚本案例本章我们进入实战环节。下面三个脚本是我认为 Shell 入门阶段最值得写一遍的案例分别覆盖系统信息采集、批量文件处理和日志清理。案例难度从简单到中等代码可以直接复制到自己的环境中运行。4.1 系统信息采集脚本运维工作第一步往往是了解机器状态磁盘用了多少、内存还够不够、当前负载如何、系统已经运行多久。写一个脚本把这些信息一次性采集出来是很好的练手项目。创建sys_info.sh#!/bin/bash # 描述: 采集 Linux 系统关键信息 # 用法: bash sys_info.sh echo 系统基本信息 echo 主机名: $(hostname) echo 操作系统: $(cat /etc/os-release | grep PRETTY_NAME | cut -d -f2 | tr -d ) echo 内核版本: $(uname -r) echo 运行时间: $(uptime -p) echo echo 硬件资源使用情况 echo CPU 核心数: $(nproc) echo 内存使用情况: free -h | awk NR1 || NR2 echo echo 根分区磁盘使用情况: df -h / | awk NR1 || NR2 echo echo 网络信息 # 获取本机主要 IP排除回环地址 ip -o -4 addr show | awk {print $2, $4} | grep -v ^lo echo echo 关键进程检查 for pkg in sshd crond; do if pgrep -x $pkg /dev/null 21; then echo $pkg 运行中 else echo $pkg 未运行 fi done运行方式bash sys_info.sh运行结果类似 系统基本信息 主机名: vm-master-01 操作系统: CentOS Linux 7 (Core) 内核版本: 3.10.0-1160.el7.x86_64 运行时间: up 25 days, 3 hours 硬件资源使用情况 CPU 核心数: 2 内存使用情况: total used free shared buff/cache available Mem: 1.8G 1.2G 104M 8.5M 535M 459M ... 网络信息 eth0 192.168.1.10/24 关键进程检查 sshd 运行中 crond 未运行这个脚本用到了两个非常重要的小工具awk和grep。awk NR1 || NR2表示只输出第 1 行和第 2 行awk {print $2, $4}表示取每行的第 2 和第 4 列。grep -v表示排除匹配行。这些文本处理命令在 Shell 脚本中与循环、管道搭配使用能解决绝大多数日志与输出解析问题。4.2 批量重命名文件脚本批量重命名文件是高频需求。比如批量把*.txt改为带日期前缀把.log改成.bak或者把文件名中的空格替换成下划线。用 Shell 写一个通用一点的批量重命名脚本是一个很不错的实战练习。创建rename_files.sh#!/bin/bash # 描述: 批量重命名当前目录下的文件 # 用法: bash rename_files.sh 旧关键字 新关键字 if [ $# -ne 2 ]; then echo 用法: $0 旧关键字 新关键字 echo 示例: $0 .log .bak exit 1 fi old_word$1 new_word$2 count0 for file in *$old_word*; do # 防止目录下没有匹配文件时* 被原样输出 if [ -e $file ]; then new_name${file//$old_word/$new_word} mv $file $new_name echo 已重命名: $file - $new_name count$((count 1)) fi done echo 共重命名 $count 个文件测试一下touch a.log b.log c.txt bash rename_files.sh .log .bak输出已重命名: a.log - a.bak 已重命名: b.log - b.bak 共重命名 2 个文件这个脚本有两个细节值得注意第一for file in *$old_word*中的星号是通配符Shell 会把它展开成匹配的文件列表。如果目录下没有匹配文件循环变量会直接拿到一个不存在的路径*.log。因此循环体内先做[ -e $file ]判断是健壮性的关键。第二${file//$old_word/$new_word}是前面语法部分讲过的“全局替换”注意替换内容中如果包含/或特殊字符写法需要调整。这个脚本适用于关键字中不包含斜杠的简单场景。4.3 日志清理与磁盘告警脚本日志清理是最典型的自动化运维场景。应用日志会不断增长如果不清理最终会耗尽磁盘空间。下面这个脚本完成两件事找出指定目录下 N 天前的日志文件并删除。检查磁盘使用率超过阈值则输出告警。创建clean_logs.sh#!/bin/bash # 描述: 清理指定目录下 N 天前的日志文件同时检查磁盘空间 # 用法: bash clean_logs.sh /var/log/myapp 7 log_dir$1 days$2 threshold85 # 参数检查 if [ -z $log_dir ] || [ -z $days ]; then echo 用法: $0 日志目录 保留天数 exit 1 fi if [ ! -d $log_dir ]; then echo 错误: $log_dir 目录不存在 exit 1 fi echo 开始清理 $log_dir 下 $days 天前的日志文件... # 使用 find 命令找到并删除旧日志 deleted$(find $log_dir -name *.log -type f -mtime $days | wc -l) find $log_dir -name *.log -type f -mtime $days -delete echo 已删除 $deleted 个日志文件 # 检查磁盘使用率 disk_usage$(df -h / | awk NR2 {print $5} | tr -d %) echo 当前根分区使用率: $disk_usage% if [ $disk_usage -ge $threshold ]; then echo 警告: 磁盘使用率已超过阈值 $threshold%请及时处理 else echo 磁盘使用率正常。 fi运行示例bash clean_logs.sh /tmp/logs 7预期输出开始清理 /tmp/logs 下 7 天前的日志文件... 已删除 5 个日志文件 当前根分区使用率: 68% 磁盘使用率正常。这里find命令是核心-name *.log指定文件名模式。-type f限定为普通文件避免误删目录。-mtime $days表示文件修改时间在 N 天前。-delete直接删除匹配文件。在生产环境中建议先去掉-delete只打印出匹配的文件列表人工确认后再真正执行删除。这也是所有删除类脚本的通用安全经验先验证再删除先备份再变更。将这个脚本加入 crontab就可以实现每天自动清理crontab -e添加一行0 3 * * * /bin/bash /root/scripts/clean_logs.sh /var/log/myapp 7 /var/log/clean_logs.log 21这行配置表示每天凌晨 3 点执行一次清理脚本输出追加到日志文件中方便观察执行情况。5. 常见问题与排查思路Shell 脚本在运行时报错新手最容易无从下手。下面把最常见的几类问题汇总成表并给出排查思路。问题现象常见原因解决思路./test.sh: 权限不够脚本文件没有可执行权限执行chmod x test.sh或用bash test.sh运行bad interpreter: /bin/bash^M在 Windows 上编辑脚本文件包含回车符使用dos2unix test.sh转换或换用 Linux 编辑器[: missing]if 判断里[ ]两侧没有加空格确保写成[ $var value ]command not found可能拼写错误或环境变量 PATH 未包含命令路径检查命令名是还是空格赋值检查是否用了中文标点变量赋值写成了name xx等号两边不能有空格改成namexx脚本没有输出任何内容可能脚本没有执行权限或 shebang 写错用bash -x script.sh调试运行查看执行过程循环时*被当成普通字符目录下没有匹配的文件循环体内先加[ -e $file ]判断$?获取不到正确退出码中间执行了 echo 等命令覆盖了退出码把退出码先存到变量ret$?日期或变量在单引号内不解析用了单引号包裹改成双引号$varsyntax error near unexpected token脚本语法错误或者 if / for 结构不完整使用bash -n script.sh检查语法除了上面的问题这里额外推荐两个调试技巧。第一个是bash -x逐行跟踪脚本的执行过程常被称为 Shell 脚本调试的“金钥匙”bash -x test.sh它会打印出每条命令及其展开后的真实内容。比如echo $name实际展开为echo zhangsan一眼就能看出变量值是否符合预期。第二个是bash -n只做语法检查不实际执行适合在执行前快速排查bash -n test.sh如果没有语法错误命令不会有任何输出。如果有错误会直接指出出错行号。新手遇到脚本行为不符预期时不要猜先运行bash -x看执行过程再按上面表格对照排查绝大多数问题都能定位。6. Shell 编程最佳实践与工程建议Shell 脚本入门容易但写出“可维护、可排错、可上线”的脚本并不简单。以下经验是在实际运维和开发过程中逐渐总结出来的希望你能从最初就养成这些习惯。6.1 每个脚本都写注释和使用说明脚本是给别人看的也是给三个月后的自己看的。建议开头固定写清脚本功能、使用方式、作者或维护日期。例如#!/bin/bash ########################################################### # 脚本名称: backup_data.sh # 功能: 备份 MySQL 指定数据库到 /data/backup 并压缩 # 用法: bash backup_data.sh 数据库名 # 作者: your_name # 更新: 2025-06-01 ###########################################################写注释的成本很低但在半年后排查问题时价值巨大。6.2 脚本开头设置严谨选项在脚本开头加入下面几个选项可以显著增强脚本健壮性set -e # 遇到任何命令返回非 0 状态时立即退出脚本 set -u # 使用未定义变量时直接报错避免变量名拼写错误 set -o pipefail # 管道命令中只要有一个命令失败整个管道返回失败但这三个选项不一定是所有脚本的黄金标准。set -e在部分循环和判断场景下会“误伤”脚本比如你本来希望某个命令失败后继续执行后续逻辑。建议在编写脚本时理解每个选项的意义按需组合。更稳妥的做法是在确实需要忽略某条命令失败时显式追加|| truecommand_that_may_fail || true6.3 使用变量而不是重复硬编码硬编码路径和参数会大大降低脚本的可移植性。例如不要直接在脚本里写rm -rf /var/log/myapp/*.log而是定义成变量LOG_DIR/var/log/myapp find $LOG_DIR -name *.log -type f -delete这样后续只需要维护变量即可也能防止误操作写错目录。6.4 删除和变更类命令要加保护涉及rm、mv、覆盖写入、数据库变更等危险操作时遵循以下原则先在测试环境执行确认效果。删除前先打印将删除的文件列表人工确认后再加-delete或rm。重要数据先备份或移动到一个回收目录。生产环境脚本不要裸写rm -rf至少用变量、路径判断和日志记录保护。比如可以在清理脚本里加上一行“防呆”判断if [ $log_dir / ] || [ $log_dir /data ]; then echo 危险路径拒绝执行 exit 1 fi6.5 日志记录比 echo 更重要脚本在 crontab 中自动化运行后没有人盯着屏幕看输出。所以关键操作必须写日志。统一的日志格式建议log() { echo $(date %Y-%m-%d %H:%M:%S) $* /var/log/my_script.log }调用时log 开始执行备份任务 log 备份完成文件大小: $(du -sh backup.tar.gz | awk {print $1})6.6 注意跨平台差异不要在脚本中用\r或 Windows 记事本编辑脚本。/bin/bash和/bin/sh的差异要清楚如果脚本用到 bash 特性shebang 写#!/bin/bash。调用命令时尽量用绝对路径或确保 PATH 中包含相应目录尤其是在 crontab 环境中PATH 可能与登录 Shell 不一样。可以在脚本开头显式设置export PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin6.7 合理使用退出码脚本的不同失败场景建议返回不同的退出码。运维监控脚本可以通过$?判断失败类别exit 0 # 成功 exit 1 # 一般性错误 exit 2 # 参数错误这在后续接入监控平台、自动化调度平台时非常有用。7. 总结与进阶路线走到这里你已经掌握了 Shell 脚本从零到实战的核心知识。现在回顾一下你应该已经具备以下能力理解 Shell 脚本的作用和适用场景能区分不同 Shell 的差异。熟练写出变量、字符串处理、条件判断、循环、函数等脚本基本结构。掌握$?、$#、$、shift等特殊变量能处理脚本参数。能综合使用find、awk、grep、crontab等命令完成自动化运维任务。对常见脚本报错有清晰的排查思路知道用bash -x和bash -n调试脚本。具备编写安全、可维护脚本的工程意识。接下来的进阶方向可以根据你的实际需求选择学习文本处理三剑客的深入用法grep、sed、awk。它们能让你的 Shell 脚本处理复杂文本时效率翻倍。学习如何用 Shell 封装复杂的部署流程配合expect处理交互式输入配合rsync做增量同步。学习 Shell 与其他工具结合例如调用 Python 脚本处理复杂数据、调用 Docker 命令管理容器生命周期。如果从事云原生或平台研发还要了解容器镜像的入口脚本通常也是 Shell 写的Shell 是打通“系统命令”和“上层应用”的桥梁。最终想说的是Shell 脚本这门技术入门门槛不高但上限很高。判断你是否真正掌握它的标准不是背了多少语法而是遇到重复性工作时你是否能下意识地想到“这个能用脚本自动跑掉”。带着这个思路去写脚本你的进步会非常快。建议现在就打开终端从第一个Hello, Shell!开始把一个实际工作中的重复操作写成脚本跑通它。脚本跑通的那一刻你对 Shell 的理解就真正扎实了。
返回列表