shell 脚本(实战指南)

什么是 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 的增强条件测试,比 [ ] 更强大
  • == 用于字符串比较,注意两边有空格
  • fiif 的结束标志,必须写

循环结构: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 个文件"

使用说明:

  1. 保存为 clean_logs.sh
  2. 赋予权限:chmod +x clean_logs.sh
  3. 测试运行:./clean_logs.sh
  4. 通过 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 脚本吧。哪怕只是自动备份一次文件,也是一次值得庆祝的成长。