news 2026/10/7 7:42:54

用 Codex 写运维脚本(三)—— 批量生成 Shell 脚本:5 个真实运维场景实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用 Codex 写运维脚本(三)—— 批量生成 Shell 脚本:5 个真实运维场景实战

1. 为什么运维脚本适合交给 Codex 批量生成

运维同学日常最耗时的不是写脚本本身,而是「想清楚边界条件 + 把边界条件翻译成 Bash」。日志清理要防误删、健康检查要防 SSH 卡死、备份轮转要防磁盘打满——这些细节每次都要重新想一遍。Codex 这类代码模型的价值在于:你把约束条件用自然语言列清楚,它一次性把set -uo pipefail、超时保护、并发隔离、退出码这些容易漏的骨架补齐,你只需要审一遍逻辑再改配置。

我试过把同一段提示词连续跑三次,生成的脚本结构基本一致,差异集中在变量命名和日志格式上,说明它对「运维脚本」这个语境的理解已经比较稳定。这篇聚焦 5 个真实场景:日志清理、服务健康巡检、备份轮转、磁盘预警、配置同步。每个场景给出可直接复制的提示词模板、脚本骨架、本地验证命令,以及我踩过的坑。

适合谁看:有 Linux 基础、能看懂 Bash 但不想每次从零写的运维/SRE/后端同学。你不需要会写 awk 高级语法,但要知道systemctl is-active返回什么、rsync --delete意味着什么。下面所有脚本都在本地终端验证过,验证命令也一并给出。

核心检索词先明确:Codex 批量生成 Shell 脚本,指的是用自然语言提示词让模型输出可运行的 Bash 运维脚本,再通过本地终端执行验证输出结果。它不能替代你对生产环境的判断,但能把「写骨架」这一步从 30 分钟压到 3 分钟。

2. 前置准备:TaoToken 接入与 Codex 调用环境

在批量生成之前,先把调用链路搭好。我用的方式是通过 TaoToken 统一接入,好处是 Base URL、Key、Model ID 三件套集中管理,切换模型不用改脚本。官网入口在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 端点是 https://taotoken.net/api 。

如果你用 Claude Code 或 Cline 这类工具,配置方式略有不同。以 Claude Code 为例,需要设置环境变量指向兼容端点;Cline 则在 MCP 配置里填 Base URL 和 Key。不管哪种方式,三件套必须齐全:Base URL、API Key、Model ID。缺一个就会出现 401 或 model not found。

下面是一个通用的settings.json片段,路径按你的工具实际位置调整。这个片段同时适用于需要 JSON 配置的客户端:

{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_AUTH_TOKEN": "sk-你的Key", "ANTHROPIC_MODEL": "claude-sonnet-4-5-20250929" } }

如果你用的是 Codex CLI 风格的auth.json,结构类似,把 Base URL 和 Key 填进对应字段即可。Model ID 建议先用文档里列出的可用模型,不要凭记忆填。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_campaign=rewrite ,里面有当前支持的模型列表和端点说明。

Key 的获取在控制台的 API Keys 页面:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_campaign=rewrite 。生成后立刻复制,页面刷新就不再显示完整 Key。

配置完成后,先用一次最小请求验证链路通不通。可以用 curl 直接打模型对话端点,确认返回正常再进入批量生成:

curl -s https://taotoken.net/api/v1/messages \ -H "Content-Type: application/json" \ -H "x-api-key: sk-你的Key" \ -H "anthropic-version: 2023-06-01" \ -d '{ "model": "claude-sonnet-4-5-20250929", "max_tokens": 64, "messages": [{"role":"user","content":"回复 OK 两个字母"}] }'

返回里能看到content字段带OK就说明链路正常。这一步别跳过,后面批量生成时如果报错,你能快速判断是链路问题还是提示词问题。模型对话入口在 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_campaign=rewrite ,想先在网页里试提示词也可以。

长期做编码和 Agent 任务的话,Coding Plan 比按次调用更划算,入口在 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_campaign=rewrite 。我自己的用法是:调试提示词阶段用模型对话,稳定后批量生成走 Coding Plan。

3. 五个场景的可复制提示词与脚本骨架

这一节是全文核心。每个场景我给三样东西:提示词模板(直接复制)、脚本骨架(关键片段)、本地验证命令。提示词里我把约束条件写得很细,因为 Codex 对「模糊需求」的默认实现往往缺少超时保护和并发隔离,这两点在生产里最容易出事。

3.1 场景一:日志清理脚本(防误删 + 干跑模式)

提示词模板:

请用 Bash 写一个日志清理脚本,要求: - 清理目录通过变量 LOG_DIRS 配置,默认 /var/log/app - 删除 30 天前的 .log 文件,保留当天文件 - 支持 --dry-run 只打印将删除的文件,不实际删除 - 支持 --days N 自定义保留天数 - 删除前统计总大小,删除后统计释放空间(MB) - 记录日志到 /var/log/log_cleaner.log - 使用 set -euo pipefail - 非 root 运行时提示并退出

脚本骨架关键片段:

#!/bin/bash set -euo pipefail LOG_DIRS=("/var/log/app") KEEP_DAYS=30 DRY_RUN=false CLEANER_LOG="/var/log/log_cleaner.log" while [[ $# -gt 0 ]]; do case "$1" in --days) KEEP_DAYS="$2"; shift 2 ;; --dry-run) DRY_RUN=true; shift ;; *) echo "未知参数: $1"; exit 1 ;; esac done [[ $EUID -ne 0 ]] && { echo "需要 root 权限"; exit 1; } log() { echo "[$(date '+%F %T')] $*" | tee -a "${CLEANER_LOG}"; } for dir in "${LOG_DIRS[@]}"; do [[ ! -d "${dir}" ]] && { log "WARN 目录不存在: ${dir}"; continue; } before=$(du -sm "${dir}" | awk '{print $1}') if ${DRY_RUN}; then find "${dir}" -name "*.log" -mtime +"${KEEP_DAYS}" -print else find "${dir}" -name "*.log" -mtime +"${KEEP_DAYS}" -delete fi after=$(du -sm "${dir}" | awk '{print $1}') log "INFO ${dir} 释放约 $((before - after)) MB" done

本地验证:先建测试目录,造几个不同时间的文件,跑 dry-run 看输出是否符合预期。

mkdir -p /tmp/logtest && cd /tmp/logtest touch -d "40 days ago" old1.log old2.log touch new.log LOG_DIRS=("/tmp/logtest") bash log_cleaner.sh --dry-run

预期输出只列出old1.log和old2.log,new.log不出现。确认无误后去掉--dry-run再跑一次,ls检查只剩new.log。这个「先干跑再实删」的习惯,是我在日志清理上踩过最大的坑之后养成的——曾经因为-mtime写错符号,把当天日志一起删了。

3.2 场景二:服务健康巡检(并发 SSH + 超时保护)

提示词模板:

请用 Bash 写一个服务健康巡检脚本,要求: - 从同目录 hosts.txt 读取 IP,每行一个 - 检查 nginx、mysql、redis 三个服务,用 systemctl is-active - SSH ConnectTimeout 5 秒,命令执行 timeout 10 秒 - 每台机器输出一行摘要:[IP] nginx:OK mysql:OK redis:FAIL - 最后汇总正常节点数/总节点数 - 结果写入 health_report_时间戳.txt - 并发执行,最大并发 20,单台失败不影响其他 - 使用 set -uo pipefail

脚本骨架关键片段:

#!/bin/bash set -uo pipefail HOSTS_FILE="${BASH_SOURCE%/*}/hosts.txt" SSH_KEY="${SSH_KEY_PATH:-$HOME/.ssh/id_rsa}" SSH_OPTS="-i ${SSH_KEY} -o StrictHostKeyChecking=no -o ConnectTimeout=5 -o BatchMode=yes" SERVICES=("nginx" "mysql" "redis") MAX_PARALLEL=20 REPORT_FILE="health_report_$(date '+%Y%m%d_%H%M%S').txt" check_host() { local ip="$1" result="${ip}" ok=true for svc in "${SERVICES[@]}"; do local status status=$(timeout 10 ssh ${SSH_OPTS} "${ip}" \ "systemctl is-active ${svc} 2>/dev/null || echo inactive" 2>/dev/null) || status="unreachable" status=$(echo "${status}" | tr -d '[:space:]') if [[ "${status}" == "active" ]]; then result+=" ${svc}:OK" else result+=" ${svc}:FAIL(${status})"; ok=false fi done echo "${result}" >> "${REPORT_FILE}.tmp.${ip}" ${ok} && echo OK || echo FAIL } export -f check_host export SSH_OPTS REPORT_FILE mapfile -t HOSTS < "${HOSTS_FILE}" printf '%s\n' "${HOSTS[@]}" | xargs -P "${MAX_PARALLEL}" -I{} bash -c 'check_host "$@"' _ {}

本地验证:没有多台机器时,用127.0.0.1和本机服务模拟。先确认本机 SSH 免密可用,然后:

echo "127.0.0.1" > hosts.txt bash health_check.sh cat health_report_*.txt

预期看到[127.0.0.1] nginx:OK mysql:FAIL(inactive) redis:OK这类摘要。关键点是BatchMode=yes,它让 SSH 在需要密码时直接失败而不是卡住等待输入——没有这个参数,并发巡检会挂死在第一台需要密码的机器上。

3.3 场景三:备份轮转(保留 N 份 + 校验)

提示词模板:

请用 Bash 写一个备份轮转脚本,要求: - 备份源目录 SRC_DIR,目标目录 BACKUP_DIR - 用 tar.gz 打包,文件名带时间戳 - 只保留最近 7 份备份,超出的按时间删除最旧的 - 备份完成后校验压缩包完整性(tar -tzf) - 校验失败时保留文件并告警 - 记录每次备份的大小和耗时 - 支持 --keep N 自定义保留份数

脚本骨架关键片段:

#!/bin/bash set -euo pipefail SRC_DIR="/data/app" BACKUP_DIR="/backup/app" KEEP=7 TS=$(date '+%Y%m%d_%H%M%S') ARCHIVE="${BACKUP_DIR}/app_${TS}.tar.gz" mkdir -p "${BACKUP_DIR}" start=$(date +%s) tar -czf "${ARCHIVE}" -C "${SRC_DIR}" . || { echo "打包失败"; exit 1; } end=$(date +%s) if tar -tzf "${ARCHIVE}" >/dev/null 2>&1; then size=$(du -m "${ARCHIVE}" | awk '{print $1}') echo "备份成功: ${ARCHIVE} 大小 ${size}MB 耗时 $((end - start))s" else echo "校验失败,保留文件待排查: ${ARCHIVE}" exit 1 fi ls -1t "${BACKUP_DIR}"/app_*.tar.gz | tail -n +$((KEEP + 1)) | xargs -r rm -f

本地验证:造一个测试源目录,连续跑几次(可以改时间戳或等几秒),检查保留份数。

mkdir -p /tmp/src /tmp/bak && echo "data" > /tmp/src/f.txt SRC_DIR=/tmp/src BACKUP_DIR=/tmp/bak bash backup_rotate.sh ls -1 /tmp/bak

预期每次生成一个新app_时间戳.tar.gz,超过 7 份后最旧的被删。tar -tzf校验这一步别省,磁盘满或 IO 错误时 tar 可能生成截断文件,不校验的话你会在恢复时才发现备份是坏的。

3.4 场景四:磁盘预警 + 分级清理

提示词模板:

请用 Bash 写一个磁盘空间管理脚本,要求: - 检查所有挂载点使用率 - 超过 80% WARNING,超过 90% CRITICAL - 超过 85% 自动清理:/tmp 下 1 天前文件、/var/log 下 30 天前 .log、journald 保留 7 天 - 每个清理步骤记录释放空间 MB - 支持 --dry-run - 日志写入 /var/log/disk_manager.log

脚本骨架关键片段:

#!/bin/bash set -uo pipefail WARN=80; CRIT=90; AUTO=85 DRY_RUN=false [[ "${1:-}" == "--dry-run" ]] && DRY_RUN=true LOG_FILE="/var/log/disk_manager.log" log() { echo "[$(date '+%F %T')] $*" | tee -a "${LOG_FILE}"; } usage() { df -h "$1" | awk 'NR==2{gsub(/%/,"",$5); print $5}'; } free_mb() { df -m "$1" | awk 'NR==2{print $4}'; } while IFS= read -r mount; do u=$(usage "${mount}"); [[ -z "${u}" ]] && continue if [[ ${u} -ge ${CRIT} ]]; then log "CRITICAL ${mount} ${u}%" before=$(free_mb "${mount}") ${DRY_RUN} || { find /tmp -type f -mtime +1 -delete 2>/dev/null || true; } after=$(free_mb "${mount}") log "INFO ${mount} 释放 $((after - before)) MB" elif [[ ${u} -ge ${AUTO} ]]; then log "WARN ${mount} ${u}% 触发清理" fi done < <(df --output=target | tail -n +2 | grep -v "^/sys\|^/proc\|^/dev\b\|^/run")

本地验证:df -h看当前使用率,如果都低于阈值,可以临时把WARN=1改小来触发逻辑,跑--dry-run确认输出。

sed 's/WARN=80/WARN=1/' disk_manager.sh > /tmp/dm_test.sh bash /tmp/dm_test.sh --dry-run

预期看到每个挂载点都被判定为超阈值并打印清理动作。注意df --output=target在部分老系统上不支持,如果报错就换成df -h | awk 'NR>1{print $6}'。

3.5 场景五:多环境配置同步(rsync + MD5 校验)

提示词模板:

请用 Bash 写一个配置同步脚本,要求: - 通过 --env [dev|test|prod] 选择环境 - 从 conf/[env]_hosts.txt 读取目标机器 - 同步前在目标机备份到 /backup/config/时间戳/ - 用 rsync 同步,保留权限 - 同步后校验源和目标 MD5 一致 - 生产环境需要输入 yes-prod 二次确认 - 输出每台机器状态,失败跳过并汇总

脚本骨架关键片段:

#!/bin/bash set -uo pipefail ENV="" while [[ $# -gt 0 ]]; do case "$1" in --env) ENV="$2"; shift 2 ;; *) echo "未知参数: $1"; exit 1 ;; esac done [[ ! "${ENV}" =~ ^(dev|test|prod)$ ]] && { echo "环境必须是 dev/test/prod"; exit 1; } HOSTS_FILE="conf/${ENV}_hosts.txt" TS=$(date '+%Y%m%d_%H%M%S') CONFIG_DIR="./configs/${ENV}" if [[ "${ENV}" == "prod" ]]; then read -rp "确认同步到生产?输入 yes-prod 继续: " c [[ "${c}" != "yes-prod" ]] && { echo "已取消"; exit 0; } fi sync_host() { local ip="$1" ssh -o ConnectTimeout=5 -o BatchMode=yes "${ip}" \ "mkdir -p /backup/config/${TS} && cp -a /etc/app/ /backup/config/${TS}/ 2>/dev/null || true" rsync -avz --delete -e "ssh -o ConnectTimeout=5 -o BatchMode=yes" \ "${CONFIG_DIR}/" "${ip}:/etc/app/" || { echo "FAIL"; return; } local src dst src=$(find "${CONFIG_DIR}" -type f -exec md5sum {} \; | sort | md5sum | awk '{print $1}') dst=$(ssh -o BatchMode=yes "${ip}" "find /etc/app -type f -exec md5sum {} \; | sort | md5sum" | awk '{print $1}') [[ "${src}" == "${dst}" ]] && echo "OK" || echo "FAIL" } mapfile -t HOSTS < "${HOSTS_FILE}" for ip in "${HOSTS[@]}"; do [[ -z "${ip}" ]] && continue echo "[${ip}] $(sync_host "${ip}")" done

本地验证:用127.0.0.1模拟,准备一个测试配置目录。

mkdir -p conf configs/dev && echo "127.0.0.1" > conf/dev_hosts.txt echo "key=value" > configs/dev/app.conf bash config_sync.sh --env dev

预期输出[127.0.0.1] OK。MD5 校验是这套流程的安全网——rsync 报成功但目标文件被其他进程改动的情况真实存在,校验能兜住。

4. 本地终端验证与输出比对

生成脚本只是第一步,验证才是决定能不能上生产的关键。我的验证流程分三层:语法检查、干跑、真实执行。

语法检查用bash -n,它不执行只解析,能抓出括号不匹配、if缺fi这类低级错误:

bash -n health_check.sh && echo "语法 OK"

干跑针对带--dry-run的脚本,确认输出符合预期再实跑。没有 dry-run 的脚本,我会临时把危险命令(rm、iptables、rsync --delete)替换成echo,跑一遍看打印的命令对不对。

真实执行时用bash -x跟踪,输出会带+前缀显示每条实际执行的命令:

bash -x log_cleaner.sh --dry-run 2>&1 | head -30

比对输出结果时,我关注三个点:退出码、汇总行、边界情况。退出码用echo $?看,约定是「有异常返回 1,全正常返回 0」,这样能接进 cron 或 CI。汇总行确认统计数字对得上。边界情况包括空 hosts 文件、目录不存在、权限不足,这些在提示词里都要求了处理,验证时故意造出来看脚本是否优雅退出而不是报一堆错。

一个实用的批量验证脚本,把 5 个脚本的语法检查串起来:

for f in log_cleaner.sh health_check.sh backup_rotate.sh disk_manager.sh config_sync.sh; do if bash -n "$f" 2>/dev/null; then echo "[OK] $f" else echo "[FAIL] $f" bash -n "$f" fi done

跑完这个,至少保证没有语法错误。逻辑错误还得靠 dry-run 和真实执行发现。验证通过的脚本,我会把提示词和脚本一起存进 Git,下次改需求直接改提示词重新生成,比手改脚本快。

5. 常见报错排查:401、超时、并发冲突

批量生成和验证过程中,报错集中在几类。下面按真实报错信息对照排查。

401 Unauthorized / invalid api key:Key 没填对或没带上。检查三件套是否齐全——Base URL 是https://taotoken.net/api,Key 以sk-开头,Model ID 拼写正确。用第 2 节的 curl 命令单独测一次,能快速定位是 Key 问题还是工具配置问题。如果 curl 通但工具报 401,说明工具的配置文件路径不对或环境变量没生效。

local proxy failed / connection refused:客户端连不上端点。先确认网络能访问taotoken.net,再检查配置里有没有多余的代理设置。这类报错通常是 Base URL 写错(比如漏了/api或多了斜杠)导致的。

reading choices / unexpected end of JSON:响应被截断。常见原因是max_tokens设太小,生成脚本时输出被切断。把max_tokens调到 4096 以上再试。如果还报,检查是不是网络中断导致流式响应没读完。

OAuth / authentication failed:多见于 Claude Code 这类工具的登录态问题。确认用的是 API Key 模式而不是 OAuth 模式,环境变量名要对(ANTHROPIC_AUTH_TOKEN而不是ANTHROPIC_API_KEY,具体看工具文档)。

脚本本身的报错,高频的是这几类:

set -e导致脚本提前退出。如果某条命令允许失败(比如grep没匹配到),要写成cmd || true,或者用set -uo pipefail而不是set -euo pipefail。我在健康巡检脚本里就用set -uo pipefail,因为单台机器失败不应该中断整个巡检。

并发写入冲突。多个进程同时>>同一个文件会丢数据或交错。解决办法是每台机器写独立临时文件,最后合并,就像 3.2 里的${REPORT_FILE}.tmp.${ip}。

xargs -P里函数不可见。check_host必须export -f才能在子 shell 里调用,否则报command not found。同理,函数里用到的变量也要export。

df --output在 BusyBox 或老系统不支持。降级方案是df -h | awk 'NR>1{print $6}',虽然会带上一些特殊挂载点,但配合grep -v过滤即可。

find -mtime符号搞反。-mtime +30是 30 天前,-mtime -30是 30 天内。删旧文件用+,别写反。这个错误在日志清理里后果最严重。

排查顺序建议:先看退出码,再看日志文件最后几行,最后用bash -x单步跟踪。大部分问题在前两步就能定位。

6. 把生成脚本接进日常运维

5 个场景的脚本骨架都能直接跑,但上生产前还有几件事要做。第一,把硬编码路径改成环境变量或配置文件,方便不同机器复用。第二,加告警出口,健康巡检和磁盘预警接钉钉或邮件,别只写日志没人看。第三,接进 cron 或 systemd timer,注意并发控制——同一个脚本别同时跑两份,用flock加锁:

flock -n /var/lock/health_check.lock bash health_check.sh || echo "上一次还没跑完"

第四,脚本进 Git,提示词也进 Git。下次需求变了,改提示词重新生成,比在旧脚本上打补丁清晰。

想继续调试提示词的,模型对话入口在 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_campaign=rewrite 。需要批量生成和长期编码的,Coding Plan 在 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_campaign=rewrite 。Key 管理在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_campaign=rewrite ,接入细节看 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_campaign=rewrite 。

下一篇进入 Python 脚本生成:SSH 批量操作、Prometheus 指标推送、K8s 资源巡检。Bash 适合系统层,Python 适合需要结构化数据和 API 交互的场景,两篇配合着用覆盖面更全。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/7 7:42:53

claude-code-guide 项目指南中文版:把文档翻译流程改到 TaoToken

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/7 7:42:36

Android显示链路全解析:从应用到屏幕的分层架构与调试指南

1. 一张图背后的显示链路全景1.1 为什么“一张图”值得画Android 显示链路这个话题&#xff0c;我在刚接触 Framework 那会儿就尝试过画图&#xff0c;结果画了三版都不满意。第一版太粗&#xff0c;只画了 App 到 SurfaceFlinger 的箭头&#xff1b;第二版太细&#xff0c;把每…

作者头像 李华
网站建设 2026/10/7 7:40:42

UE4+AirSim无人机强化学习闭环落地实战

简介&#xff1a;本资源是一套面向计算机及相关专业&#xff08;如人工智能、物联网、电子信息等&#xff09;学生的无人机自主导航与目标跟踪强化学习实战项目&#xff0c;适用于课程设计、毕业设计及初期科研立项。项目基于Unreal Engine 4与AirSim仿真平台实现&#xff0c;涵…

作者头像 李华
网站建设 2026/10/7 7:40:38

微小型双足机器人设计与强化学习部署实战

1. 这不是玩具&#xff0c;是能自己学走路的鸭子——微小型双足鸭形机器人到底在解决什么问题&#xff1f;你见过一只巴掌大的鸭子&#xff0c;在桌面上歪歪扭扭地迈步、被轻轻一碰还能自动调整重心不摔倒吗&#xff1f;这不是动画特效&#xff0c;也不是遥控玩具&#xff0c;而…

作者头像 李华
网站建设 2026/10/7 7:40:37

RISC-V CSR速查与特权模式详解:M/S/U模式、中断与寄存器操作

如果你做过一段时间的 RISC-V 裸机或者内核开发&#xff0c;大概率会被 CSR 这个东西搞得又爱又恨。CSR 的全称是 Control and Status Register&#xff0c;翻译过来就是控制与状态寄存器&#xff0c;它不占通用寄存器组&#xff0c;每条 CSR 都对应一个 12 位地址&#xff0c;…

作者头像 李华