事情是这样的。上个月换了一家公司,接手的第一天就被安排了一个任务——维护一个 3000 行的 deploy.sh。打开一看,200 多行没人知道是干什么的、变量名是 a b c、没有函数封装、一堆 cd /tmp && rm -rf 裸写。更离谱的是,整个公司的自动化脚本分布在 7 台服务器上,没有版本控制,没有任何注释。
这种场景我估计很多运维同行都遇到过。
我用 OpenCode 把整个公司的老旧脚本重构了一遍,从 3000 行缩减到 1200 行,全部标准化。这篇文章就是我踩完坑后整理出来的完整方案——用开源 AI 工具帮你清洗、重构、测试一键搞定。
这篇是 Ollama + OpenCode + VS Code 本地补全方案 的实战篇。如果你还没学会基础操作,先看那篇把环境搭起来。
OpenCode 支持用自然语言直接扫描整个目录结构。不需要写复杂命令,对话式指挥就行。
# 先 cd 到你的旧脚本仓库
cd /data/deploy/scripts
# 进入 OpenCode 交互模式
opencode
在 OpenCode 终端里输入以下内容:
> 扫描当前目录下的所有 .sh 文件,帮我做结构分析:
> 1. 列出所有脚本,标注行数
> 2. 统计所有没有函数封装的"裸代码"行数(顶层执行的,不是在函数里的)
> 3. 统计未定义变量(grep 没用 set -u 的脚本)
> 4. 统计没有任何注释的脚本
> 5. 标记可能包含敏感信息(密码、API Key 等)的脚本
(实测)我的旧项目有 43 个 .sh 文件,OpenCode 花了大约 10 分钟扫描完。结果让人头皮发麻:32 个没有函数封装、21 个没有注释、5 个硬编码了服务器密码。
不要一次性全改,分批来。
| 优先级 | 类型 | 典型脚本 | 数量 | 处理方式 |
|---|---|---|---|---|
| P0 | 含敏感信息的脚本 | config.sh、db_conn.sh | 5 个 | 立即脱敏 + 重构 |
| P1 | 线上正在跑的脚本 | deploy.sh、backup.sh | 12 个 | 逐个重构 + 测试 |
| P2 | 有注释但结构乱 | init.sh、cleanup.sh | 15 个 | 标准化后重构 |
| P3 | 半年没用的脚本 | old_migrate.sh、legacy*.sh | 11 个 | 先确认能否删除,再重构 |
(实测)我们上线后第一件事是处理 P0 的 5 个脚本。它们里硬编码了数据库密码和 API Key,改完后让所有密码从环境变量读取。
> 分析 deploy.sh(当前 3000 行),告诉我:
> 1. 哪些代码块可以提取为独立函数
> 2. 哪些逻辑有重复(重复的代码段)
> 3. 有哪些明显的安全风险
> 4. 哪些错误处理根本不存在
输出结构:
deploy.sh 结构分析
- 核心逻辑:构建 → 测试 → 发布(需要保留)
- 废弃逻辑:约 800 行的日志清理(可删除)
- 重复代码:3 处相同的磁盘检查逻辑(可合并)
- 安全风险:密码硬编码(第 37 行)、路径未引号包裹(第 58/63/78 行)
缺失处理:没有 set -e、没有 trap
(实测)3000 行分析完毕后,我直接删掉了 800 行废弃的日志清理逻辑。公司去年就停用了那个日志系统,没人知道这 800 行还是上线状态的一部分。
分析完成后,直接在 OpenCode 里让它重写:
> 把 deploy.sh 重构成结构化的 Shell 脚本:
> 1. 把所有全局变量提取到脚本头部(用大写变量名 + 注释)
> 2. 核心逻辑拆成独立函数(init()、build()、deploy()、rollback())
> 3. 所有错误处理统一用 error_exit() 函数处理
> 4. 所有路径引用用双引号包裹(避免空格/特殊字符导致错误)
> 5. 密码使用环境变量(如 ${DB_PASSWORD}),不要硬编码
> 6. 输出统一的日志格式:`[2026-07-26 10:00:01] [INFO/ERROR] 信息`
> 7. 顶部加 set -euo pipefail(遇到错误立即退出)
> 8. 底部加 trap,脚本异常中断时清理临时文件
>
> 先给我一个大纲,确认后再输出完整脚本。
OpenCode 会先给你一个大纲,如下:
重写大纲:
1. 变量区(顶部):APP_NAME、DEPLOY_PATH、LOG_FILE 等
2. 工具函数(工具函数):
- log():统一日志输出
- error_exit():错误处理 + 退出
- check_disk():磁盘空间检查
3. 主逻辑:
- init():环境预检
- build():构建程序
- deploy():部署发布
- rollback():回滚
4. 主入口:case 语句,按参数选择走哪个流程
(实测)这个过程就像有一个资深运维坐在你旁边帮你审代码、拆函数、补错误处理。我花了 3 小时看完全部 43 个脚本的重构结果,而 OpenCode 只用了大约 20 分钟生成所有重构方案。
AI 重构最危险的地方在于——它会改掉你的业务逻辑。如果你不测试直接上线,会出事。
# 运行旧脚本(记录旧脚本的完整输出)
bash deploy.sh --mock-mode > old_output.log 2>&1
echo $? > old_exit_code.txt
# 运行 OpenCode 重构后的新脚本
bash deploy_new.sh --mock-mode > new_output.log 2>&1
echo $? > new_exit_code.txt
# 对比差异
diff old_output.log new_output.log
diff old_exit_code.txt new_exit_code.txt
(实测)我的旧脚本和新脚本的输出差异有 34 处。其中 8 处是 OpenCode 改错的逻辑(路径差异、参数处理错误),其余 26 处是它修复了旧脚本的 bug(比如旧脚本没处理某个错误码,新脚本正确地返回了 1)。
不要直接上线。用 diff 逐条核对差异,测试完再上线。
# 测试脚本对各种异常输入的处理
# 案例 1:参数为空
bash deploy_new.sh
# 预期输出:usage: deploy.sh [build|deploy|rollback]
# 案例 2:路径不存在
bash deploy_new.sh build --path /nonexistent/path
# 预期输出:[ERROR] 路径不存在 /nonexistent/path
# 案例 3:权限不足
bash deploy_new.sh deploy --user nobody
# 预期输出:[ERROR] 权限不足,需要 root
# 案例 4:并发运行(两次同时跑)
bash deploy_new.sh deploy &
bash deploy_new.sh rollback &
# 预期输出:第二个应该阻止,返回冲突警告
(实测)边界测试是 OpenCode 不能帮你做的。因为它不知道你的业务环境长什么样。至少写 10 个边界测试用例。
重构完所有脚本后,制定一套标准化的 Shell 脚本模板,以后所有新脚本按这个来:
#!/bin/bash
#
# 脚本功能:标准模板
# 作者:your_name
# 修改时间:2026-07-26
#
set -euo pipefail
# ==================== 配置区 ====================
readonly APP_NAME="app_name"
readonly DEPLOY_PATH="/data/xxx"
readonly LOG_FILE="/var/log/${APP_NAME}.log"
readonly LOCK_FILE="/tmp/${APP_NAME}.lock"
# ==================== 错误码 ====================
readonly ERR_CONFIG=1
readonly ERR_DEPLOY=2
readonly ERR_ROLLBACK=3
# ==================== 工具函数 ====================
log() {
local level="$1"
shift
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [${level}] $*" | tee -a "${LOG_FILE}"
}
error_exit() {
log "ERROR" "$1"
exit "${2:-1}"
}
cleanup() {
log "INFO" "清理临时文件"
rm -f "${LOCK_FILE}"
}
trap cleanup EXIT
check_disk() {
local min_space_gb="$1"
local available_gb
available_gb=$(df -BG "${DEPLOY_PATH}" | tail -1 | awk '{print $4}' | sed 's/G//')
if [ "${available_gb}" -lt "${min_space_gb}" ]; then
error_exit "磁盘空间不足:可用 ${available_gb}G,需要 ${min_space_gb}G"
fi
}
# ==================== 业务函数 ====================
init() {
log "INFO" "初始化环境..."
if [ ! -d "${DEPLOY_PATH}" ]; then
error_exit "部署路径不存在:${DEPLOY_PATH}"
fi
local load
load=$(uptime | awk -F'load average:' '{print $2}' | cut -d',' -f1 | tr -d ' ')
if [ "$(echo "${load} > 0.8" | bc)" -eq 1 ]; then
error_exit "系统负载过高:${load}"
fi
log "INFO" "环境检查通过,负载:${load}"
}
build() {
log "INFO" "开始构建..."
check_disk 10
# TODO: 填入构建命令
# make build
log "INFO" "构建完成"
}
deploy() {
log "INFO" "开始部署..."
if [ -f "${LOCK_FILE}" ]; then
error_exit "部署锁存在:${LOCK_FILE},可能已有部署在运行"
fi
touch "${LOCK_FILE}"
log "INFO" "创建部署锁"
check_disk 5
local timestamp
timestamp=$(date '+%Y%m%d_%H%M%S')
local backup_path="${DEPLOY_PATH}/backup_${timestamp}"
cp -r "${DEPLOY_PATH}" "${backup_path}"
log "INFO" "备份完成:${backup_path}"
# TODO: 填入部署命令
log "INFO" "部署完成"
rm -f "${LOCK_FILE}"
}
rollback() {
log "INFO" "开始回滚..."
local latest_backup
latest_backup=$(ls -t "${DEPLOY_PATH}/backup_"* 2>/dev/null | head -1)
if [ -z "${latest_backup}" ]; then
error_exit "没有可用的备份"
fi
log "INFO" "恢复备份:${latest_backup}"
cp -r "${latest_backup}/." "${DEPLOY_PATH}"
log "INFO" "回滚完成"
}
# ==================== 主入口 ====================
usage() {
echo "用法:$0 {init|build|deploy|rollback}"
echo " init 初始化环境检查"
echo " build 构建程序"
echo " deploy 部署发布"
echo " rollback 回滚到上次部署"
exit "${ERR_CONFIG}"
}
main() {
[ $# -eq 0 ] && usage
case "$1" in
init) init ;;
build) build ;;
deploy) deploy ;;
rollback) rollback ;;
*) usage ;;
esac
}
main "$@"
(实测)这个模板不是 AI 生成的,是我从重构经验中固化下来的。全部脚本都用这套结构,以后新人接手也能直接看懂。
> 对当前目录下所有 .sh 文件(除了已完成的):
> 1. 全部按标准模板结构重写
> 2. 顶部加 set -euo pipefail(如果还没有)
> 3. 所有路径引用双引号包裹
> 4. 统一 set +e 替换为 trap + error_exit
> 5. 每个函数加注释说明
>
> 不改变业务逻辑。
> 重构后的脚本保存为 *_new.sh,旧脚本改为 *.sh.bak 备份。
(实测)这个对话完成后,OpenCode 会逐个扫描剩余的 sh 文件,挨个重写、挨个备份。我一次性处理了 30 多个 .sh 文件,只用了大约 1.5 小时就完成了所有重构和备份。而之前手动做这件事预计需要一周时间。
(实测)OpenCode 第一次重构,把所有 rm -rf /tmp/build_* 改成了 rm -rf /tmp/build_*(看起来一模一样但多了一个空格)。因为路径加了引号,空格变成了不同路径。核对差异时一定要关注路径和参数的变化。
# 旧脚本
if [ "$a" = "true" ]
# OpenCode 改成了
if [ ${a} = "true" ] # 没有引号!
# 如果 $a 为空或包含空格,这行就炸了
(实测)Shell 脚本里不加引号的变量引用是最常见的 bug。你需要在 prompt 里明确告诉 OpenCode 所有变量都要放在双引号里,否则它可能会把引号摘掉。
按我的经验,AI 重构后大约 15-20% 的脚本有逻辑错误。没上线前先跑三天测试,覆盖不同场景。
观点收尾:
一个中型规模的旧项目(43 个 .sh 文件),我用 OpenCode 重构完,耗时:
手动分析:3 小时(不可替代的时间,只有你知道业务逻辑)
AI 重写:2 小时(OpenCode 做了,你审了一遍)
测试验证:8 小时(必须亲自做,不能跳过)
部署上线:2 小时(灰度发布 + 确认)
总计:15 小时
如果没有 AI:预计需要 一周以上 才能做完同样的工作。
AI 重构不是替代你,而是让你从写代码变成审代码。运维的价值没有减少——你对业务逻辑的理解、测试场景的设计、上线的节奏把控,这些都是 AI 替代不了的。AI 的价值在于帮你在几小时内完成原本要几天的工作量。
如果你还没用上 OpenCode,先搭好环境:Ollama + OpenCode + VS Code 免费离线方案。关于 OpenCode 的知识库导入,可以看 OpenCode 本地知识库导入失败怎么解决。
本文基于博主用 OpenCode 重构公司自动化脚本的实操经历写成。Shell 脚本模板和边界测试用例部分参考了 Google Shell Style Guide 和博主团队内部分享。