去年做 Linux 运维时,有个任务让我每天崩溃 —— 手动清理 10 台服务器的日志文件。每天早上,我要登录每台机器,执行cd /var/log、rm -rf *.log.1、tar -zcvf nginx.log.tar.gz nginx.log,一套操作下来,1 小时就没了。
有次因为加班忘了清理,某台服务器日志占满磁盘,导致业务中断,被领导批评时,我忍不住想:“有没有办法让这些操作自动跑?”
就在这时,同事扔给我一个文件,说:“试试这个 Shell 脚本,双击就能搞定所有机器的日志清理。” 我打开文件,里面是几行看似简单的代码,却包含了登录、执行命令、压缩文件的全流程。运行后,原本 1 小时的工作,3 分钟就完成了。
那是我第一次意识到:Shell 脚本不是 “程序员的专属工具”,而是每个 Linux 使用者的 “自动化利器”。后来我花 7 天系统学完 Shell 基础,把 “手动部署服务”“批量创建用户”“监控磁盘空间” 等重复任务全改成了脚本,工作效率直接提升 30%。
这篇文章,我会用 “零基础能看懂” 的语言,拆解 Shell 脚本的核心知识,再通过 3 个实战案例,帮你快速掌握 “用脚本解放双手” 的能力。
一、先搞懂:什么是 Shell 脚本?它为什么能 “自动化”?
很多人觉得 Shell 脚本 “高深”,其实它的本质特别简单 —— 就像把你在 Linux 终端里敲的命令,按顺序写进一个文件里,让系统 “按剧本执行”。
1. 一句话说清:Shell 与 Shell 脚本的关系
我们可以把 Linux 系统比作 “一家餐厅”:
- 内核:后厨,负责真正的 “做菜”(执行底层操作,如读写磁盘、网络通信);
- Shell:服务员,是你和后厨的 “翻译官”—— 你说 “来份宫保鸡丁”(输入命令),服务员传给后厨,再把做好的菜(执行结果)端给你;
- Shell 脚本:你提前写好的 “点餐清单”,把 “宫保鸡丁、米饭、可乐” 按顺序列好,服务员直接按清单下单,不用你重复开口。
简单来说:Shell 是 “命令解释器”,Shell 脚本是 “多个 Shell 命令的集合文件”,通过脚本,你可以让系统自动执行一系列操作,不用手动敲命令。
2. Shell 脚本的 3 个核心优势:为什么运维 / 开发都离不开它?
- 自动化重复工作:像日志清理、文件备份、服务部署这类 “每天都要做” 的操作,写一次脚本,后续双击就能执行;
- 跨机器批量操作:结合
ssh命令,一个脚本能同时控制 10 台、100 台 Linux 机器,不用逐台登录; - 学习成本低:语法接近 Linux 命令,不用像学 Python、Java 那样记复杂的类和函数,零基础 7 天就能上手实战。
3. 常见的 Shell 类型:我们该学哪一种?
Linux 里有多种 Shell(如 sh、bash、zsh),其中bash(Bourne Again Shell) 是最常用的,默认安装在 99% 的 Linux 系统(CentOS、Ubuntu、Debian)里,也是我们要学的重点。
判断你的系统是否支持 bash,只需在终端输入echo $SHELL,如果输出/bin/bash,就说明可以直接写 bash 脚本。
二、快速上手:从 “环境准备” 到 “基础语法”,7 天搞定核心知识
零基础学 Shell 脚本,不用贪多求全,先掌握 “能写出实用脚本” 的核心语法即可。我把 7 天的学习计划拆解成 “环境准备→基本要素→核心语法” 三部分,每部分都配 “代码示例 + 运行效果”,跟着做就能学会。
1. 第 1 天:环境准备 ——3 步写出第一个 Shell 脚本
写 Shell 脚本不需要复杂工具,终端 + 文本编辑器(如 vim、nano)就能搞定,全程 3 步:
步骤 1:创建脚本文件
在终端输入vim auto_clean_log.sh,创建一个名为 “auto_clean_log.sh” 的脚本文件(.sh是 Shell 脚本的默认后缀,方便识别)。
步骤 2:写脚本内容(第一个 “Hello World”)
在 vim 里输入以下内容,这是最基础的 Shell 脚本结构:
#!/bin/bash
# 这是我的第一个Shell脚本(#开头的是注释,不会被执行)
echo "开始执行日志清理脚本..." # echo命令:在终端输出文字
date # 输出当前时间,方便查看脚本执行时间
#!/bin/bash:指定解释器,告诉系统用 bash 来执行这个脚本(必须写在第一行);# 注释内容:用来解释脚本功能,方便自己或别人看懂,系统会忽略注释;echo "文字":输出文字到终端,相当于 “打印日志”,方便跟踪脚本执行过程。
步骤 3:赋予权限并运行
Shell 脚本默认没有 “执行权限”,需要先授权,再运行:
# 步骤1:赋予执行权限(chmod +x 文件名:给文件增加“执行”权限)
chmod +x auto_clean_log.sh
# 步骤2:运行脚本(两种方式)
./auto_clean_log.sh # 方式1:./表示“当前目录”,推荐用这种,避免和系统命令重名
bash auto_clean_log.sh # 方式2:直接用bash解释器运行,不用提前授权
运行效果:
终端会输出:
开始执行日志清理脚本...
2024年 05月 20日 星期一 14:30:00 CST
到这里,你已经写出并运行了第一个 Shell 脚本!是不是比想象中简单?
2. 第 2-3 天:核心语法 —— 掌握这 4 个要素,能写 80% 实用脚本
Shell 脚本的语法不复杂,重点掌握 “变量、条件判断、循环、函数” 这 4 个要素,就能应对大部分自动化场景。
(1)变量:存储数据,让脚本更灵活
变量就像 “小盒子”,用来存数字、文字、命令结果,方便后续调用。比如你想让脚本 “清理指定目录的日志”,就可以用变量存 “目录路径”,后续改路径时不用改整个脚本。
常用变量用法示例:
#!/bin/bash
# 1. 定义变量(格式:变量名=值,注意:=前后不能有空格!这是新手最常踩的坑)
LOG_DIR="/var/log" # 定义“日志目录”变量,存日志文件的路径
BACKUP_DIR="/var/backup/log" # 定义“备份目录”变量,存压缩后的日志
CURRENT_DATE=$(date +%Y%m%d) # 定义“当前日期”变量,$(命令)表示“执行命令并把结果存到变量里”
# 2. 使用变量(格式:$变量名 或 ${变量名},{}可选,推荐用${},避免歧义)
echo "日志目录:${LOG_DIR}"
echo "备份目录:${BACKUP_DIR}"
echo "当前日期:${CURRENT_DATE}"
# 3. 特殊变量:系统自带的变量,不用定义就能用
echo "脚本文件名:$0" # $0:当前脚本的文件名
echo "传入的第一个参数:$1" # $1:运行脚本时传入的第一个参数(比如./script.sh 123,$1就是123)
echo "传入的参数总数:$#" # $#:运行脚本时传入的参数总数
运行效果(执行./test_var.sh 20240520):
日志目录:/var/log
备份目录:/var/backup/log
当前日期:20240520
脚本文件名:./test_var.sh
传入的第一个参数:20240520
传入的参数总数:1
(2)条件判断:让脚本 “会做选择”
条件判断用来实现 “如果满足 A 条件,就执行 B 操作;否则执行 C 操作”。比如 “如果日志文件存在,就压缩备份;否则提示文件不存在”。
核心语法(if-else):
#!/bin/bash
LOG_FILE="/var/log/nginx.log" # 要判断的日志文件
# 条件判断格式:if [ 条件表达式 ]; then 操作; else 操作; fi(fi表示结束)
# 注意:[ 和 ] 前后必须有空格!否则会报错
if [ -f "${LOG_FILE}" ]; then # -f:判断文件是否存在且是普通文件
echo "找到日志文件:${LOG_FILE},开始压缩备份..."
tar -zcvf "${LOG_FILE}_${CURRENT_DATE}.tar.gz" "${LOG_FILE}" # 压缩文件
else
echo "错误:未找到日志文件 ${LOG_FILE}"
fi
常用条件表达式(记这几个足够用):
| 表达式 | 含义 | 示例 |
|---|---|---|
| -f 文件 | 判断文件是否存在且是普通文件 | [ -f /var/log/nginx.log ] |
| -d 目录 | 判断目录是否存在 | [ -d /var/backup ] |
| -n 字符串 | 判断字符串是否非空 | [ -n “$LOG_FILE” ] |
| $a -eq $b | 判断 a 和 b 是否相等(数字) | [ $1 -eq 100 ] |
| $a -gt $b | 判断 a 是否大于 b(数字) | [ $disk_usage -gt 80 ] |
(3)循环:让脚本 “重复做一件事”
循环用来实现 “重复执行某段操作”,比如 “批量清理 10 个目录的日志”“给 5 个用户设置密码”。最常用的是for循环和while循环。
示例 1:for 循环 —— 批量处理文件
#!/bin/bash
# 批量清理/var/log下所有.log.1后缀的日志文件
LOG_DIR="/var/log"
# for循环格式:for 变量 in 列表; do 操作; done
for log_file in $(ls "${LOG_DIR}"/*.log.1); do # ls ${LOG_DIR}/*.log.1:列出所有.log.1文件
echo "正在删除旧日志:${log_file}"
rm -rf "${log_file}" # 删除旧日志
done
echo "批量清理完成!"
示例 2:while 循环 —— 按条件重复执行
#!/bin/bash
# 监控磁盘使用率,当使用率超过80%时报警
disk_usage=$(df -h / | grep / | awk '{print $5}' | sed 's/%//g') # 获取根目录使用率(数字)
# while循环格式:while [ 条件 ]; do 操作; done
while [ ${disk_usage} -gt 80 ]; do # 当使用率>80%时,持续报警
echo "警告:磁盘使用率已达 ${disk_usage}%,请及时清理!"
sleep 60 # 暂停60秒后再次检查
disk_usage=$(df -h / | grep / | awk '{print $5}' | sed 's/%//g') # 重新获取使用率
done
echo "磁盘使用率正常:${disk_usage}%"
(4)函数:把重复代码 “打包”,让脚本更简洁
如果脚本里有 “多次重复的操作”,可以把这些操作封装成 “函数”,像调用命令一样调用,减少代码冗余。
函数示例 —— 封装 “日志备份” 操作:
#!/bin/bash
CURRENT_DATE=$(date +%Y%m%d)
# 定义函数(格式:函数名() { 操作; })
backup_log() {
local log_file=$1 # local:定义局部变量,只在函数内有效;$1:函数的第一个参数
local backup_dir=$2 # $2:函数的第二个参数
echo "开始备份:${log_file}"
# 先判断备份目录是否存在,不存在则创建
if [ ! -d "${backup_dir}" ]; then # !:否定,-d:判断目录
mkdir -p "${backup_dir}" # -p:创建多级目录
fi
# 压缩备份
tar -zcvf "${backup_dir}/$(basename ${log_file})_${CURRENT_DATE}.tar.gz" "${log_file}"
}
# 调用函数(格式:函数名 参数1 参数2)
backup_log "/var/log/nginx.log" "/var/backup/log"
backup_log "/var/log/mysql.log" "/var/backup/log"
echo "所有日志备份完成!"
3. 第 4-7 天:脚本调试 —— 新手必学的 “排错技巧”
写脚本时难免出错,比如语法错误、路径错误,掌握调试技巧能帮你快速定位问题。
(1)查看语法错误
在终端执行bash -n 脚本名.sh,bash 会检查脚本的语法是否正确,比如少写fi、=前后有空格等错误:
bash -n auto_clean_log.sh # 无输出表示语法正确,有错误会提示位置
(2)跟踪执行过程
执行bash -x 脚本名.sh,bash 会逐行输出脚本的执行过程,包括 “执行了哪个命令”“变量的值是什么”,方便定位哪一步出错:
bash -x auto_clean_log.sh
# 输出示例(+开头的是执行的命令,不带+的是echo输出)
+ CURRENT_DATE=20240520
+ backup_log /var/log/nginx.log /var/backup/log
+ local log_file=/var/log/nginx.log
+ local backup_dir=/var/backup/log
+ echo 开始备份:/var/log/nginx.log
开始备份:/var/log/nginx.log
(3)输出错误日志
在脚本里用echo "错误信息" >&2,把错误信息输出到 “标准错误流”,方便后续查看错误记录:
if [ ! -f "${LOG_FILE}" ]; then
echo "错误:未找到日志文件 ${LOG_FILE}" >&2 # >&2:把输出重定向到错误流
exit 1 # exit 1:脚本异常退出,告诉调用者“脚本执行失败”
fi
三、实战 3 案例:从 “理论” 到 “落地”,脚本直接能用
学完基础语法后,一定要用实战案例巩固。下面 3 个案例是我日常工作中常用的脚本,你可以直接复制修改后使用。
案例 1:日志自动清理 + 备份脚本(每天执行)
需求:每天凌晨 2 点,清理 /var/log 下 3 天前的.log.1 旧日志,备份当天的 nginx.log 和 mysql.log 到 /var/backup/log,并输出执行日志到 /var/log/auto_backup.log。
完整脚本(auto_backup_log.sh):
#!/bin/bash
# 日志自动清理+备份脚本
# 运行方式:1. 手动执行 ./auto_backup_log.sh;2. 加入crontab定时执行
# 1. 定义变量
LOG_DIR="/var/log"
BACKUP_DIR="/var/backup/log"
CURRENT_DATE=$(date +%Y%m%d)
LOG_FILE="${LOG_DIR}/auto_backup.log" # 脚本自身的执行日志
KEEP_DAYS=3 # 旧日志保留天数
# 2. 函数:输出日志(带时间戳)
write_log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" >> "${LOG_FILE}" # >>:追加日志到文件
}
# 3. 开始执行
write_log "=== 开始执行日志备份脚本 ==="
# 3.1 备份当天的nginx.log和mysql.log
for log_name in "nginx.log" "mysql.log"; do
full_log_path="${LOG_DIR}/${log_name}"
if [ -f "${full_log_path}" ]; then
write_log "正在备份日志:${full_log_path}"
tar -zcf "${BACKUP_DIR}/${log_name}_${CURRENT_DATE}.tar.gz" "${full_log_path}"
if [ $? -eq 0 ]; then # $?:上一个命令的执行结果(0=成功,非0=失败)
write_log "备份成功:${BACKUP_DIR}/${log_name}_${CURRENT_DATE}.tar.gz"
else
write_log "错误:备份 ${full_log_path} 失败" >&2
fi
else
write_log "警告:未找到日志文件 ${full_log_path}" >&2
fi
done
# 3.2 清理3天前的.log.1旧日志
write_log "开始清理${KEEP_DAYS}天前的旧日志..."
find "${LOG_DIR}" -name "*.log.1" -mtime +${KEEP_DAYS} -exec rm -rf {} \; # -mtime +3:3天前的文件
write_log "旧日志清理完成"
write_log "=== 脚本执行结束 ==="
echo "脚本执行完成,日志查看:${LOG_FILE}"
定时执行:用crontab设置每天凌晨 2 点运行(Linux 定时任务工具):
# 编辑定时任务
crontab -e
# 在文件末尾添加以下内容(含义:每天2点0分执行脚本)
0 2 * * * /root/scripts/auto_backup_log.sh # 脚本路径要写绝对路径
案例 2:批量创建 Linux 用户脚本
需求:为新入职的 5 个员工创建 Linux 账号,用户名分别为 user1-user5,初始密码为 “Company@2024”,并赋予 sudo 权限。
完整脚本(batch_create_user.sh):
#!/bin/bash
# 批量创建用户脚本
# 运行方式:sudo ./batch_create_user.sh(创建用户需要root权限)
# 1. 检查是否为root用户(创建用户必须root权限)
if [ "$(whoami)" != "root" ]; then # whoami:获取当前用户名
echo "错误:请用root权限运行(sudo ./batch_create_user.sh)" >&2
exit 1
fi
# 2. 定义变量
USER_PREFIX="user" # 用户名前缀
USER_NUM=5 # 要创建的用户数量
INIT_PWD="Company@2024" # 初始密码
# 3. 批量创建用户
for ((i=1; i<=${USER_NUM}; i++)); do
username="${USER_PREFIX}${i}"
# 先判断用户是否已存在
if id -u "${username}" >/dev/null 2>&1; then # id -u:检查用户是否存在,>/dev/null 2>&1:隐藏输出
echo "警告:用户 ${username} 已存在,跳过创建"
continue # 跳过当前循环,继续下一个
fi
# 创建用户(-m:创建用户家目录,-s:指定默认Shell为/bin/bash)
useradd -m -s /bin/bash "${username}"
# 设置密码(echo "密码" | passwd --stdin 用户名:非交互式设置密码)
echo "${INIT_PWD}" | passwd --stdin "${username}"
# 赋予sudo权限(把用户加入wheel组,CentOS默认wheel组有sudo权限)
usermod -aG wheel "${username}"
if [ $? -eq 0 ]; then
echo "成功创建用户:${username},初始密码:${INIT_PWD}(请提醒用户首次登录修改密码)"
else
echo "错误:创建用户 ${username} 失败" >&2
fi
done
echo "批量创建用户完成!"
案例 3:磁盘使用率监控报警脚本
需求:每 30 分钟检查一次根目录(/)的磁盘使用率,当使用率超过 80% 时,发送邮件提醒管理员。
完整脚本(monitor_disk.sh):
#!/bin/bash
# 磁盘使用率监控报警脚本
# 依赖:需要安装mailx工具(CentOS:yum install -y mailx;Ubuntu:apt install -y mailutils)
# 1. 定义变量
MONITOR_DIR="/" # 要监控的目录(根目录)
THRESHOLD=80 # 报警阈值(80%)
ADMIN_EMAIL="admin@example.com" # 管理员邮箱
CURRENT_TIME=$(date +'%Y-%m-%d %H:%M:%S')
# 2. 获取磁盘使用率(数字)
# df -h /:查看根目录磁盘信息;grep /:过滤根目录行;awk '{print $5}':取第5列(使用率,如85%);sed 's/%//g':去掉%
disk_usage=$(df -h "${MONITOR_DIR}" | grep "${MONITOR_DIR}" | awk '{print $5}' | sed 's/%//g')
# 3. 判断是否超过阈值,超过则发邮件
if [ ${disk_usage} -gt ${THRESHOLD} ]; then
# 邮件主题和内容
subject="【警告】${MONITOR_DIR} 磁盘使用率超过${THRESHOLD}%"
content="
监控时间:${CURRENT_TIME}
监控目录:${MONITOR_DIR}
当前使用率:${disk_usage}%
报警阈值:${THRESHOLD}%
建议:请及时清理无用文件(如旧日志、备份文件),避免磁盘占满导致业务中断。
"
# 发送邮件(echo 内容 | mail -s 主题 收件人)
echo -e "${content}" | mail -s "${subject}" "${ADMIN_EMAIL}"
echo "[$CURRENT_TIME] 警告:磁盘使用率${disk_usage}%,已发送邮件提醒管理员"
else
echo "[$CURRENT_TIME] 正常:磁盘使用率${disk_usage}%,低于阈值${THRESHOLD}%"
fi
四、新手避坑指南:我踩过的 5 个经典错误
- 变量赋值有空格:比如
LOG_DIR = "/var/log"(错误),正确是LOG_DIR="/var/log"——=前后不能有空格,否则系统会把 “LOG_DIR” 当成命令。 - 条件判断缺空格:比如
if [-f "${LOG_FILE}"](错误),正确是if [ -f "${LOG_FILE}" ]——[和]前后必须有空格,这是 bash 的语法要求。 - 路径用相对路径:比如
./auto_clean.sh里写rm -rf log/*.log,如果在其他目录执行脚本,会找不到 “log” 目录 —— 正确是用绝对路径,如rm -rf /var/log/*.log。 - 不判断命令执行结果:比如直接执行
tar -zcvf ...,不检查是否备份成功 —— 正确是用if [ $? -eq 0 ]判断上一步是否成功,避免失败了还不知道。 - 忽略权限问题:比如创建用户脚本不用 root 权限运行,导致
useradd命令报错 —— 正确是用sudo运行需要高权限的脚本。
结语:Shell 脚本的核心,是 “解放你的双手”
很多人学 Shell 脚本时,会陷入 “追求复杂语法” 的误区,其实 Shell 的核心价值是 “自动化重复工作”—— 哪怕是只有 10 行的简单脚本,只要能帮你节省时间,就是好脚本。
我刚开始学的时候,也只会写 “清理日志”“备份文件” 这类简单脚本,但正是这些脚本,帮我从每天重复敲命令的枯燥工作中解放出来,有更多时间学习其他技能。
记住:学习 Shell 脚本不用 “一步到位”,先从解决自己的实际问题开始 —— 比如你每天要手动备份文件,就写个备份脚本;每天要检查服务状态,就写个监控脚本。多练、多改,7 天就能上手,1 个月就能熟练,3 个月就能写出能落地的自动化脚本。
现在,打开你的 Linux 终端,试着写一个 “自动备份你工作目录” 的脚本吧 —— 从实践中学习,才是最快的方式!
网络安全学习资源分享:
给大家分享一份全套的网络安全学习资料,给那些想学习 网络安全的小伙伴们一点帮助!
对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。
因篇幅有限,仅展示部分资料,朋友们如果有需要全套《网络安全入门+进阶学习资源包》,请看下方扫描即可前往获取



571

被折叠的 条评论
为什么被折叠?



