首页 › tutorials › Headless会话管理:非交互到断点续传

Claude Code Headless会话管理:非交互到断点续传实战

概述

Claude Code的Headless模式(-p标志)远不止"无界面运行"这么简单——它是一套完整的会话生命周期管理系统。本教程深入讲解Headless模式的核心机制、会话续传(--continue/--resume)、结构化输出控制(--output-format)、权限自动化配置,以及生产级错误恢复策略。学完你将掌握用Shell脚本构建可靠、可恢复、可审计的AI自动化工作流。

适用人群:已有Claude Code基础使用经验的开发者、DevOps工程师、需要将AI能力嵌入CI/CD或定时任务的技术负责人。


前置条件

项目 要求
Claude Code v1.0.18+(支持--output-format json)
Node.js v18+
操作系统 macOS / Linux(Windows需WSL2)
API Key Anthropic API Key已配置
基础知识 熟悉Shell脚本、管道操作、JSON处理

安装验证:

claude --version
# 确保 >= 1.0.18

一、Headless模式核心机制

1.1 -p标志:非交互的基石

-p(--print)标志是所有Headless自动化的入口。它让Claude Code跳过交互式TUI界面,直接处理提示并输出结果到stdout:

# 最简形式:单次执行,输出到stdout
claude -p "列出src/目录下所有未使用TypeScript类型的文件"

# 管道输入:将内容通过stdin传入
git diff HEAD~5 | claude -p "为这些变更生成CHANGELOG条目"

# 组合使用:文件内容作为上下文
cat error.log | claude -p "分析错误日志,给出修复建议"

核心行为规则: - 执行完毕自动退出,退出码0表示成功 - 工具调用(文件读写、Bash执行)照常运行 - 无需人类交互——默认使用settings.json中的权限配置

1.2 --bare:极速启动模式

在自动化场景中,启动速度至关重要。--bare标志跳过所有启动发现过程:

# 普通Headless:需要加载hooks、skills、MCP发现
claude -p "检查代码风格"  # 启动约2-5秒

# Bare模式:跳过hooks、skills自动发现、MCP初始化
claude -p --bare "检查代码风格"  # 启动约0.5-1秒

--bare跳过的内容: - Hooks自动发现和注册 - Skills目录扫描 - MCP服务器连接初始化 - 部分环境检测

何时用--bare:纯代码分析、日志解读、文本生成等不依赖MCP或自定义工具的轻量任务。

何时不用--bare:需要调用特定MCP工具、依赖Hooks触发器、或使用自定义Skills的任务。

1.3 输出格式控制

Headless模式最强大的特性之一是结构化输出:

# 纯文本输出(默认)
claude -p "总结这个项目的架构" > summary.txt

# JSON输出:包含完整元数据
claude -p "分析package.json的依赖" \
  --output-format json \
  > analysis.json

# JSON输出 + Schema约束
claude -p "列出所有TODO注释" \
  --output-format json \
  --json-schema '{
    "type": "object",
    "properties": {
      "todos": {
        "type": "array",
        "items": {
          "type": "object",
          "properties": {
            "file": {"type": "string"},
            "line": {"type": "integer"},
            "text": {"type": "string"},
            "priority": {"enum": ["high", "medium", "low"]}
          },
          "required": ["file", "line", "text"]
        }
      }
    }
  }'

JSON输出结构示例:

{
  "result": "找到12个TODO注释...",
  "cost_usd": 0.0034,
  "duration_ms": 4521,
  "num_turns": 2,
  "session_id": "abc123def456",
  "is_error": false
}

关键字段解读: - session_id:后续用--resume恢复此会话的关键标识 - cost_usd:本次执行的精确Token费用 - num_turns:Agent循环的迭代次数,用于成本分析 - is_error:是否因错误退出

1.4 流式输出(Streaming)

对于长时间运行的任务,流式输出提供实时反馈:

# 流式JSON输出
claude -p "重构整个auth模块" \
  --output-format stream-json

# 每行输出一个JSON对象
# {"type":"assistant","content":"正在分析..."}
# {"type":"tool_use","tool":"Read","input":{"file_path":"src/auth.ts"}}
# {"type":"assistant","content":"发现3个问题..."}

流式输出的JSON Lines格式可用于构建进度监控:

# 实时显示进度,同时保存完整日志
claude -p "运行测试并修复失败的用例" \
  --output-format stream-json \
  2>/dev/null | while IFS= read -r line; do
    type=$(echo "$line" | jq -r '.type // empty')
    case "$type" in
      "assistant") echo "🤖 $(echo "$line" | jq -r '.content[:80]')" ;;
      "tool_use") echo "🔧 调用工具: $(echo "$line" | jq -r '.tool')" ;;
      "tool_result") echo "✅ 工具结果: $(echo "$line" | jq -r '.content[:80]')" ;;
    esac
  done

二、会话管理:断点续传

2.1 会话生命周期

Claude Code的每个Headless执行都创建一个会话,会话具有完整的生命周期:

stateDiagram-v2
    [*] --> 创建: claude -p
    创建 --> 活跃: Agent循环执行
    活跃 --> 工具调用: 读取/编辑/执行
    工具调用 --> 活跃: 返回结果
    活跃 --> 挂起: 达到max-turns限制
    活跃 --> 完成: 任务结束
    挂起 --> 恢复: --resume session_id
    完成 --> 续传: --continue
    恢复 --> 活跃: 继续执行
    续传 --> 活跃: 基于上次会话
    完成 --> [*]

2.2 --continue:延续上一次会话

--continue(简写-c)加载最近一次会话的完整上下文:

# 第一步:初始分析
claude -p "分析src/api/目录的代码质量问题" \
  --output-format json > step1.json

# 第二步:基于上一次会话继续,要求修复
claude -p --continue "根据上面的分析,修复所有高优先级问题" \
  --output-format json > step2.json

# 第三步:验证修复
claude -p --continue "运行测试验证所有修复" \
  --output-format json > step3.json

关键特性: - --continue只能恢复最近一次会话 - 会话上下文包含之前的所有对话和工具调用结果 - 即使中间发生了其他交互式会话,-c仍然指向最后一次Headless会话

2.3 --resume:精确恢复指定会话

--resume(简写-r)通过session_id恢复任意历史会话:

# 从JSON输出中提取session_id
SESSION_ID=$(claude -p "分析代码覆盖率" \
  --output-format json | jq -r '.session_id')

echo "会话ID: $SESSION_ID"

# ... 几小时后或几天后 ...

# 恢复该会话,继续工作
claude -p --resume "$SESSION_ID" \
  "基于之前的覆盖率分析,为未覆盖的函数生成测试用例"

实际场景——长时间任务的分段执行:

#!/bin/bash
# 文件: incremental-refactor.sh
# 用途:大型重构的分段执行,每段限制20轮

TASK_DESCRIPTION="将所有class组件迁移为函数组件"
SESSION_FILE=".claude-refactor-session"

# 第一次执行或恢复
if [ -f "$SESSION_FILE" ]; then
  SESSION_ID=$(cat "$SESSION_FILE")
  echo "恢复会话: $SESSION_ID"
  RESULT=$(claude -p --resume "$SESSION_ID" \
    "继续迁移工作。上一次你迁移了哪些文件?继续剩余的。" \
    --output-format json \
    --max-turns 20)
else
  echo "创建新会话"
  RESULT=$(claude -p "$TASK_DESCRIPTION" \
    --output-format json \
    --max-turns 20)
fi

# 提取并保存session_id
NEW_SESSION=$(echo "$RESULT" | jq -r '.session_id')
echo "$NEW_SESSION" > "$SESSION_FILE"

# 检查是否完成
IS_ERROR=$(echo "$RESULT" | jq -r '.is_error')
NUM_TURNS=$(echo "$RESULT" | jq -r '.num_turns')

echo "本轮执行: ${NUM_TURNS}轮, 费用: \$(echo "$RESULT" | jq -r '.cost_usd')"

if [ "$IS_ERROR" = "true" ]; then
  echo "执行出错,请检查日志"
  exit 1
fi

if [ "$NUM_TURNS" -lt 20 ]; then
  echo "任务已完成!清理会话文件。"
  rm "$SESSION_FILE"
else
  echo "达到轮次上限,运行此脚本继续下一阶段。"
fi

2.4 会话上下文的边界

理解会话上下文包含什么,不包含什么:

上下文内容 是否保留 说明
对话历史 是 完整的提示和回复链
工具调用记录 是 读取的文件内容、执行的命令输出
文件系统变更 否 恢复的是对话记忆,不是文件快照
环境变量变更 否 每次执行都是新的Shell环境
MCP连接状态 否 重新建立MCP连接
运行中进程 否 后台进程不会被恢复

关键洞察:会话恢复不是"时光机"——它恢复的是对话上下文,不是系统状态。如果之前的会话修改了文件,那些修改已经在磁盘上了;如果之前的会话启动了服务器,你需要重新启动它。


三、权限自动化

3.1 Headless模式的权限挑战

交互模式下,Claude Code遇到需要权限的操作会弹出确认。Headless模式下没有人类来点击"允许"——你需要预先配置权限。

3.2 --allowedTools:工具白名单

精确控制Headless执行可以使用哪些工具:

# 只读分析:只允许读取和搜索
claude -p "审计src/目录的安全漏洞" \
  --allowedTools "Read,Glob,Grep"

# 允许读取和编辑,但不允许执行Shell命令
claude -p "修复所有lint错误" \
  --allowedTools "Read,Edit,Glob,Grep"

# 完全自动化:允许所有工具
claude -p "实现用户认证模块" \
  --allowedTools "Bash,Read,Edit,Write,Glob,Grep"

工具名称完整列表: - Read — 读取文件 - Edit — 编辑现有文件 - Write — 创建新文件 - Bash — 执行Shell命令 - Glob — 文件搜索 - Grep — 内容搜索 - Task — 启动子Agent

3.3 settings.json权限配置

对于频繁执行的自动化任务,在项目级.claude/settings.json中预配置权限:

{
  "permissions": {
    "allow": [
      "Read",
      "Glob",
      "Grep",
      "Edit(src/**)",
      "Bash(npm test*)",
      "Bash(npm run lint*)",
      "Bash(git diff*)",
      "Bash(git log*)"
    ],
    "deny": [
      "Bash(rm -rf*)",
      "Bash(git push*)",
      "Bash(git reset*)",
      "Write(.env*)",
      "Write(*credentials*)"
    ]
  }
}

模式匹配规则: - Edit(src/**) — 允许编辑src/目录下的所有文件 - Bash(npm test*) — 允许所有以npm test开头的命令 - Write(.env*) — 拒绝写入任何以.env开头的文件

3.4 Permission Modes(权限模式)

Claude Code提供三种全局权限模式:

# Plan模式:只读取不修改,适合分析
claude -p --permission-mode plan "分析代码架构并给出重构建议"

# Auto-accept模式:自动批准所有操作(危险!)
claude -p --permission-mode auto-accept "重构整个项目"

# 默认模式:根据settings.json配置
claude -p "常规开发任务"

生产环境推荐:

# 安全的CI审计模式
claude -p "检查PR的代码质量" \
  --permission-mode plan \
  --output-format json \
  --max-turns 5

# 受限的修复模式
claude -p "修复测试失败" \
  --allowedTools "Read,Edit,Bash(npm*,npx*)" \
  --max-turns 10 \
  --output-format json

四、安全边界与成本控制

4.1 三重安全阀门

每个Headless执行都应该设置三道安全边界:

claude -p "你的任务描述" \
  --max-turns 15 \          # 阀门1:最大Agent循环轮次
  --max-tokens 50000 \      # 阀门2:最大Token消耗
  --timeout 300000          # 阀门3:最大执行时间(ms)
graph TD
    A[Headless执行启动] --> B{max-turns检查}
    B -->|未达上限| C{max-tokens检查}
    B -->|已达上限| Z[停止: 轮次耗尽]
    C -->|未达上限| D{timeout检查}
    C -->|已达上限| Z2[停止: Token耗尽]
    D -->|未超时| E[执行Agent循环]
    D -->|已超时| Z3[停止: 时间到]
    E --> F{任务完成?}
    F -->|是| G[输出结果 + 退出码0]
    F -->|否| B

4.2 --max-turns:轮次上限

这是最重要的安全参数。每次Agent循环("思考→调用工具→获取结果→再思考")算一轮:

# 轻量任务:1-3轮足够
claude -p "这个函数做了什么?" --max-turns 3

# 中等任务:5-15轮
claude -p "修复所有TypeScript类型错误" --max-turns 15

# 重量任务:15-30轮
claude -p "重构认证模块为OAuth2.0" --max-turns 30

# 禁止:不加限制的执行
# claude -p "重构整个项目"  # 危险!可能无限循环

轮次规划公式:

预估轮次 = 文件数量 × 每文件操作数 × 安全系数(1.5)

示例:修复20个文件的lint错误
预估轮次 = 20 × 2(读取+编辑) × 1.5 = 60
设置 --max-turns 60

4.3 成本监控与预算控制

#!/bin/bash
# 文件: budget-guard.sh
# 用途:带预算控制的Headless执行

MAX_COST=0.50  # 单次执行最大费用(美元)

RESULT=$(claude -p "$1" \
  --output-format json \
  --max-turns 20)

# 提取费用
ACTUAL_COST=$(echo "$RESULT" | jq -r '.cost_usd')
SESSION_ID=$(echo "$RESULT" | jq -r '.session_id')
TURNS=$(echo "$RESULT" | jq -r '.num_turns')

echo "=== 执行报告 ==="
echo "会话ID: $SESSION_ID"
echo "轮次: $TURNS"
echo "费用: \$$ACTUAL_COST"

# 预算检查
if (( $(echo "$ACTUAL_COST > $MAX_COST" | bc -l) )); then
  echo "⚠️ 费用超预算!上限: \$$MAX_COST, 实际: \$$ACTUAL_COST"
  exit 2
fi

# 提取实际回答内容
echo "$RESULT" | jq -r '.result'

4.4 退出码语义

Headless执行的退出码承载重要信息:

退出码 含义 处理策略
0 成功完成 继续下一步
1 一般性错误 检查提示和权限
2 被安全限制终止 增加--max-turns或--timeout
# 根据退出码做条件处理
claude -p "运行测试" --max-turns 10 --output-format json > result.json
EXIT_CODE=$?

case $EXIT_CODE in
  0) echo "成功"; cat result.json | jq -r '.result' ;;
  1) echo "执行出错"; cat result.json | jq -r '.result' ;;
  2) echo "被限制终止,考虑增加限制或分段执行" ;;
esac

五、实战案例:生产级Headless任务运行器

5.1 架构设计

我们将构建一个完整的Headless任务运行器,具备以下能力:

graph LR
    A[任务队列 JSON] --> B[任务调度器]
    B --> C{会话存在?}
    C -->|是| D[resume恢复]
    C -->|否| E[新建会话]
    D --> F[Claude -p 执行]
    E --> F
    F --> G{执行结果}
    G -->|成功| H[保存结果 + 清理会话]
    G -->|轮次耗尽| I[保存session_id + 排入重试队列]
    G -->|错误| J[记录错误 + 人工通知]
    H --> K[生成报告]
    I --> B
    J --> K

5.2 任务定义文件

// 文件: tasks/queue.json
[
  {
    "id": "task-001",
    "name": "安全审计",
    "prompt": "扫描src/目录,检查SQL注入、XSS和其他OWASP Top 10漏洞。对每个漏洞给出文件名、行号、严重程度和修复建议。",
    "allowedTools": ["Read", "Glob", "Grep"],
    "maxTurns": 10,
    "maxCost": 0.30,
    "outputFormat": "json"
  },
  {
    "id": "task-002",
    "name": "生成API文档",
    "prompt": "基于src/routes/目录的路由定义,生成OpenAPI 3.0规范的YAML文档。包含所有端点、请求/响应schema和示例。",
    "allowedTools": ["Read", "Glob", "Grep", "Write"],
    "maxTurns": 15,
    "maxCost": 0.50,
    "outputFormat": "json"
  },
  {
    "id": "task-003",
    "name": "测试覆盖率提升",
    "prompt": "分析当前测试覆盖率报告,为覆盖率低于80%的模块生成新的测试用例。使用项目现有的测试框架。",
    "allowedTools": ["Read", "Edit", "Write", "Bash", "Glob", "Grep"],
    "maxTurns": 25,
    "maxCost": 1.00,
    "outputFormat": "json"
  }
]

5.3 任务运行器核心代码

#!/bin/bash
# 文件: headless-runner.sh
# 用途:生产级Headless任务运行器

set -euo pipefail

# ==================== 配置 ====================
TASKS_FILE="${1:-tasks/queue.json}"
STATE_DIR=".headless-state"
REPORT_DIR="reports"
MAX_RETRIES=3

mkdir -p "$STATE_DIR" "$REPORT_DIR"

# ==================== 工具函数 ====================

# 获取任务状态文件路径
state_file() { echo "$STATE_DIR/$1.state.json"; }

# 检查任务是否完成
is_completed() {
  local sf=$(state_file "$1")
  [ -f "$sf" ] && [ "$(jq -r '.status' "$sf")" = "completed" ]
}

# 检查任务是否需要重试
needs_retry() {
  local sf=$(state_file "$1")
  [ -f "$sf" ] && [ "$(jq -r '.status' "$sf")" = "retry" ] && \
    [ "$(jq -r '.retries' "$sf")" -lt "$MAX_RETRIES" ]
}

# 获取会话ID(用于续传)
get_session_id() {
  local sf=$(state_file "$1")
  if [ -f "$sf" ]; then
    jq -r '.session_id // empty' "$sf"
  fi
}

# ==================== 单任务执行 ====================

run_task() {
  local task_json="$1"
  local task_id=$(echo "$task_json" | jq -r '.id')
  local task_name=$(echo "$task_json" | jq -r '.name')
  local prompt=$(echo "$task_json" | jq -r '.prompt')
  local max_turns=$(echo "$task_json" | jq -r '.maxTurns // 15')
  local max_cost=$(echo "$task_json" | jq -r '.maxCost // 0.50')
  local output_format=$(echo "$task_json" | jq -r '.outputFormat // "json"')
  local tools=$(echo "$task_json" | jq -r '.allowedTools // [] | join(",")')

  local sf=$(state_file "$task_id")
  local session_id=""
  local retries=0

  # 恢复已有状态
  if [ -f "$sf" ]; then
    retries=$(jq -r '.retries // 0' "$sf")
    session_id=$(jq -r '.session_id // empty' "$sf")
  fi

  echo "▶ 执行任务: $task_name (ID: $task_id, 重试: $retries)"

  # 构建Claude命令
  local cmd="claude -p"

  # 会话续传或初始提示
  if [ -n "$session_id" ]; then
    echo "  ↻ 恢复会话: $session_id"
    cmd="$cmd --resume $session_id"
    cmd="$cmd \"继续之前的任务。\""
  else
    # 转义prompt中的引号
    local escaped_prompt=$(echo "$prompt" | sed 's/"/\\"/g')
    cmd="$cmd \"$escaped_prompt\""
  fi

  # 工具限制
  if [ -n "$tools" ]; then
    cmd="$cmd --allowedTools \"$tools\""
  fi

  # 安全限制
  cmd="$cmd --max-turns $max_turns"
  cmd="$cmd --output-format $output_format"

  # 执行并捕获结果
  local result
  set +e
  result=$(eval "$cmd" 2>&1)
  local exit_code=$?
  set -e

  # 解析结果
  local actual_cost="0"
  local actual_turns="0"
  local new_session_id=""
  local is_error="true"

  if [ "$output_format" = "json" ]; then
    actual_cost=$(echo "$result" | jq -r '.cost_usd // 0' 2>/dev/null || echo "0")
    actual_turns=$(echo "$result" | jq -r '.num_turns // 0' 2>/dev/null || echo "0")
    new_session_id=$(echo "$result" | jq -r '.session_id // empty' 2>/dev/null || echo "")
    is_error=$(echo "$result" | jq -r '.is_error // true' 2>/dev/null || echo "true")
  fi

  # 成本检查
  local cost_status="ok"
  if (( $(echo "$actual_cost > $max_cost" | bc -l 2>/dev/null || echo "0") )); then
    cost_status="over_budget"
  fi

  # 确定任务状态
  local task_status="completed"
  if [ "$exit_code" -ne 0 ]; then
    task_status="retry"
  elif [ "$is_error" = "true" ]; then
    task_status="retry"
  elif [ "$cost_status" = "over_budget" ]; then
    task_status="retry"
  fi

  # 保存状态
  cat > "$sf" <<EOF
{
  "task_id": "$task_id",
  "task_name": "$task_name",
  "status": "$task_status",
  "session_id": "$new_session_id",
  "retries": $((retries + 1)),
  "exit_code": $exit_code,
  "cost_usd": $actual_cost,
  "turns": $actual_turns,
  "timestamp": "$(date -Iseconds)"
}
EOF

  # 保存完整结果
  echo "$result" > "$REPORT_DIR/${task_id}-result.json"

  # 状态报告
  case "$task_status" in
    completed)
      echo "  ✅ 完成 | 轮次: $actual_turns | 费用: \$$actual_cost"
      ;;
    retry)
      echo "  ⚠️ 需要重试 | 退出码: $exit_code | 重试: $((retries + 1))/$MAX_RETRIES"
      ;;
  esac

  return 0
}

# ==================== 主循环 ====================

echo "======================================"
echo "  Claude Code Headless 任务运行器"
echo "  任务文件: $TASKS_FILE"
echo "======================================"

TOTAL_TASKS=$(jq 'length' "$TASKS_FILE")
COMPLETED=0
FAILED=0
TOTAL_COST=0

# 遍历所有任务
for i in $(seq 0 $((TOTAL_TASKS - 1))); do
  task_json=$(jq ".[$i]" "$TASKS_FILE")
  task_id=$(echo "$task_json" | jq -r '.id')

  # 跳过已完成的任务
  if is_completed "$task_id"; then
    echo "⏭ 跳过已完成任务: $task_id"
    COMPLETED=$((COMPLETED + 1))
    continue
  fi

  # 检查重试次数
  if [ -f "$(state_file "$task_id")" ]; then
    retries=$(jq -r '.retries // 0' "$(state_file "$task_id")")
    if [ "$retries" -ge "$MAX_RETRIES" ]; then
      echo "❌ 任务 $task_id 已达最大重试次数,跳过"
      FAILED=$((FAILED + 1))
      continue
    fi
  fi

  # 执行任务
  run_task "$task_json"

  # 统计
  sf=$(state_file "$task_id")
  if [ "$(jq -r '.status' "$sf")" = "completed" ]; then
    COMPLETED=$((COMPLETED + 1))
  fi
  TOTAL_COST=$(echo "$TOTAL_COST + $(jq -r '.cost_usd // 0' "$sf")" | bc)
done

# ==================== 生成报告 ====================

echo ""
echo "======================================"
echo "  执行报告"
echo "======================================"
echo "总任务数: $TOTAL_TASKS"
echo "已完成:   $COMPLETED"
echo "失败:     $FAILED"
echo "总费用:   \$$TOTAL_COST"
echo ""

# 生成JSON报告
jq -n \
  --arg total "$TOTAL_TASKS" \
  --arg completed "$COMPLETED" \
  --arg failed "$FAILED" \
  --arg cost "$TOTAL_COST" \
  --arg timestamp "$(date -Iseconds)" \
  '{
    total_tasks: ($total | tonumber),
    completed: ($completed | tonumber),
    failed: ($failed | tonumber),
    total_cost_usd: ($cost | tonumber),
    timestamp: $timestamp
  }' > "$REPORT_DIR/summary.json"

echo "报告已保存到: $REPORT_DIR/"

5.4 使用方式

# 赋予执行权限
chmod +x headless-runner.sh

# 执行所有任务
./headless-runner.sh tasks/queue.json

# 查看某个任务的结果
cat reports/task-001-result.json | jq -r '.result'

# 查看执行摘要
cat reports/summary.json | jq .

# 重新运行未完成的任务(已完成的会自动跳过)
./headless-runner.sh tasks/queue.json

六、高级模式

6.1 多任务并行执行

利用Shell的后台任务实现并行:

#!/bin/bash
# 文件: parallel-runner.sh
# 用途:并行执行多个独立任务

MAX_PARALLEL=3  # 最大并行数
RUNNING=0

run_with_limit() {
  while [ "$(jobs -r | wc -l)" -ge "$MAX_PARALLEL" ]; do
    sleep 5
  done
  eval "$1" &
}

# 定义并行任务
TASKS=(
  'claude -p "扫描src/的安全漏洞" --allowedTools "Read,Glob,Grep" --max-turns 10 --output-format json > reports/security.json'
  'claude -p "分析代码复杂度" --allowedTools "Read,Glob,Grep" --max-turns 8 --output-format json > reports/complexity.json'
  'claude -p "检查依赖版本过期" --allowedTools "Read,Bash(npm*)" --max-turns 5 --output-format json > reports/deps.json'
  'claude -p "生成API文档" --allowedTools "Read,Glob,Grep,Write" --max-turns 15 --output-format json > reports/api-docs.json'
)

for task in "${TASKS[@]}"; do
  run_with_limit "$task"
done

# 等待所有任务完成
wait

echo "所有并行任务完成"

6.2 定时任务集成

将Headless执行嵌入cron实现定期自动化:

# 编辑crontab
crontab -e

# 每天凌晨2点执行代码审查
0 2 * * * cd /path/to/project && claude -p "审查最近24小时的代码提交,检查潜在问题" --allowedTools "Read,Glob,Grep,Bash(git*)" --max-turns 15 --output-format json > /var/log/claude-review-$(date +\%Y\%m\%d).json

# 每周一生成架构文档
0 3 * * 1 cd /path/to/project && claude -p "更新项目架构文档,反映最新代码结构" --allowedTools "Read,Glob,Grep,Write" --max-turns 20 --output-format json > /var/log/claude-arch-$(date +\%Y\%m\%d).json

6.3 链式会话:多步骤工作流

通过会话续传实现复杂的多步骤自动化:

#!/bin/bash
# 文件: chain-workflow.sh
# 用途:链式执行——上一步的输出是下一步的输入

set -euo pipefail

echo "=== 第1步:代码分析 ==="
STEP1=$(claude -p "分析src/目录的代码质量,列出前10个最需要改进的文件和具体问题" \
  --allowedTools "Read,Glob,Grep" \
  --max-turns 10 \
  --output-format json)

SESSION=$(echo "$STEP1" | jq -r '.session_id')
echo "分析完成,会话ID: $SESSION"

echo "=== 第2步:制定修复计划 ==="
STEP2=$(claude -p --resume "$SESSION" \
  "基于上面的分析,为每个问题制定具体的修复方案。输出为JSON格式,包含file、issue、fix字段。" \
  --max-turns 10 \
  --output-format json)

echo "修复计划完成"

echo "=== 第3步:执行修复 ==="
STEP3=$(claude -p --resume "$SESSION" \
  "按照修复方案逐个修复。每修复一个文件后运行相关测试确认没有破坏功能。" \
  --allowedTools "Read,Edit,Write,Bash(npm*),Glob,Grep" \
  --max-turns 30 \
  --output-format json)

echo "修复完成,费用: \$(echo "$STEP3" | jq -r '.cost_usd')"

echo "=== 第4步:验证 ==="
STEP4=$(claude -p --resume "$SESSION" \
  "运行完整测试套件,确认所有修复没有引入新问题。给出最终报告。" \
  --allowedTools "Read,Bash(npm*),Glob,Grep" \
  --max-turns 5 \
  --output-format json)

echo "$STEP4" | jq -r '.result'
echo "=== 工作流完成 ==="
echo "总费用: \$(echo "$STEP4" | jq -r '.cost_usd')"

6.4 错误恢复策略

#!/bin/bash
# 文件: resilient-exec.sh
# 用途:带自动恢复的Headless执行

execute_with_recovery() {
  local prompt="$1"
  local max_attempts=3
  local attempt=1
  local session_id=""

  while [ "$attempt" -le "$max_attempts" ]; do
    echo "尝试 $attempt/$max_attempts..."

    local cmd="claude -p"
    if [ -n "$session_id" ]; then
      cmd="$cmd --resume $session_id"
      cmd="$cmd \"上一次执行遇到问题,请继续完成任务。检查之前的操作是否完整。\""
    else
      cmd="$cmd \"$prompt\""
    fi

    cmd="$cmd --max-turns 15 --output-format json"

    local result
    set +e
    result=$(eval "$cmd" 2>&1)
    local exit_code=$?
    set -e

    session_id=$(echo "$result" | jq -r '.session_id // empty' 2>/dev/null || echo "")
    local is_error=$(echo "$result" | jq -r '.is_error // true' 2>/dev/null || echo "true")

    if [ "$exit_code" -eq 0 ] && [ "$is_error" = "false" ]; then
      echo "执行成功"
      echo "$result" | jq -r '.result'
      return 0
    fi

    echo "执行失败 (退出码: $exit_code)"
    echo "错误信息: $(echo "$result" | jq -r '.result' 2>/dev/null | head -5)"

    attempt=$((attempt + 1))

    if [ "$attempt" -le "$max_attempts" ]; then
      echo "等待10秒后重试..."
      sleep 10
    fi
  done

  echo "所有尝试均失败"
  return 1
}

# 使用
execute_with_recovery "为UserService添加完整的CRUD接口和对应的单元测试"

七、常见问题与陷阱

7.1 会话过大导致恢复失败

问题:长链式会话的上下文可能超出Token限制。

解决方案:

# 不要无限续传,定期创建新会话并总结
# 每5次续传后,总结当前状态,开启新会话

# 步骤1-N:正常续传
# 步骤N+1:总结并开启新会话
claude -p "总结当前项目的重构状态:已完成什么、还剩什么、下一步做什么" \
  --resume "$OLD_SESSION" \
  --output-format json | jq -r '.result' > /tmp/summary.txt

# 新会话,以总结作为上下文
claude -p "$(cat /tmp/summary.txt)\n\n基于以上状态,继续完成剩余工作。" \
  --output-format json

7.2 Headless模式下的编码问题

问题:非UTF-8环境的输出可能乱码。

解决方案:

# 强制UTF-8环境
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8

# 或在脚本开头
# -*- coding: utf-8 -*-

7.3 权限被拒绝导致静默失败

问题:Headless模式下权限被拒绝不会报错,只是工具不执行。

解决方案:

# 始终检查JSON输出的is_error字段
claude -p "修改配置文件" --output-format json | \
  jq 'if .is_error then error("执行失败: \(.result)") else . end'

7.4 并行任务的Token竞争

问题:多个Headless实例同时运行可能触发API限速。

解决方案:

# 控制并行数,避免并发超过3
MAX_CONCURRENT=3

# 使用flock实现互斥
for task in "${TASKS[@]}"; do
  while [ "$(jobs -r | wc -l)" -ge "$MAX_CONCURRENT" ]; do
    sleep 10
  done
  (
    flock -x 200
    eval "$task"
  ) 200>/tmp/claude-concurrent.lock &
done

八、调试与可观测性

8.1 详细日志

# 启用verbose输出
claude -p "你的任务" --verbose --output-format json 2>debug.log

# debug.log包含:
# - Agent循环的每一步推理
# - 工具调用的完整参数和返回
# - Token使用统计
# - 上下文窗口状态

8.2 执行时间线分析

#!/bin/bash
# 分析多个任务的执行效率

for report in reports/*-result.json; do
  jq -s '{
    task: .[0].session_id[:8],
    turns: .[0].num_turns,
    cost: .[0].cost_usd,
    cost_per_turn: (.turns > 0 | (.cost / .turns) | . * 100 | round / 100)
  }' "$report" 2>/dev/null
done | jq -s 'sort_by(.cost_per_turn) | {
  tasks: .,
  avg_cost_per_turn: (map(.cost_per_turn) | add / length),
  total_cost: (map(.cost) | add)
}'

延伸阅读

训练营适用性评估

维度 评估
适合训练营类型 DevOps自动化训练营、AI工程化训练营、生产级AI集成课程
教学时长 4-5小时(含实操)
难度等级 中高级
前置要求 Claude Code基础使用、Shell脚本编写、JSON处理
实操占比 70%实操 + 30%理论
硬件要求 每人需要Anthropic API Key和稳定的网络环境

参考来源

  1. Run Claude Code programmatically — Anthropic官方文档 — Headless模式的权威参考,涵盖-p标志、输出格式、权限配置
  2. Claude Code Headless — Adriano Melo — 实战角度的Headless模式使用指南,包含丰富的用例示例
  3. Using Claude Code Programmatically — JacobFV Gist — 绕过TUI的完整技术方案,涵盖输出格式化和Schema约束
  4. Headless Claude in CI — AgentPatterns — CI/CD管道中-p模式和--max-turns的安全集成模式
  5. Headless Mode — Background Claude — 后台运行Claude Code的非交互模式详解

[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。

51学通信知识星球海报-扫码加入