OpenCode 一键重构旧项目脚本:运维老代码批量改写成标准 Linux Shell 实战

Published on in ai

我接手了一个 3000 行的老旧 Shell 脚本,差点辞职

事情是这样的。上个月换了一家公司,接手的第一天就被安排了一个任务——维护一个 3000 行的 deploy.sh。打开一看,200 多行没人知道是干什么的、变量名是 a b c、没有函数封装、一堆 cd /tmp && rm -rf 裸写。更离谱的是,整个公司的自动化脚本分布在 7 台服务器上,没有版本控制,没有任何注释。

这种场景我估计很多运维同行都遇到过。

我用 OpenCode 把整个公司的老旧脚本重构了一遍,从 3000 行缩减到 1200 行,全部标准化。这篇文章就是我踩完坑后整理出来的完整方案——用开源 AI 工具帮你清洗、重构、测试一键搞定。

这篇是 Ollama + OpenCode + VS Code 本地补全方案 的实战篇。如果你还没学会基础操作,先看那篇把环境搭起来。

第一步:用 OpenCode 扫描旧项目,自动识别哪些脚本需要重构

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 重写你的脚本

分析完成后,直接在 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 小时就完成了所有重构和备份。而之前手动做这件事预计需要一周时间。

避免 AI 重构的 3 个关键教训

教训 1:不要让它改变业务逻辑

(实测)OpenCode 第一次重构,把所有 rm -rf /tmp/build_* 改成了 rm -rf /tmp/build_*(看起来一模一样但多了一个空格)。因为路径加了引号,空格变成了不同路径。核对差异时一定要关注路径和参数的变化

教训 2:变量引用方式可能被改

# 旧脚本 if [ "$a" = "true" ]

# OpenCode 改成了
if [ ${a} = "true" ] # 没有引号!
# 如果 $a 为空或包含空格,这行就炸了

(实测)Shell 脚本里不加引号的变量引用是最常见的 bug。你需要在 prompt 里明确告诉 OpenCode 所有变量都要放在双引号里,否则它可能会把引号摘掉。

教训 3:不要省略测试

按我的经验,AI 重构后大约 15-20% 的脚本有逻辑错误。没上线前先跑三天测试,覆盖不同场景。

我的结论:AI 重构的真正价值

观点收尾

一个中型规模的旧项目(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 和博主团队内部分享。