综合练习脚本 | Evergreen Notes返回首页综合练习脚本
一个脚本覆盖 Shell 常用核心知识点
#!/bin/bash
###############################################################################
# Shell 综合练习脚本
# 目标:一个脚本覆盖 Shell 常用核心知识点
# 建议:每天练一遍,先看懂,再手敲,再改造
###############################################################################
###############################################################################
# 知识点 1:严格模式 set
# set -e:命令出错立即退出
# set -u:使用未定义变量时报错
# pipefail:管道中任意命令失败,整体就算失败
###############################################################################
set -euo pipefail
###############################################################################
# 知识点 2:变量定义
# 注意:等号两边不能有空格
###############################################################################
SCRIPT_NAME="Shell综合练习脚本"
AUTHOR="ningbo"
TODAY=$(date +%F)
NOW=$(date '+%F %T')
BASE_DIR="/tmp/shell_practice"
LOG_FILE="$BASE_DIR/practice.log"
CONFIG_FILE="$BASE_DIR/config.conf"
DATA_FILE="$BASE_DIR/users.txt"
BACKUP_DIR="$BASE_DIR/backup"
###############################################################################
# 知识点 3:函数定义
# 函数可以重复调用,提升脚本可读性
###############################################################################
log() {
local msg="$1"
echo "$(date '+%F %T') $msg" | tee -a "$LOG_FILE"
}
print_title() {
echo
echo "=============================="
echo "$1"
echo "=============================="
}
###############################################################################
# 知识点 4:参数与特殊变量
# $0:脚本名称
# $1:第一个参数
# $#:参数个数
# $@:所有参数
# $$:当前脚本进程 ID
# $?:上一条命令执行结果,0 表示成功,非 0 表示失败
###############################################################################
show_args() {
print_title "知识点 4:参数与特殊变量"
echo "脚本名称:$0"
echo "参数个数:$#"
echo "所有参数:$@"
echo "当前脚本 PID:$$"
if [ $# -ge 1 ]; then
echo "第一个参数是:$1"
else
echo "没有传入参数"
fi
}
###############################################################################
# 知识点 5:目录和文件操作
# mkdir:创建目录
# touch:创建文件
# cp:复制
# mv:移动/改名
# rm:删除
# ls:查看
###############################################################################
prepare_files() {
print_title "知识点 5:目录和文件操作"
mkdir -p "$BASE_DIR" "$BACKUP_DIR"
touch "$LOG_FILE"
cat > "$CONFIG_FILE" <<EOF
APP_NAME=shell-practice
APP_ENV=dev
PORT=8080
EOF
cat > "$DATA_FILE" <<EOF
zhangsan 18 linux
lisi 22 docker
wangwu 25 kubernetes
zhaoliu 30 shell
EOF
cp "$DATA_FILE" "$BACKUP_DIR/users_$TODAY.txt"
ls -lah "$BASE_DIR"
log "文件和目录初始化完成"
}
###############################################################################
# 知识点 6:if 条件判断
# -f:是否为普通文件
# -d:是否为目录
# -e:是否存在
# -z:字符串为空
# -n:字符串不为空
###############################################################################
check_file_and_dir() {
print_title "知识点 6:if 条件判断"
if [ -d "$BASE_DIR" ]; then
echo "目录存在:$BASE_DIR"
else
echo "目录不存在:$BASE_DIR"
fi
if [ -f "$DATA_FILE" ]; then
echo "文件存在:$DATA_FILE"
else
echo "文件不存在:$DATA_FILE"
fi
local name=""
if [ -z "$name" ]; then
echo "变量 name 是空字符串"
fi
name="nginx"
if [ -n "$name" ]; then
echo "变量 name 不为空:$name"
fi
}
###############################################################################
# 知识点 7:数字比较
# -eq:等于
# -ne:不等于
# -gt:大于
# -lt:小于
# -ge:大于等于
# -le:小于等于
###############################################################################
number_compare() {
print_title "知识点 7:数字比较"
local disk_usage=75
if [ "$disk_usage" -gt 80 ]; then
echo "磁盘使用率过高:${disk_usage}%"
elif [ "$disk_usage" -ge 60 ]; then
echo "磁盘使用率偏高:${disk_usage}%"
else
echo "磁盘使用率正常:${disk_usage}%"
fi
}
###############################################################################
# 知识点 8:字符串比较
# =:等于
# !=:不等于
###############################################################################
string_compare() {
print_title "知识点 8:字符串比较"
local env="dev"
if [ "$env" = "prod" ]; then
echo "生产环境"
elif [ "$env" = "dev" ]; then
echo "开发环境"
else
echo "未知环境"
fi
}
###############################################################################
# 知识点 9:for 循环
# 适合遍历固定列表、文件列表、数组
###############################################################################
for_loop_demo() {
print_title "知识点 9:for 循环"
for service in nginx mysql redis docker; do
echo "检查服务:$service"
done
for file in "$BASE_DIR"/*; do
echo "发现文件:$file"
done
}
###############################################################################
# 知识点 10:while 循环
# 适合条件循环、逐行读取文件
###############################################################################
while_loop_demo() {
print_title "知识点 10:while 循环"
local count=1
while [ "$count" -le 3 ]; do
echo "while 循环第 $count 次"
count=$((count + 1))
done
echo "逐行读取用户文件:"
while read -r username age skill; do
echo "用户:$username,年龄:$age,技能:$skill"
done < "$DATA_FILE"
}
###############################################################################
# 知识点 11:case 分支
# 适合处理 start/stop/restart/status 这种命令参数
###############################################################################
case_demo() {
print_title "知识点 11:case 分支"
local action="${1:-status}"
case "$action" in
start)
echo "执行启动操作"
;;
stop)
echo "执行停止操作"
;;
restart)
echo "执行重启操作"
;;
status)
echo "查看状态"
;;
*)
echo "用法:$0 {start|stop|restart|status}"
;;
esac
}
###############################################################################
# 知识点 12:数组
# 数组适合保存多个值
###############################################################################
array_demo() {
print_title "知识点 12:数组"
local services=("nginx" "mysql" "redis" "docker")
echo "第一个元素:${services[0]}"
echo "所有元素:${services[@]}"
echo "数组长度:${#services[@]}"
for item in "${services[@]}"; do
echo "数组元素:$item"
done
}
###############################################################################
# 知识点 13:算术运算
# $(( )) 用于整数计算
###############################################################################
arithmetic_demo() {
print_title "知识点 13:算术运算"
local a=10
local b=3
echo "a + b = $((a + b))"
echo "a - b = $((a - b))"
echo "a * b = $((a * b))"
echo "a / b = $((a / b))"
echo "a % b = $((a % b))"
}
###############################################################################
# 知识点 14:管道和重定向
# |:管道,把前一个命令结果交给后一个命令
# >:覆盖写入
# >>:追加写入
# 2>&1:把错误输出合并到标准输出
###############################################################################
pipe_redirect_demo() {
print_title "知识点 14:管道和重定向"
echo "hello shell" > "$BASE_DIR/redirect.txt"
echo "hello linux" >> "$BASE_DIR/redirect.txt"
echo "hello devops" >> "$BASE_DIR/redirect.txt"
echo "包含 hello 的行数:"
grep "hello" "$BASE_DIR/redirect.txt" | wc -l
ls /not-exist-dir > "$BASE_DIR/error.log" 2>&1 || true
echo "错误日志已写入:$BASE_DIR/error.log"
}
###############################################################################
# 知识点 15:grep 文本过滤
###############################################################################
grep_demo() {
print_title "知识点 15:grep 文本过滤"
echo "筛选包含 linux 的行:"
grep "linux" "$DATA_FILE"
echo "筛选不包含 linux 的行:"
grep -v "linux" "$DATA_FILE"
}
###############################################################################
# 知识点 16:awk 按列处理
# awk 默认按空格分列
# $1 第一列,$2 第二列,$3 第三列
###############################################################################
awk_demo() {
print_title "知识点 16:awk 按列处理"
echo "只打印用户名:"
awk '{print $1}' "$DATA_FILE"
echo "打印年龄大于 20 的用户:"
awk '$2 > 20 {print $1, $2, $3}' "$DATA_FILE"
}
###############################################################################
# 知识点 17:sed 文本替换
# s/旧内容/新内容/g 表示替换
###############################################################################
sed_demo() {
print_title "知识点 17:sed 文本替换"
echo "把 shell 替换成 bash,只显示不修改原文件:"
sed 's/shell/bash/g' "$DATA_FILE"
cp "$DATA_FILE" "$BASE_DIR/users_sed.txt"
sed -i 's/docker/container/g' "$BASE_DIR/users_sed.txt"
echo "替换后的文件:$BASE_DIR/users_sed.txt"
}
###############################################################################
# 知识点 18:find 查找文件
###############################################################################
find_demo() {
print_title "知识点 18:find 查找文件"
echo "查找 $BASE_DIR 下的 txt 文件:"
find "$BASE_DIR" -type f -name "*.txt"
}
###############################################################################
# 知识点 19:xargs 参数传递
# 把前面命令的输出,作为后面命令的参数
###############################################################################
xargs_demo() {
print_title "知识点 19:xargs 参数传递"
echo "统计 txt 文件行数:"
find "$BASE_DIR" -type f -name "*.txt" | xargs wc -l
}
###############################################################################
# 知识点 20:读取配置文件 source
# source 可以加载另一个文件里的变量
###############################################################################
source_demo() {
print_title "知识点 20:source 加载配置"
# 把 KEY=VALUE 格式的配置加载进当前脚本
source "$CONFIG_FILE"
echo "应用名称:$APP_NAME"
echo "运行环境:$APP_ENV"
echo "端口:$PORT"
}
###############################################################################
# 知识点 21:命令执行状态
# if command; then 可以直接判断命令是否成功
###############################################################################
command_status_demo() {
print_title "知识点 21:命令执行状态"
if grep "shell" "$DATA_FILE" > /dev/null 2>&1; then
echo "文件中存在 shell 关键词"
else
echo "文件中不存在 shell 关键词"
fi
}
###############################################################################
# 知识点 22:进程检查
# ps:查看进程
# pgrep:按名字查进程
###############################################################################
process_demo() {
print_title "知识点 22:进程检查"
echo "当前 bash 相关进程:"
ps aux | grep bash | grep -v grep || true
if pgrep bash > /dev/null 2>&1; then
echo "bash 进程存在"
else
echo "bash 进程不存在"
fi
}
###############################################################################
# 知识点 23:网络检查
# ping:检查网络连通性
# curl:检查网站接口
###############################################################################
network_demo() {
print_title "知识点 23:网络检查"
if ping -c 1 -W 1 8.8.8.8 > /dev/null 2>&1; then
echo "网络连通:8.8.8.8"
else
echo "网络不通或被限制:8.8.8.8"
fi
if command -v curl > /dev/null 2>&1; then
echo "curl 已安装"
curl -I -s https://www.baidu.com | head -n 1 || true
else
echo "curl 未安装"
fi
}
###############################################################################
# 知识点 24:压缩和备份
# tar -czf:打包并 gzip 压缩
###############################################################################
backup_demo() {
print_title "知识点 24:压缩和备份"
local backup_file="$BACKUP_DIR/shell_practice_$TODAY.tar.gz"
tar -czf "$backup_file" -C "$BASE_DIR" .
echo "备份文件已生成:$backup_file"
}
###############################################################################
# 知识点 25:删除旧文件
# find + -mtime 查找 N 天前文件
# 注意:真实生产环境删除前一定要先确认
###############################################################################
clean_demo() {
print_title "知识点 25:删除旧文件"
echo "演示查找 7 天前的备份文件,不真正删除:"
find "$BACKUP_DIR" -type f -name "*.tar.gz" -mtime +7 -print
# 真正删除时才使用下面这行:
# find "$BACKUP_DIR" -type f -name "*.tar.gz" -mtime +7 -delete
}
###############################################################################
# 知识点 26:here document 多行文本
# 常用于生成配置文件
###############################################################################
heredoc_demo() {
print_title "知识点 26:Here Document"
cat > "$BASE_DIR/nginx_demo.conf" <<EOF
server {
listen 80;
server_name example.com;
location / {
root /usr/share/nginx/html;
}
}
EOF
echo "已生成配置文件:$BASE_DIR/nginx_demo.conf"
}
###############################################################################
# 知识点 27:trap 退出清理
# EXIT:脚本退出时执行
###############################################################################
cleanup() {
echo
echo "脚本即将退出,执行清理动作"
echo "日志文件位置:$LOG_FILE"
}
trap cleanup EXIT
###############################################################################
# 知识点 28:菜单函数
# 方便每天选择性练习
###############################################################################
show_menu() {
echo
echo "请选择练习内容:"
echo "1) 全部执行"
echo "2) 参数与变量"
echo "3) 判断与循环"
echo "4) 文本处理 grep/awk/sed"
echo "5) 运维实战:进程/网络/备份"
echo "0) 退出"
}
###############################################################################
# 知识点 29:read 读取用户输入
###############################################################################
main() {
prepare_files
print_title "脚本基本信息"
echo "脚本名称:$SCRIPT_NAME"
echo "作者:$AUTHOR"
echo "当前时间:$NOW"
show_menu
read -r -p "请输入选项:" choice
case "$choice" in
1)
show_args "$@"
check_file_and_dir
number_compare
string_compare
for_loop_demo
while_loop_demo
case_demo "${1:-status}"
array_demo
arithmetic_demo
pipe_redirect_demo
grep_demo
awk_demo
sed_demo
find_demo
xargs_demo
source_demo
command_status_demo
process_demo
network_demo
backup_demo
clean_demo
heredoc_demo
;;
2)
show_args "$@"
array_demo
arithmetic_demo
source_demo
;;
3)
check_file_and_dir
number_compare
string_compare
for_loop_demo
while_loop_demo
case_demo "${1:-status}"
;;
4)
pipe_redirect_demo
grep_demo
awk_demo
sed_demo
find_demo
xargs_demo
;;
5)
command_status_demo
process_demo
network_demo
backup_demo
clean_demo
heredoc_demo
;;
0)
echo "退出练习"
exit 0
;;
*)
echo "无效选项"
exit 1
;;
esac
log "本次练习执行完成"
}
###############################################################################
# 知识点 30:主入口
# "$@" 表示把脚本接收到的所有参数继续传给 main 函数
###############################################################################
main "$@"