三天内速学liunx shell 脚 本 快速过一遍

三天内解决 ——快速学习 Linux Shell 文档

liunx  shell  通常我们想定时上传文件,或下载,或清理日志,统一执行,也可以执行一些简单任务,
需要人不在线场让电脑自动操作电脑的时候,
真正写代码的时候以前都 是抄百度,不过现在有ai 了 ,更方便,不过还是建议我们提前快速学一遍、

 

适用对象:零基础到进阶开发者,对liunx 不熟悉的朋友
学习目标:读完本文档,能独立编写简单的脚本,能运行起来就好,因为后期我们还有更好的工具
阅读建议:每章先通读概念,再动手敲一遍代码,最后对照「本章小结」复盘

第一章:Shell 基础入门

1.1 什么是 Shell?什么时候用它?

Shell 是用户与 Linux 内核之间的"翻译官"。你输入的是人类能看懂的命令,Shell 把它翻译成内核能执行的操作。
什么时候写 Shell 脚本?
  • 需要把多个命令组合在一起,一键执行
  • 需要定时自动执行任务(如每天凌晨备份数据库)
  • 需要批量处理文件(如批量重命名 1000 张图片)
  • 需要快速搭建自动化部署流程
学习路径:
plain
 
看懂命令 → 写简单脚本 → 加判断逻辑 → 写函数复用 → 处理文本 → 实战项目
 

1.2 第一个 Shell 脚本:Hello World

目标:创建一个文件,运行后输出 "Hello, Shell!"
分几步:
  1. 创建脚本文件
  2. 第一行声明用哪个 Shell 解释器
  3. 写执行代码
  4. 给文件加执行权限
  5. 运行脚本
bash
 
#!/bin/bash
# 上面这行叫 "Shebang",告诉系统用 /bin/bash 来执行这个脚本
# # 开头的是注释,程序不会执行

echo "Hello, Shell!"
echo "当前时间是: $(date)"
echo "当前用户是: $USER"
 
运行方式:
bash
 
# 方式一:给执行权限后直接运行(推荐)
chmod +x hello.sh
./hello.sh

# 方式二:用 bash 命令直接解释执行(不需要 chmod)
bash hello.sh
 

1.3 变量:脚本的数据容器

目标:学会定义变量、使用变量、理解特殊变量
分几步:
  1. 定义变量(等号两边不能有空格!)
  2. 使用变量(前面加 $
  3. 了解只读变量和删除变量
  4. 掌握常用的特殊变量
bash
 
#!/bin/bash

# ========== 自定义变量 ==========
name="张三"           # 字符串变量
age=25               # 数字变量(Shell 里其实全是字符串)
pi=3.14159

# 使用变量:前面加 $,建议用 ${} 包裹,防止歧义
echo "姓名: $name"
echo "年龄: ${age}岁"

# ========== 只读变量 ==========
readonly MAX_RETRY=3
# MAX_RETRY=5   # 这行会报错,因为只读变量不能修改

# ========== 删除变量 ==========
temp="临时数据"
unset temp
# echo $temp    # 输出空,因为变量已被删除

# ========== 特殊变量(超级重要) ==========
echo "脚本名称: $0"
echo "第一个参数: $1"
echo "第二个参数: $2"
echo "参数总数: $#"
echo "所有参数: $*"
echo "脚本进程号: $$"
echo "上一条命令执行结果: $?"   # 0 表示成功,非0表示失败
 
运行测试:
bash
 
./test.sh hello world 123
 

1.4 输入与输出

目标:学会与用户交互,读写文件
bash
 
#!/bin/bash

# ========== 输出 ==========
echo "普通输出"
echo -e "带颜色输出: \033[31m红色文字\033[0m"  # -e 启用转义

# ========== 输入 ==========
read -p "请输入你的名字: " username
echo "你好, $username!"

read -s -p "请输入密码(隐藏输入): " password   # -s 隐藏输入
echo ""  # 换行
echo "密码长度: ${#password}"

# ========== 重定向 ==========
echo "这是日志" > log.txt      # > 覆盖写入
echo "追加内容" >> log.txt     # >> 追加写入
ls /notexist 2> error.log     # 2> 把错误信息写入文件

# ========== 命令替换 ==========
# 方式一:反引号(旧写法,不推荐)
current_dir=`pwd`

# 方式二:$()(新写法,推荐,支持嵌套)
current_dir=$(pwd)
echo "当前目录: $current_dir"

files=$(ls /home)
echo "文件列表: $files"
 

📌 第一章小结

表格
 
 
知识点 关键记忆
Shebang #!/bin/bash 必须放第一行
变量定义 name="值",等号两侧不能有空格
变量使用 $name${name}
特殊变量 $0 $1 $# $* $? $$ 必背
输入 read -p "提示: " 变量名
输出 echo "内容"> 覆盖,>> 追加
命令替换 $(命令)

第二章:流程控制

2.1 条件判断 if 语句

目标:让脚本根据不同情况执行不同代码
分几步:
  1. 理解判断语法 [ 条件 ](方括号两侧必须有空格!)
  2. 学会数字比较、字符串比较、文件判断
  3. 掌握 if / elif / else 结构
bash
 
#!/bin/bash

read -p "请输入一个数字: " num

# ========== 数字比较 ==========
# -eq 等于  -ne 不等于  -gt 大于  -ge 大于等于  -lt 小于  -le 小于等于
if [ $num -gt 10 ]; then
    echo "$num 大于 10"
elif [ $num -eq 10 ]; then
    echo "$num 等于 10"
else
    echo "$num 小于 10"
fi

# ========== 字符串比较 ==========
# = 等于  != 不等于  -z 长度为0  -n 长度非0
name="admin"
if [ "$name" = "admin" ]; then
    echo "管理员身份确认"
fi

# ========== 文件判断 ==========
# -e 文件存在  -f 是普通文件  -d 是目录  -r 可读  -w 可写  -x 可执行
if [ -f "log.txt" ]; then
    echo "log.txt 存在"
else
    echo "log.txt 不存在,正在创建..."
    touch log.txt
fi

# ========== 逻辑组合 ==========
# -a 表示 AND,-o 表示 OR
age=25
if [ $age -ge 18 -a $age -le 60 ]; then
    echo "属于劳动年龄"
fi

# 或者用 [[ ]] 支持更现代的写法(推荐)
if [[ $age -ge 18 && $age -le 60 ]]; then
    echo "属于劳动年龄(现代写法)"
fi
 

2.2 循环结构

目标:批量处理数据,自动化重复任务

for 循环

bash
 
#!/bin/bash

# ========== 方式一:遍历列表 ==========
echo "=== 遍历数字 ==="
for i in 1 2 3 4 5; do
    echo "当前数字: $i"
done

# ========== 方式二:遍历范围 ==========
echo "=== 遍历范围 ==="
for i in {1..5}; do
    echo "第 $i 次循环"
done

# ========== 方式三:C语言风格 ==========
echo "=== C风格循环 ==="
for ((i=0; i<5; i++)); do
    echo "i = $i"
done

# ========== 方式四:遍历命令结果 ==========
echo "=== 遍历当前目录的 .sh 文件 ==="
for file in $(ls *.sh 2>/dev/null); do
    echo "发现脚本: $file"
done

# ========== 实战:批量重命名 ==========
# 把当前目录所有 .txt 改成 .bak
for file in *.txt; do
    [ -f "$file" ] || continue   # 如果没有匹配文件,跳过
    newname="${file%.txt}.bak"   # 去掉 .txt 后缀,加上 .bak
    mv "$file" "$newname"
    echo "已重命名: $file -> $newname"
done
 

while 循环

bash
 
#!/bin/bash

# ========== 基本 while ==========
count=1
while [ $count -le 5 ]; do
    echo "计数: $count"
    count=$((count + 1))   # 或者 ((count++))
done

# ========== 读取文件逐行处理 ==========
# 场景:逐行读取配置文件,处理每一行
while read line; do
    echo "处理: $line"
done < log.txt

# ========== 无限循环 + 条件退出 ==========
while true; do
    read -p "输入 'exit' 退出: " input
    if [ "$input" = "exit" ]; then
        echo "程序结束"
        break   # 跳出循环
    fi
    echo "你输入了: $input"
done
 

until 循环(条件为假时执行)

bash
 
#!/bin/bash

# until 和 while 相反:条件为假时执行,为真时停止
count=1
until [ $count -gt 5 ]; do
    echo "count = $count"
    ((count++))
done
 

2.3 case 多分支选择

目标:替代多个 if-elif,代码更清晰
bash
 
#!/bin/bash

read -p "请输入操作 [start|stop|restart|status]: " action

case $action in
    start)
        echo "正在启动服务..."
        # 启动命令写这里
        ;;
    stop)
        echo "正在停止服务..."
        # 停止命令写这里
        ;;
    restart)
        echo "正在重启服务..."
        # 重启命令写这里
        ;;
    status)
        echo "服务状态: 运行中"
        ;;
    *)   # * 匹配所有其他情况,类似 default
        echo "未知操作: $action"
        echo "用法: $0 {start|stop|restart|status}"
        exit 1
        ;;
esac
 

📌 第二章小结

表格
 
 
知识点 关键记忆
if 判断 [ 条件 ] 两侧必须有空格
数字比较 -eq -ne -gt -ge -lt -le
字符串比较 = != -z -n
文件判断 -e -f -d -r -w -x
for 循环 for i in 列表for ((;;))
while 循环 while [ 条件 ]; do ... done
case case $var in pattern) ... ;; esac
跳出循环 break 跳出,continue 跳过本次

第三章:函数与数组

3.1 函数:代码复用的利器

目标:把重复代码封装成函数,提高可维护性
分几步:
  1. 定义函数
  2. 调用函数
  3. 传递参数
  4. 返回值处理
bash
 
#!/bin/bash

# ========== 定义函数 ==========
# 方式一
function say_hello() {
    echo "你好, $1!"   # $1 是传入的第一个参数
    echo "今天天气: $2"
}

# 方式二(不带 function 关键字)
check_file() {
    local filepath=$1   # local 表示局部变量,只在函数内有效
    
    if [ -f "$filepath" ]; then
        echo "文件存在"
        return 0        # return 返回数字状态码,0表示成功
    else
        echo "文件不存在"
        return 1        # 非0表示失败
    fi
}

# ========== 调用函数 ==========
say_hello "张三" "晴朗"

# ========== 获取函数返回值 ==========
check_file "test.txt"
result=$?   # $? 获取上一条命令/函数的返回状态码
if [ $result -eq 0 ]; then
    echo "检查通过"
else
    echo "检查失败"
fi

# ========== 函数返回字符串(用 echo + 命令替换) ==========
get_timestamp() {
    echo "$(date '+%Y-%m-%d %H:%M:%S')"
}

ts=$(get_timestamp)
echo "当前时间戳: $ts"

# ========== 实战:日志函数 ==========
log() {
    local level=$1      # INFO/WARN/ERROR
    local message=$2
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    
    echo "[$timestamp] [$level] $message"
}

log "INFO" "系统启动"
log "ERROR" "数据库连接失败"
 

3.2 数组:批量存储数据

目标:用数组存储多个值,批量处理
bash
 
#!/bin/bash

# ========== 定义数组 ==========
fruits=("苹果" "香蕉" "橙子" "葡萄")

# ========== 访问元素 ==========
echo "第一个水果: ${fruits[0]}"      # 苹果
echo "第二个水果: ${fruits[1]}"      # 香蕉

# ========== 数组操作 ==========
echo "数组长度: ${#fruits[@]}"       # 4
echo "所有元素: ${fruits[@]}"        # 苹果 香蕉 橙子 葡萄
echo "所有索引: ${!fruits[@]}"       # 0 1 2 3

# ========== 遍历数组 ==========
echo "=== 遍历数组 ==="
for fruit in "${fruits[@]}"; do
    echo "水果: $fruit"
done

# ========== 带索引遍历 ==========
echo "=== 带索引遍历 ==="
for i in "${!fruits[@]}"; do
    echo "索引 $i: ${fruits[$i]}"
done

# ========== 添加/删除元素 ==========
fruits[4]="西瓜"           # 添加
fruits+=("芒果")           # 追加
unset fruits[1]            # 删除索引1的元素

echo "修改后: ${fruits[@]}"

# ========== 关联数组(键值对,需要 bash 4.0+)==========
declare -A user_info       # -A 声明关联数组
user_info["name"]="张三"
user_info["age"]="25"
user_info["city"]="深圳"

echo "用户名: ${user_info["name"]}"
echo "所有键: ${!user_info[@]}"
echo "所有值: ${user_info[@]}"
 

📌 第三章小结

表格
 
 
知识点 关键记忆
函数定义 function name() { ... }name() { ... }
函数参数 函数内用 $1 $2 接收,类似脚本参数
局部变量 local var=value
返回状态 return 0/1,用 $? 接收
返回字符串 函数内 echo,调用处 $(函数名)
普通数组 arr=(a b c)${arr[0]}${#arr[@]}
关联数组 declare -A arrarr["key"]="value"

第四章:文本处理三剑客

4.1 grep:文本搜索

目标:从大量文本中快速找到想要的内容
bash
 
#!/bin/bash

# ========== 基础搜索 ==========
grep "error" log.txt              # 在文件中搜索包含 "error" 的行
grep -i "error" log.txt           # -i 忽略大小写
grep -v "error" log.txt           # -v 反向匹配,显示不包含 "error" 的行
grep -n "error" log.txt           # -n 显示行号

# ========== 正则匹配 ==========
grep "^2024" log.txt              # ^ 开头:以 2024 开头的行
grep "error$" log.txt             # $ 结尾:以 error 结尾的行
grep "err[oa]r" log.txt           # [] 匹配其中一个字符:error 或 errar
grep "err.*r" log.txt             # .* 匹配任意字符

# ========== 在脚本中使用 grep ==========
# 场景:检查进程是否在运行
if pgrep nginx > /dev/null; then
    echo "Nginx 正在运行"
else
    echo "Nginx 未运行"
fi

# 场景:统计错误数量
error_count=$(grep -c "ERROR" app.log)
echo "错误数量: $error_count"
 

4.2 sed:流编辑器

目标:不打开文件,直接对文本进行增删改查
bash
 
#!/bin/bash

# ========== 替换 ==========
# sed 's/旧内容/新内容/' 文件
sed 's/apple/orange/' fruits.txt        # 每行第一个 apple 替换为 orange
sed 's/apple/orange/g' fruits.txt       # g 表示全局替换,一行内所有 apple
sed 's/apple/orange/2' fruits.txt       # 只替换每行第2个

# ========== 直接修改文件(-i)==========
sed -i 's/localhost/127.0.0.1/g' config.txt

# ========== 删除行 ==========
sed '3d' file.txt               # 删除第3行
sed '/^#/d' file.txt            # 删除以 # 开头的行(注释)
sed '/^$/d' file.txt            # 删除空行

# ========== 在脚本中使用 sed ==========
# 场景:修改配置文件中的端口号
config_file="server.conf"
old_port="8080"
new_port="9090"

sed -i "s/port=${old_port}/port=${new_port}/g" "$config_file"
echo "端口已修改为 $new_port"

# 场景:提取特定行
sed -n '5,10p' log.txt          # 只显示第5到10行
 

4.3 awk:文本分析神器

目标:按列处理数据,做统计计算
bash
 
#!/bin/bash

# 假设有一个文件 data.txt,内容如下:
# 张三 25 8000
# 李四 30 12000
# 王五 28 9500

# ========== 基础用法 ==========
awk '{print $1}' data.txt       # 打印第一列(姓名)
awk '{print $1, $3}' data.txt   # 打印第一列和第三列
awk '{print "姓名:", $1, "工资:", $3}' data.txt

# ========== 带条件过滤 ==========
awk '$2 > 26 {print $1}' data.txt      # 年龄大于26的姓名
awk '$3 >= 10000 {print $1, $3}' data.txt  # 工资大于等于10000的

# ========== 计算 ==========
awk '{sum += $3} END {print "总工资:", sum}' data.txt
awk '{sum += $3; count++} END {print "平均工资:", sum/count}' data.txt

# ========== 指定分隔符 ==========
awk -F: '{print $1}' /etc/passwd   # 以 : 为分隔符,打印用户名

# ========== 在脚本中使用 awk ==========
# 场景:获取本机 IP
ip=$(ip addr show eth0 | awk '/inet / {print $2}' | awk -F/ '{print $1}')
echo "本机IP: $ip"

# 场景:分析日志,统计每个 IP 访问次数
awk '{count[$1]++} END {for(ip in count) print ip, count[ip]}' access.log
 

📌 第四章小结

表格
 
 
命令 核心用途 关键参数
grep 搜索文本 -i忽略大小写, -v反向, -n行号, -c计数
sed 替换/删除 s/旧/新/g, d删除, -i直接修改
awk 按列处理/计算 {print $1}, -F:指定分隔符, END{}收尾处理

第五章:高级特性

5.1 正则表达式

目标:用模式匹配复杂的文本规则
bash
 
#!/bin/bash

# ========== 基础元字符 ==========
# .     匹配任意一个字符
# *     匹配前一个字符0次或多次
# +     匹配前一个字符1次或多次(grep 用 -E)
# ?     匹配前一个字符0次或1次
# ^     开头
# $     结尾
# []    匹配括号内任意一个字符
# [^]   匹配不在括号内的字符
# {}    匹配次数,如 {3} 正好3次,{2,5} 2到5次

# ========== 实战示例 ==========
# 验证邮箱格式
email="test@example.com"
if [[ $email =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
    echo "邮箱格式正确"
else
    echo "邮箱格式错误"
fi

# 验证手机号(中国大陆)
phone="13800138000"
if [[ $phone =~ ^1[3-9][0-9]{9}$ ]]; then
    echo "手机号格式正确"
else
    echo "手机号格式错误"
fi

# 提取日志中的 IP 地址
grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' access.log
 

5.2 进程管理与后台任务

bash
 
#!/bin/bash

# ========== 后台运行 ==========
./long_task.sh &            # & 放到后台运行
nohup ./task.sh &           # nohup 用户退出后继续运行

# ========== 作业控制 ==========
jobs                        # 查看后台任务
fg %1                       # 把后台任务1调到前台
bg %1                       # 把暂停的任务1放到后台继续
kill %1                     # 结束后台任务1

# ========== 等待进程 ==========
./task1.sh &
pid1=$!

./task2.sh &
pid2=$!

wait $pid1                  # 等待 task1 完成
echo "任务1完成"

wait                        # 等待所有后台任务完成
echo "所有任务完成"
 

5.3 信号处理

目标:让脚本优雅地处理中断(如 Ctrl+C)
bash
 
#!/bin/bash

# ========== 定义信号处理函数 ==========
cleanup() {
    echo ""
    echo "收到中断信号,正在清理..."
    # 删除临时文件
    rm -f /tmp/my_script_*.tmp
    echo "清理完成,退出"
    exit 0
}

# ========== 注册信号处理器 ==========
trap cleanup INT TERM EXIT
# INT = Ctrl+C
# TERM = kill 命令
# EXIT = 脚本正常退出时也会执行

# ========== 主程序 ==========
echo "脚本运行中,按 Ctrl+C 测试信号处理..."
for i in {1..100}; do
    echo "工作中... $i"
    sleep 1
done
 

5.4 文件描述符与重定向进阶

bash
 
#!/bin/bash

# ========== 标准文件描述符 ==========
# 0 = 标准输入 (stdin)
# 1 = 标准输出 (stdout)
# 2 = 标准错误 (stderr)

# ========== 同时重定向输出和错误 ==========
command > output.log 2>&1       # 输出和错误都写入文件
command &> output.log           # 同上(bash 4.0+)

# ========== 丢弃输出 ==========
command > /dev/null 2>&1        # 静默执行,什么都不显示

# ========== 自定义文件描述符 ==========
exec 3<> data.txt               # 打开文件,3 是文件描述符
echo "写入数据" >&3
exec 3<&-                       # 关闭文件描述符
 

📌 第五章小结

表格
 
 
知识点 关键记忆
正则匹配 [[ $var =~ 正则 ]]
后台运行 命令 &nohup 命令 &
信号捕获 trap '命令' 信号
重定向 > 文件 2>&1/dev/null

第六章:实战项目

项目一:日志分析脚本

需求:分析 Nginx 访问日志,输出访问统计报告
bash
 
#!/bin/bash

LOG_FILE="${1:-/var/log/nginx/access.log}"   # 默认日志路径
REPORT_FILE="report_$(date +%Y%m%d).txt"

if [ ! -f "$LOG_FILE" ]; then
    echo "错误: 日志文件 $LOG_FILE 不存在"
    exit 1
fi

echo "========== 访问日志分析报告 ==========" > "$REPORT_FILE"
echo "分析时间: $(date)" >> "$REPORT_FILE"
echo "日志文件: $LOG_FILE" >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"

# 总请求数
total=$(wc -l < "$LOG_FILE")
echo "总请求数: $total" >> "$REPORT_FILE"

# 独立 IP 数
unique_ips=$(awk '{print $1}' "$LOG_FILE" | sort -u | wc -l)
echo "独立IP数: $unique_ips" >> "$REPORT_FILE"

# 请求最多的 10 个 IP
echo "" >> "$REPORT_FILE"
echo "TOP 10 IP:" >> "$REPORT_FILE"
awk '{print $1}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -10 >> "$REPORT_FILE"

# 请求最多的 10 个 URL
echo "" >> "$REPORT_FILE"
echo "TOP 10 URL:" >> "$REPORT_FILE"
awk '{print $7}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -10 >> "$REPORT_FILE"

# 状态码统计
echo "" >> "$REPORT_FILE"
echo "状态码分布:" >> "$REPORT_FILE"
awk '{print $9}' "$LOG_FILE" | sort | uniq -c | sort -rn >> "$REPORT_FILE"

echo "报告已生成: $REPORT_FILE"
cat "$REPORT_FILE"
 

项目二:系统监控脚本

需求:监控 CPU、内存、磁盘,超过阈值时报警
bash
 
#!/bin/bash

# ========== 配置阈值 ==========
CPU_THRESHOLD=80        # CPU 使用率阈值
MEM_THRESHOLD=80        # 内存使用率阈值
DISK_THRESHOLD=90       # 磁盘使用率阈值
LOG_FILE="/var/log/system_monitor.log"

# ========== 日志函数 ==========
log_msg() {
    local level=$1
    local msg=$2
    local ts=$(date '+%Y-%m-%d %H:%M:%S')
    echo "[$ts] [$level] $msg" | tee -a "$LOG_FILE"
}

# ========== 检查 CPU ==========
check_cpu() {
    # 取 1 分钟平均负载,与 CPU 核心数比较
    local load=$(uptime | awk -F'load average:' '{print $2}' | awk '{print $1}' | tr -d ',')
    local cores=$(nproc)
    local usage=$(echo "scale=2; $load * 100 / $cores" | bc)
    
    if (( $(echo "$usage > $CPU_THRESHOLD" | bc -l) )); then
        log_msg "WARN" "CPU 使用率过高: ${usage}% (阈值: ${CPU_THRESHOLD}%)"
    else
        log_msg "INFO" "CPU 正常: ${usage}%"
    fi
}

# ========== 检查内存 ==========
check_memory() {
    local mem_info=$(free | grep Mem)
    local total=$(echo $mem_info | awk '{print $2}')
    local used=$(echo $mem_info | awk '{print $3}')
    local usage=$(echo "scale=2; $used * 100 / $total" | bc)
    
    if (( $(echo "$usage > $MEM_THRESHOLD" | bc -l) )); then
        log_msg "WARN" "内存使用率过高: ${usage}% (阈值: ${MEM_THRESHOLD}%)"
    else
        log_msg "INFO" "内存正常: ${usage}%"
    fi
}

# ========== 检查磁盘 ==========
check_disk() {
    df -h | awk 'NR>1 {
        gsub(/%/,"",$5)
        if ($5 > '"$DISK_THRESHOLD"') {
            print "WARN|分区 " $6 " 使用率: " $5 "%"
        }
    }' | while IFS='|' read level msg; do
        log_msg "$level" "$msg"
    done
}

# ========== 主程序 ==========
log_msg "INFO" "========== 开始系统监控 =========="
check_cpu
check_memory
check_disk
log_msg "INFO" "========== 监控结束 =========="
 

项目三:自动化部署脚本

需求:一键部署 Web 应用,包含备份、更新、回滚
bash
 
#!/bin/bash

# ========== 配置 ==========
APP_NAME="myweb"
APP_DIR="/var/www/$APP_NAME"
BACKUP_DIR="/backup/$APP_NAME"
GIT_REPO="https://github.com/user/myweb.git"
BRANCH="main"

# ========== 颜色输出 ==========
RED='\033[31m'
GREEN='\033[32m'
YELLOW='\033[33m'
NC='\033[0m'

log() { echo -e "${GREEN}[INFO]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; }

# ========== 备份 ==========
backup() {
    local backup_name="backup_$(date +%Y%m%d_%H%M%S).tar.gz"
    mkdir -p "$BACKUP_DIR"
    
    if [ -d "$APP_DIR" ]; then
        tar -czf "$BACKUP_DIR/$backup_name" -C "$(dirname $APP_DIR)" "$(basename $APP_DIR)"
        log "备份完成: $backup_name"
    else
        warn "应用目录不存在,跳过备份"
    fi
}

# ========== 部署 ==========
deploy() {
    log "开始部署 $APP_NAME ..."
    
    # 1. 备份
    backup
    
    # 2. 拉取代码
    if [ -d "$APP_DIR/.git" ]; then
        cd "$APP_DIR" || error "进入目录失败"
        git pull origin "$BRANCH" || error "拉取代码失败"
    else
        git clone -b "$BRANCH" "$GIT_REPO" "$APP_DIR" || error "克隆代码失败"
    fi
    
    # 3. 安装依赖(示例:Node.js 项目)
    cd "$APP_DIR" || error "进入目录失败"
    if [ -f "package.json" ]; then
        npm install || error "安装依赖失败"
        npm run build || error "构建失败"
    fi
    
    # 4. 重启服务
    if systemctl is-active --quiet "$APP_NAME"; then
        systemctl restart "$APP_NAME" || error "重启服务失败"
    else
        warn "服务 $APP_NAME 未运行,请手动启动"
    fi
    
    log "部署完成!"
}

# ========== 回滚 ==========
rollback() {
    local latest_backup=$(ls -t "$BACKUP_DIR"/*.tar.gz 2>/dev/null | head -1)
    
    if [ -z "$latest_backup" ]; then
        error "没有找到备份文件"
    fi
    
    log "开始回滚,使用备份: $(basename $latest_backup)"
    
    # 停止服务
    systemctl stop "$APP_NAME" 2>/dev/null
    
    # 恢复备份
    rm -rf "$APP_DIR"
    tar -xzf "$latest_backup" -C "$(dirname $APP_DIR)"
    
    # 启动服务
    systemctl start "$APP_NAME"
    
    log "回滚完成!"
}

# ========== 主入口 ==========
case "${1:-deploy}" in
    deploy)
        deploy
        ;;
    rollback)
        rollback
        ;;
    backup)
        backup
        ;;
    *)
        echo "用法: $0 {deploy|rollback|backup}"
        exit 1
        ;;
esac
 

附录:常用命令速查表

表格
 
 
命令 作用 示例
echo 输出文本 echo "hello"
read 读取输入 read -p "提示: " var
test / [ ] 条件测试 [ $a -eq $b ]
expr 算术运算 expr 1 + 2
let 算术运算 let a=1+2
$(( )) 算术运算 echo $((1+2))
source / . 执行脚本(当前环境) source script.sh
chmod 修改权限 chmod +x script.sh
cut 按列切割 cut -d: -f1 /etc/passwd
sort 排序 sort -n file.txt
uniq 去重 sort file | uniq -c
wc 统计行数/字数 wc -l file.txt
find 查找文件 find / -name "*.sh"
xargs 参数传递 find . -name "*.log" | xargs rm
curl 网络请求 curl -o file.zip URL
wget 下载文件 wget URL

写在最后

Shell 脚本学习心法:
  1. 先抄后改:把本文档的代码全部敲一遍,不要复制粘贴
  2. 小步快跑:每个知识点写一个小脚本验证,不要一次性写大脚本
  3. 善用 set -e:在脚本开头加 set -e,遇到错误立即退出,避免隐患
  4. 多用双引号:变量尽量用 "$var" 包裹,防止空格和特殊字符导致的问题
  5. 多查 man 手册man bash 是最权威的参考书
关键口诀:变量加 $,判断加空格,循环有结构,函数要复用,文本三剑客,实战出真知。
posted @ 2026-07-29 18:42  鬼门元歌  阅读(6)  评论(0)    收藏  举报