调用 Claude Code CLI 问题总结

遇到的问题及解决方案

问题 1: Windows 编码问题

现象:

UnicodeDecodeError: 'gbk' codec can't decode byte 0x80 in position 33: illegal multibyte sequence

原因分析:

  • Windows 中文系统默认使用 GBK 编码
  • subprocess.run(text=True) 自动用系统默认编码(GBK)解码输出
  • Claude CLI 返回 UTF-8 编码内容
  • GBK 无法解码 UTF-8 字节序列

解决方案:

# ❌ 错误写法
result = subprocess.run(cmd, capture_output=True, text=True, shell=True)
output = result.stdout  # GBK 解码失败

# ✅ 正确写法
result = subprocess.run(cmd, capture_output=True, text=False, shell=True)
output = result.stdout.decode('utf-8', errors='ignore')  # 手动 UTF-8 解码

subprocess 模块详解

基本概念

subprocess 是 Python 的标准库模块,用于在 Python 程序中运行外部命令(如 claudegitnpm 等)。

核心用法

import subprocess

# 运行外部命令
result = subprocess.run(
    ["claude", "--version"],  # 命令及参数列表
    capture_output=True,      # 捕获输出
    text=False,               # 返回字节(不自动解码)
    shell=True                # Windows 需要
)

# 获取结果
print(result.returncode)  # 返回码(0=成功)
print(result.stdout)      # 标准输出
print(result.stderr)      # 错误输出

同步 vs 异步调用

同步调用(阻塞,等待完成):

import subprocess

result = subprocess.run(
    ["claude", "-p", "你好"],
    capture_output=True,
    text=False,
    timeout=60,
    shell=True
)

output = result.stdout.decode('utf-8')

异步调用(非阻塞,可并发):

import asyncio

async def call_claude():
    # Windows 用 create_subprocess_shell
    process = await asyncio.create_subprocess_shell(
        "claude -p 你好",
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.PIPE
    )
    
    stdout, stderr = await process.communicate()
    output = stdout.decode('utf-8')
    return output

# 运行
result = asyncio.run(call_claude())

关键参数说明

参数说明推荐值
capture_output是否捕获输出True
text是否自动解码为文本False(手动解码避免编码问题)
shell是否通过 shell 运行True(Windows 需要)
timeout超时时间(秒)60600
cwd工作目录项目路径

Windows 特殊处理

问题: Windows 下 subprocess.run(["claude", "..."]) 可能找不到命令

原因:

  • Windows 需要通过 shell 解析命令
  • 直接调用可执行文件名(如 claude)需要 shell

解决方案:

# ✅ 同步:使用 shell=True
subprocess.run(["claude", "--version"], shell=True)

# ✅ 异步:使用 create_subprocess_shell
await asyncio.create_subprocess_shell("claude --version")

使用 Python 调用 Claude Code CLI 的完整流程

1. 基本调用

import subprocess

# 构建命令
cmd = [
    "claude",
    "-p", "请分析这段代码",
    "--model", "claude-sonnet-4-5-20250929",
    "--output-format", "stream-json"
]

# 执行命令
result = subprocess.run(
    cmd,
    capture_output=True,
    text=False,  # 字节模式
    timeout=60,
    shell=True,  # Windows 需要
    cwd="/path/to/project"  # 工作目录
)

# 处理结果
if result.returncode == 0:
    output = result.stdout.decode('utf-8', errors='ignore')
    print(f"成功: {output}")
else:
    error = result.stderr.decode('utf-8', errors='ignore')
    print(f"失败: {error}")

2. 异步批量调用(项目分析场景)

import asyncio

async def analyze_batch(files):
    """分析一批文件"""
    prompt = f"分析这些文件: {', '.join(files)}"
    
    # 构建命令
    cmd = f'claude -p "{prompt}" --model claude-sonnet-4-5-20250929'
    
    # 异步执行
    process = await asyncio.create_subprocess_shell(
        cmd,
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.PIPE,
        cwd="/path/to/project"
    )
    
    # 等待完成(10分钟超时)
    stdout, stderr = await asyncio.wait_for(
        process.communicate(),
        timeout=600
    )
    
    # 解析结果
    if process.returncode == 0:
        output = stdout.decode('utf-8', errors='ignore')
        return parse_json(output)
    else:
        raise RuntimeError(stderr.decode('utf-8', errors='ignore'))

# 批量处理
async def analyze_project(file_batches):
    tasks = [analyze_batch(batch) for batch in file_batches]
    results = await asyncio.gather(*tasks, return_exceptions=True)
    return results

# 运行
batches = [["file1.java", "file2.java"], ["file3.java", "file4.java"]]
results = asyncio.run(analyze_project(batches))

3. 错误处理最佳实践

try:
    result = subprocess.run(
        cmd,
        capture_output=True,
        text=False,
        timeout=60,
        shell=True
    )
    
    if result.returncode != 0:
        # 解码错误信息
        error = result.stderr.decode('utf-8', errors='ignore')
        
        # 分析常见错误
        if "API key" in error:
            print("错误:API 密钥未配置")
        elif "git-bash" in error:
            print("错误:Git Bash 未找到")
        elif "not found" in error:
            print("错误:Claude CLI 未安装")
        else:
            print(f"未知错误: {error}")
    
except subprocess.TimeoutExpired:
    print("错误:命令执行超时")
except FileNotFoundError:
    print("错误:找不到 claude 命令,请检查 PATH")
except Exception as e:
    print(f"错误:{e}")

总结

核心要点

  • 编码处理:Windows 下使用 text=False + 手动 UTF-8 解码
  • Shell 支持:Windows 需要 shell=True
  • 异步处理:大项目分批用 asyncio 提高效率
  • 错误日志:详细的 DEBUG 输出帮助快速定位问题

subprocess 本质

  • 作用:Python 中运行外部命令的桥梁
  • 输入:命令、参数、配置
  • 输出:返回码、标准输出、错误输出
  • 特点:可同步、可异步、可超时、可捕获输出
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐