shell编程(千字长文)

什么是 shell编程?从命令行到自动化脚本的跃迁

你有没有在 Linux 或 macOS 的终端里敲过命令?比如 ls 列出文件,cd 切换目录,grep 搜索内容?这些命令本身很强大,但当你需要重复执行多个操作时,手动一条条敲就显得低效了。这时候,shell编程 就是你的“自动化助手”。

简单来说,shell编程 就是用 shell 脚本语言来组织一系列命令,让它们按顺序自动执行。你可以把它想象成一个“命令流水线”——你把任务写成脚本,系统就会像流水线工人一样,自动帮你完成所有步骤。

比如你想每天备份某个文件夹,手动操作要三步:进入目录、压缩文件、复制到备份位置。用 shell编程,你只需写一个脚本,每天运行一次,一切自动搞定。

更重要的是,shell编程 不仅能简化重复操作,还能处理判断、循环、变量等逻辑,让脚本具备“智能”能力。它不是简单的命令堆叠,而是一种真正的编程语言。

对初学者而言,shell编程 是通往系统管理、运维自动化、开发部署的第一步。对中级开发者,它是提升效率、构建 CI/CD 流程的关键技能。掌握它,你就能从“命令使用者”蜕变为“系统掌控者”。


shell脚本的基本结构与执行方式

一个 shell 脚本本质上是一个纯文本文件,里面写的是 shell 命令和控制结构。要让它运行,必须先“告诉”系统这是 shell 脚本。

脚本开头:shebang

每个 shell 脚本的第一行必须是 shebang,它决定了脚本用哪个解释器运行。最常见的写法是:

#!/bin/bash

这行的意思是:“请用 /bin/bash 这个程序来解释下面的内容”。如果没有这行,系统可能无法识别你的脚本类型,导致执行失败。

创建与执行脚本

我们来创建一个简单的脚本。假设你想在终端打印一句欢迎语:

#!/bin/bash

echo "欢迎使用我的第一个 shell 脚本!"

保存为 hello.sh,然后给它执行权限:

chmod +x hello.sh

chmod +x 命令的作用是“给文件添加可执行权限”,就像给一个程序“贴上许可证”。

最后运行脚本:

./hello.sh

输出结果:

欢迎使用我的第一个 shell 脚本!

注意:运行脚本时必须加 ./ 前缀,表示当前目录下的文件。直接输入 hello.sh 可能会报错,因为 shell 默认不在当前路径查找可执行文件。


变量定义与使用:脚本中的“记忆库”

在 shell编程 中,变量就像一个临时存储柜,用来保存数据。你可以用它来保存用户输入、文件路径、计算结果等。

变量声明与赋值

#!/bin/bash

username="Alice"

current_date=$(date +%Y-%m-%d)

echo "用户: $username"
echo "今天是: $current_date"

关键点说明:

  • 变量名不能有空格,如 my var 是错误的,应写成 my_var
  • 赋值时等号两边不能有空格,name = "Bob" 会报错,必须写成 name="Bob"
  • 使用变量时前面加 $,如 $username

命令替换:变量的“动态内容”

变量还能保存命令的输出。比如:

file_count=$(ls -1 | wc -l)
echo "当前目录有 $file_count 个文件"

这里 $(...) 是命令替换语法:把括号内的命令执行结果,当作字符串赋给变量。ls -1 列出所有文件(每行一个),wc -l 统计行数,结果就是文件总数。

这就像你让助手去数一数书架上有多少本书,然后把数字告诉你。


条件判断:让脚本“学会思考”

你写的脚本不能总是“一条路走到黑”。很多时候,你需要根据条件决定下一步怎么做。这就要用到条件判断。

if 语句:最基础的判断

#!/bin/bash

echo "请输入一个数字:"
read number

if [ $number -gt 10 ]; then
    echo "这个数字大于 10"
elif [ $number -eq 10 ]; then
    echo "这个数字等于 10"
else
    echo "这个数字小于 10"
fi

说明:

  • read number:从标准输入读取用户输入,并存入变量 number
  • [ $number -gt 10 ]:判断 number 是否大于 10,-gt 是“大于”的缩写
  • elif 表示“否则如果”,fi 表示 if 语句的结束
  • 条件判断必须用 [ ] 括起来,注意中括号两边要有空格

实际应用:检查文件是否存在

#!/bin/bash

filename="config.txt"

if [ -f "$filename" ]; then
    echo "文件 $filename 存在,正在读取..."
    cat "$filename"
else
    echo "文件 $filename 不存在,无法读取。"
fi
  • -f 参数检查是否为普通文件
  • 使用双引号 "$filename" 是为了防止路径中包含空格导致错误

这个例子就像你去图书馆找一本书,先问管理员“这本书在不在”,再决定下一步。


循环结构:重复执行任务的利器

当你要对多个文件、多个用户、多个配置做相同操作时,循环就派上用场了。

for 循环:遍历列表

#!/bin/bash

files=("report1.txt" "report2.txt" "report3.txt")

for file in "${files[@]}"; do
    # 检查文件是否存在
    if [ -f "$file" ]; then
        echo "正在处理文件: $file"
        # 统计行数
        line_count=$(wc -l < "$file")
        echo "文件 $file 有 $line_count 行"
    else
        echo "文件 $file 不存在,跳过。"
    fi
done

说明:

  • "${files[@]}" 表示展开数组所有元素
  • dodone 是循环的起止标志
  • < "$file" 是将文件内容作为输入传给 wc -l

while 循环:条件满足就一直执行

#!/bin/bash

count=1

while [ $count -le 5 ]; do
    echo "这是第 $count 次循环"
    count=$((count + 1))  # count = count + 1
done

$((...)) 是数学运算的语法,类似 Python 的 int(),但用于 shell。


函数封装:让代码更清晰、可复用

随着脚本变复杂,你会发现重复代码越来越多。这时,函数就是你的“代码模块化工具”。

#!/bin/bash

print_system_info() {
    echo "=== 系统信息 ==="
    echo "主机名: $(hostname)"
    echo "当前用户: $(whoami)"
    echo "当前目录: $(pwd)"
    echo "内存使用: $(free -m | awk 'NR==2{printf "%.1f%%", $3*100/$2}')"
    echo "================"
}

print_system_info
  • 函数名后跟一对括号,内容写在大括号里
  • 调用时直接输入函数名即可
  • 函数内部可以使用变量、命令、条件和循环

这样,你只需写一次函数,就能在脚本中多次调用,大大提升可读性和维护性。


实战案例:自动化备份脚本

让我们综合运用前面的知识,写一个完整的 shell脚本:每日备份指定目录。

#!/bin/bash


SOURCE_DIR="/home/user/data"
BACKUP_DIR="/backup"
DATE=$(date +%Y%m%d)
BACKUP_FILE="backup_${DATE}.tar.gz"

LOG_FILE="/var/log/backup.log"

log_message() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') | $1" >> "$LOG_FILE"
}

if [ ! -d "$SOURCE_DIR" ]; then
    log_message "错误:源目录不存在: $SOURCE_DIR"
    exit 1
fi

if [ ! -d "$BACKUP_DIR" ]; then
    mkdir -p "$BACKUP_DIR"
    log_message "创建备份目录: $BACKUP_DIR"
fi

tar -czf "$BACKUP_DIR/$BACKUP_FILE" -C "$SOURCE_DIR" .

if [ $? -eq 0 ]; then
    log_message "备份成功: $BACKUP_DIR/$BACKUP_FILE"
    echo "✅ 备份完成"
else
    log_message "备份失败"
    echo "❌ 备份失败,请检查日志"
    exit 1
fi

脚本说明:

  • ? 是上一条命令的退出状态码,0 表示成功,非 0 表示失败
  • tar -czf:压缩并打包,c 创建,z 压缩,f 指定文件名
  • -C "$SOURCE_DIR":进入源目录再打包,避免路径问题
  • 日志记录让问题可追踪,生产环境必备

把这个脚本保存为 backup.sh,赋予执行权限,并通过 cron 定时运行,就能实现真正的自动化。


结语:从命令到智能自动化

shell编程 并不是高深莫测的技能,它只是让你把“重复劳动”交给机器,把精力留给更有价值的事。从变量、判断、循环,到函数和脚本封装,每一步都在构建你的自动化能力。

当你能写出一个能自动备份、检查服务、清理日志的脚本时,你会意识到:原来自己也能成为“系统管理员”。

别再手动敲命令了。从今天开始,动手写一个属于你的第一个 shell脚本吧。哪怕只是打印一句“Hello World”,也是你通往自动化世界的第一步。