什么是 shell 脚本?从命令行到自动化工具
你有没有试过每天重复输入同样的命令?比如每天备份文件、检查服务状态、清理日志……这些操作如果靠手动执行,不仅费时,还容易出错。这时候,shell 脚本 就像一个“自动助手”,帮你把重复动作写成程序,一键完成。
简单来说,shell 脚本就是一组按顺序执行的命令集合,用文本文件保存,通过 shell 解释器运行。它不是一门独立的编程语言,而是命令行环境的“脚本语言”。你可以把它想象成一个“自动化流水线”:把一堆操作步骤写成步骤清单,然后让系统自动执行。
常见的 shell 有 Bash(Bourne Again Shell)、Zsh、Sh 等,其中 Bash 是最广泛使用的。我们今天聚焦 Bash,因为它兼容性好、学习门槛低,是进入 shell 脚本世界的最佳入口。
从零开始写第一个 shell 脚本
我们先来一个最简单的例子。假设你想每天运行一次 date 命令,查看当前时间,然后输出“今天是星期几”。
打开你喜欢的文本编辑器(比如 vim、nano 或 VS Code),新建一个文件,命名为 hello.sh。
#!/bin/bash
echo "现在的时间是:$(date)"
weekday=$(date +%A)
echo "今天是:$weekday"
保存后,给脚本添加可执行权限:
chmod +x hello.sh
然后运行它:
./hello.sh
你会看到类似输出:
现在的时间是:Fri Apr 5 10:30:22 CST 2025
今天是:Friday
关键点解析:
#!/bin/bash是脚本的“启动头”,必须放在第一行。echo是输出命令,类似 Python 的 print。$(...)是命令替换,用于执行命令并获取结果。- 变量定义不加
var关键字,直接变量名=值,引用时加$。
变量、条件判断与流程控制
shell 脚本的“大脑”是流程控制。理解变量和条件判断,才能写出真正智能的脚本。
变量的定义与使用
变量在 shell 中非常灵活,但要注意:等号两边不能有空格,否则会出错。
name="张三"
age=25
city="北京"
echo "姓名:$name,年龄:$age,城市:$city"
变量是无类型的,你可以随时重新赋值:
count=1
count=2
count="hello" # 也可以存字符串
条件判断:if 语句
我们来写一个判断系统是否为 Linux 的脚本:
#!/bin/bash
if [[ "$(uname -s)" == "Linux" ]]; then
echo "当前系统是 Linux"
else
echo "当前系统不是 Linux"
fi
uname -s输出内核名称,Linux 系统返回Linux[[ ]]是 Bash 的增强条件测试,比[ ]更强大==用于字符串比较,注意两边有空格fi是if的结束标志,必须写
循环结构:for 和 while
for 循环:遍历列表
#!/bin/bash
for user in alice bob charlie; do
echo "欢迎用户:$user"
done
输出:
欢迎用户:alice
欢迎用户:bob
欢迎用户:charlie
while 循环:条件驱动
#!/bin/bash
count=1
while [ $count -le 5 ]; do
echo "第 $count 次循环"
count=$((count + 1))
# 使用 $(( )) 进行数学运算
done
输出:
第 1 次循环
第 2 次循环
第 3 次循环
第 4 次循环
第 5 次循环
函数封装与模块化设计
随着脚本变长,代码会变得难以维护。这时候,函数 就是你的“工具箱”。
定义和调用函数
#!/bin/bash
greet() {
local name="$1" # $1 是第一个参数,local 表示局部变量
echo "你好,$name!欢迎使用本脚本。"
}
greet "小李"
greet "小王"
输出:
你好,小李!欢迎使用本脚本。
你好,小王!欢迎使用本脚本。
local是可选的,但建议使用,避免污染全局命名空间。$1,$2... 是函数参数,最多可支持 9 个。- 函数名后加括号
(),但函数体不需要括号。
返回值:exit code 与 return
函数可以通过 return 返回状态码(0 表示成功,非 0 表示失败),也可以通过 echo 输出结果。
check_disk_usage() {
local threshold=80 # 磁盘使用率阈值
local usage=$(df / | tail -1 | awk '{print $5}' | tr -d '%')
if [ $usage -gt $threshold ]; then
echo "警告:磁盘使用率已超过 $threshold%,当前为 $usage%"
return 1 # 返回失败
else
echo "磁盘使用正常,当前为 $usage%"
return 0 # 返回成功
fi
}
check_disk_usage
if [ $? -eq 0 ]; then
echo "✅ 检查通过"
else
echo "❌ 检查失败"
fi
$?是上一个命令的返回值(exit code)awk '{print $5}'提取第 5 列(使用率),tr -d '%'去掉百分号
实战案例:自动化日志清理脚本
我们来写一个实用的脚本:每天自动清理超过 7 天的旧日志文件。
#!/bin/bash
log_dir="/var/log"
days=7
if [ ! -d "$log_dir" ]; then
echo "❌ 目录不存在:$log_dir"
exit 1
fi
find "$log_dir" -name "*.log" -mtime +$days -delete
if [ $? -eq 0 ]; then
echo "✅ 已成功清理 $days 天前的日志文件"
else
echo "❌ 清理失败,请检查权限或路径"
fi
count=$(find "$log_dir" -name "*.log" -mtime +$days | wc -l)
echo "共清理了 $count 个文件"
使用说明:
- 保存为
clean_logs.sh - 赋予权限:
chmod +x clean_logs.sh - 测试运行:
./clean_logs.sh - 通过 crontab 定时执行(如每天凌晨 2 点):
0 2 * * * /path/to/clean_logs.sh
常见陷阱与最佳实践
shell 脚本虽简单,但容易踩坑。以下是几个高频问题:
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 变量赋值时等号两边有空格 | shell 误认为是命令 | 确保 name=value,不要写 name = value |
| 未使用双引号包裹变量 | 包含空格的路径会出错 | 用 "$var" 而非 $var |
| 忘记添加可执行权限 | 脚本无法运行 | 使用 chmod +x script.sh |
使用 [ 而不是 [[ |
功能有限,不支持正则等 | 优先使用 [[ ]] |
| 脚本路径使用相对路径 | 在不同目录运行会失败 | 使用绝对路径或 $(dirname $0) 获取脚本路径 |
总结:shell 脚本是高效运维的基石
从今天起,不要再手动执行重复命令。一个小小的 shell 脚本,就能帮你节省大量时间,提升工作质量。
我们从最基础的语法讲起,逐步深入变量、条件、循环、函数,最后通过一个真实案例展示了自动化日志清理的完整流程。你已经掌握了从“写脚本”到“用脚本”的完整能力。
记住:编程不是写代码,而是解决问题。 shell 脚本虽小,却是解决系统管理、部署、监控等问题的利器。当你能用脚本自动完成日常任务时,你就真正进入了高效开发的境界。
现在,就动手写一个属于你的第一个 shell 脚本吧。哪怕只是自动备份一次文件,也是一次值得庆祝的成长。