一、Shell脚本基础概念
什么是Shell?
Shell是Unix/Linux系统的命令解释器,它是用户与操作系统内核之间的桥梁。我们平时在终端输入的命令,实际上都是由Shell来解释执行的。常见的Shell类型包括:
- Bash (Bourne Again Shell):Linux默认Shell,功能最强大
- Zsh (Z Shell):功能丰富,可定制性强
- Fish (Friendly Interactive Shell):用户友好,语法简单

第一个Shell脚本
让我们从最简单的例子开始:
bash
#!/bin/bash
# 这是我的第一个Shell脚本
echo "Hello, Shell World!"
# 输出当前日期和时间
echo "当前时间是:$(date)"
保存为hello.sh,然后执行:
bash
chmod +x hello.sh # 添加执行权限
./hello.sh # 运行脚本
提示:
#!/bin/bash称为shebang,它告诉系统使用哪个解释器来执行脚本。
二、变量与数据类型
定义和使用变量
Shell中的变量定义非常灵活,不需要声明类型:
bash
#!/bin/bash
# 定义字符串变量
name="Linux"
version="Ubuntu 22.04"
# 定义数字变量
count=100
# 使用变量(两种方式)
echo "系统名称: $name"
echo "版本信息: ${version}"
# 只读变量
readonly PI=3.14159
# 删除变量
temp="临时数据"
unset temp
特殊变量
Shell提供了一些特殊变量来处理脚本参数:
bash
#!/bin/bash
echo "脚本名称: $0"
echo "第一个参数: $1"
echo "第二个参数: $2"
echo "所有参数: $@"
echo "参数个数: $#"
echo "当前进程ID: $$"
echo "上一个命令退出状态: $?"
# 运行示例:./script.sh arg1 arg2
变量替换
bash
#!/bin/bash
var="hello"
# 默认值替换
echo "${var:-"默认值"}" # 输出: hello
echo "${unset_var:-"默认值"}" # 输出: 默认值
# 设置默认值
echo "${var:="新值"}" # 输出: hello,var保持不变
echo "${unset_var:="新值"}" # 输出: 新值,unset_var被赋值
# 检查变量是否已设置
echo "${var:?"变量未设置"}" # 输出: hello
# echo "${unset:?"变量未设置"}" # 会报错退出
三、条件判断与流程控制
基础条件判断
Shell中使用test命令或[ ]进行条件判断:
bash
#!/bin/bash
# 数值比较
num1=10
num2=20
if [ $num1 -eq $num2 ]; then
echo "相等"
elif [ $num1 -lt $num2 ]; then
echo "$num1 小于 $num2"
else
echo "$num1 大于 $num2"
fi
# 常见比较操作符
# -eq: 等于 (equal)
# -ne: 不等于 (not equal)
# -lt: 小于 (less than)
# -le: 小于等于 (less or equal)
# -gt: 大于 (greater than)
# -ge: 大于等于 (greater or equal)
字符串判断
bash
#!/bin/bash
str1="hello"
str2="world"
str3="hello"
# 字符串比较
if [ "$str1" = "$str3" ]; then
echo "字符串相等"
fi
if [ -z "$empty_str" ]; then
echo "字符串为空"
fi
if [ -n "$str1" ]; then
echo "字符串非空"
fi
# 常见字符串操作符
# =: 相等
# !=: 不等
# -z: 字符串长度为0
# -n: 字符串长度不为0
文件测试
bash
#!/bin/bash
file="/path/to/file.txt"
if [ -e "$file" ]; then
echo "文件存在"
fi
if [ -f "$file" ]; then
echo "是普通文件"
fi
if [ -d "$file" ]; then
echo "是目录"
fi
if [ -r "$file" ]; then
echo "可读"
fi
if [ -w "$file" ]; then
echo "可写"
fi
if [ -x "$file" ]; then
echo "可执行"
fi
case语句
对于多条件判断,case语句更加清晰:
bash
#!/bin/bash
grade="B"
case $grade in
"A")
echo "优秀"
;;
"B")
echo "良好"
;;
"C")
echo "及格"
;;
*)
echo "未知等级"
;;
esac
# 通配符示例
read -p "请输入命令: " cmd
case $cmd in
start)
echo "启动服务..."
;;
stop)
echo "停止服务..."
;;
restart)
echo "重启服务..."
;;
*)
echo "未知命令"
;;
esac
四、循环结构
for循环
bash
#!/bin/bash
# 遍历列表
for fruit in apple banana orange; do
echo "我喜欢吃: $fruit"
done
# 使用C风格语法
for ((i=0; i<5; i++)); do
echo "计数器: $i"
done
# 遍历文件
for file in *.txt; do
echo "处理文件: $file"
done
# 使用seq命令
for num in $(seq 1 5); do
echo "数字: $num"
done
# 简化写法
for i in {1..10}; do
echo $i
done
while循环
bash
#!/bin/bash
# 基本while循环
count=1
while [ $count -le 5 ]; do
echo "计数: $count"
((count++))
done
# 读取文件行
while read line; do
echo "读取到: $line"
done < file.txt
# 无限循环(配合break使用)
while true; do
read -p "输入q退出: " input
if [ "$input" = "q" ]; then
break
fi
echo "你输入了: $input"
done
# until循环(条件为假时继续)
num=1
until [ $num -gt 5 ]; do
echo "until循环: $num"
((num++))
done
循环控制
bash
#!/bin/bash
# break和continue
for i in {1..10}; do
if [ $i -eq 3 ]; then
continue # 跳过当前迭代
fi
if [ $i -eq 8 ]; then
break # 退出循环
fi
echo "数字: $i"
done
五、函数定义与使用
函数基础
bash
#!/bin/bash
# 定义函数
function greet() {
echo "你好,$1!"
}
# 调用函数
greet "小明"
greet "小红"
# 带返回值的函数
function add() {
local sum=$(($1 + $2))
return $sum # 返回值只能是0-255
}
add 10 20
result=$?
echo "计算结果: $result"
函数作用域
bash
#!/bin/bash
variable="全局变量"
function demo() {
local local_var="局部变量"
variable="修改后的全局变量"
echo "函数内: $variable"
echo "函数内: $local_var"
}
demo
echo "函数外: $variable"
# echo "函数外: $local_var" # 这会报错
函数参数
bash
#!/bin/bash
function process() {
echo "参数个数: $#"
echo "所有参数: $@"
echo "第一个参数: $1"
echo "第二个参数: $2"
}
process "arg1" "arg2" "arg3"
六、实战案例
案例1:自动备份脚本
bash
#!/bin/bash
# 自动备份脚本
# 配置
BACKUP_DIR="/backup"
SOURCE_DIR="/data"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="backup_${DATE}.tar.gz"
# 创建备份目录
mkdir -p "$BACKUP_DIR"
# 执行备份
if tar -czf "${BACKUP_DIR}/${BACKUP_FILE}" "$SOURCE_DIR" 2>/dev/null; then
echo "备份成功: ${BACKUP_FILE}"
# 计算备份大小
size=$(du -h "${BACKUP_DIR}/${BACKUP_FILE}" | cut -f1)
echo "备份大小: $size"
# 清理7天前的备份
find "$BACKUP_DIR" -name "backup_*.tar.gz" -mtime +7 -delete
echo "清理旧备份完成"
else
echo "备份失败"
exit 1
fi
案例2:批量重命名文件
bash
#!/bin/bash
# 批量重命名脚本
directory=$1
prefix=$2
if [ -z "$directory" ] || [ -z "$prefix" ]; then
echo "用法: $0 <目录> <文件名前缀>"
exit 1
fi
cd "$directory" || exit 1
count=1
for file in *; do
if [ -f "$file" ]; then
extension="${file##*.}"
new_name="${prefix}_${count}.${extension}"
mv "$file" "$new_name"
echo "重命名: $file -> $new_name"
((count++))
fi
done
echo "完成,共处理 $((count-1)) 个文件"
案例3:系统监控脚本
bash
#!/bin/bash
# 系统监控脚本
echo "========== 系统监控报告 =========="
echo "生成时间: $(date)"
echo ""
# CPU使用率
echo "【CPU使用率】"
top -bn1 | head -5
# 内存使用
echo ""
echo "【内存使用】"
free -h
# 磁盘使用
echo ""
echo "【磁盘使用】"
df -h | grep -E "^/dev"
# 进程Top5
echo ""
echo "【CPU占用前5的进程】"
ps aux --sort=-%cpu | head -6
# 网络连接
echo ""
echo "【网络连接统计】"
netstat -an | grep ESTABLISHED | wc -l
echo "当前建立连接数"
七、调试技巧
调试选项
bash
#!/bin/bash
# 启用调试模式
# -x: 打印每条命令及其参数
# -v: 打印输入的行
# -n: 检查语法但不执行
set -x # 启用命令跟踪
echo "这是调试信息"
set +x # 关闭命令跟踪
# 另一种方式:运行时指定
# bash -x script.sh
错误处理
bash
#!/bin/bash
# 遇到错误立即退出
set -e
# 未定义的变量视为错误
set -u
# 管道中任何一个命令失败,整个管道失败
set -o pipefail
# 使用trap捕获错误
function error_handler() {
echo "错误发生在第 $1 行"
}
trap 'error_handler $LINENO' ERR
# 测试
ls /nonexistent # 会触发错误处理
八、常见问题与最佳实践
最佳实践
- 总是使用引号:避免变量为空时出现问题
bash# 正确 if [ "$var" = "value" ]; then # 错误 if [ $var = "value" ]; then - 使用
[[ ]]替代[ ]:更强大的条件判断
bash# 支持更多操作 if [[ $str =~ pattern ]]; then - 添加错误处理:让脚本更健壮
bashset -euo pipefail - 添加使用说明:方便他人和自己理解
bashfunction usage() { echo "用法: $0 [选项]" echo "选项:" echo " -h: 显示帮助" echo " -v: 详细模式" } - 使用日志记录:便于问题排查
bashLOG_FILE="/var/log/script.log" function log() { echo "[$(date)] $1" | tee -a "$LOG_FILE" }
性能优化
bash
#!/bin/bash
# 避免子shell,使用内联操作
# 慢:
for item in $(cat file.txt); do
process $item
done
# 快:
while read item; do
process $item
done < file.txt
# 使用数组而非字符串拼接
array=()
for i in {1..1000}; do
array+=("item_$i")
done
九、总结
Shell脚本是每个Linux使用者必须掌握的技能。通过本教程的学习,你应该已经掌握了:
- Shell变量的定义和使用
- 条件判断和流程控制
- 循环结构的各种用法
- 函数的定义和调用
- 实际场景中的脚本编写
- 调试技巧和最佳实践
继续练习,多写脚本解决实际问题,你会发现Shell脚本的强大之处。推荐进一步学习正则表达式、awk、sed等文本处理工具,它们与Shell脚本配合使用可以完成更复杂的任务。
相关资源:
阅读更多:

发表回复