什么是 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[@]}"表示展开数组所有元素do和done是循环的起止标志< "$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”,也是你通往自动化世界的第一步。