跳转到内容

修复非交互式 Shell 中 Claude Code 的 SSH Agent 访问问题

当使用 Claude Code 等 AI 编码助手通过 SSH 执行 Git 操作时,可能会遇到身份验证失败的问题,原因是非交互式 Shell 无法访问 SSH Agent。本文记录了根本原因分析,并提供了适用于交互式终端和自动化工具的完整解决方案。

问题:通过 Claude Code 的 Bash 工具执行 git commitgit push 时失败,报错”Couldn’t get agent socket”。

解决方案:在 ~/.bashrc 的交互式检查之前配置 SSH Agent 套接字发现,结合交互式会话的自动代理启动和密钥加载。


当 Claude Code(或任何使用非交互式 Shell 的工具)尝试执行需要 SSH 身份验证的 Git 命令时,会出现以下错误:

Terminal window
error: Couldn't get agent socket?
fatal: failed to write commit object

然而,在交互式终端中直接执行相同的命令却完全正常。


  • 操作系统:WSL2(Windows 上的 Ubuntu)
  • Shell:Bash
  • Git 远程仓库:SSH 协议(git@github.com:username/repo.git
  • 工具:Claude Code(使用非交互式 Bash Shell)
  • SSH 密钥:Ed25519 密钥,位于 ~/.ssh/

标准的 ~/.bashrc 配置在开头包含这样的检查:

Terminal window
# If not running interactively, don't do anything
case $- in
*i*) ;;
*) return;;
esac

作用

  • 检查当前 Shell 是否为交互式(通过在 Shell 标志中查找 ‘i’)
  • 如果是非交互式,立即返回,阻止后续所有配置的执行

大多数 SSH Agent 配置都放置在 ~/.bashrc 中这个检查之后

Terminal window
# ~/.bashrc (第 ~120 行左右)
if [ -z "$SSH_AUTH_SOCK" ]; then
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_*
fi

结果:非交互式 Shell 永远不会执行到 SSH Agent 配置,因此 SSH_AUTH_SOCK 永远不会被设置。

Claude Code 的 Bash 工具:

  • 为每个命令执行创建非交互式 Shell
  • 每次执行都是独立的会话
  • 不会继承用户终端的环境变量
  • 会加载 ~/.bashrc,但由于交互式检查而提前退出

Terminal window
$ head -10 ~/.bashrc

输出确认存在:

Terminal window
case $- in
*i*) ;;
*) return;;
esac
Terminal window
$ echo $-
# 交互式:himBHs(包含 'i')
# 非交互式:hmtBc(不包含 'i')

步骤 3:验证非交互式 Shell 中的 SSH_AUTH_SOCK

Section titled “步骤 3:验证非交互式 Shell 中的 SSH_AUTH_SOCK”
Terminal window
$ bash -c 'echo "SSH_AUTH_SOCK: $SSH_AUTH_SOCK"'
# 输出:SSH_AUTH_SOCK:(空)
Terminal window
$ find /tmp -type s -path "*ssh*agent*" -user "$USER" 2>/dev/null
# 输出:(空 - 没有运行的 Agent)
Terminal window
$ ssh-add -l
# 输出:Could not open a connection to your authentication agent.
username/repo.git
$ git remote -v

结论:问题确实存在 - 非交互式 Shell 无法访问 SSH Agent,通过 SSH 的 Git 操作将失败。


解决方案涉及在 ~/.bashrc两个位置配置 SSH Agent:

case $- 交互式检查之前添加(大约第 5 行):

Terminal window
# === SSH Agent 配置(用于非交互式 Shell) ===
if [ -z "$SSH_AUTH_SOCK" ]; then
export SSH_AUTH_SOCK=$(find /tmp -type s -path "*ssh*agent*" -user "$USER" 2>/dev/null | head -1)
fi
# ============================================================

目的

  • 允许非交互式 Shell 查找并使用现有的 SSH Agent 套接字
  • 在 Shell 返回之前执行
  • 轻量级 - 只导出一个环境变量

case $- 检查之后添加(大约第 120 行以后):

Terminal window
# === SSH Agent 自动启动(交互式 Shell) ===
# 检查 SSH Agent 是否已运行
if [ -z "$SSH_AUTH_SOCK" ]; then
# 尝试查找现有的 Agent 套接字
EXISTING_SOCK=$(find /tmp -type s -path "*ssh*agent*" -user "$USER" 2>/dev/null | head -1)
if [ -n "$EXISTING_SOCK" ]; then
export SSH_AUTH_SOCK="$EXISTING_SOCK"
else
# 如果不存在则启动新的 Agent
eval "$(ssh-agent -s)" >/dev/null
fi
fi
# 如果 Agent 正在运行且密钥未加载,则添加 SSH 密钥
if [ -n "$SSH_AUTH_SOCK" ]; then
ssh-add -l >/dev/null 2>&1
if [ $? -eq 1 ]; then
# Agent 没有身份,添加密钥
for key in ~/.ssh/id_*; do
if [ -f "$key" ] && [[ "$key" != *.pub ]]; then
ssh-add "$key" 2>/dev/null
fi
done
fi
fi
# =====================================================

目的

  • 在打开新终端时自动启动 SSH Agent(如果尚未运行)
  • 自动从 ~/.ssh/ 加载所有 SSH 私钥
  • 避免重复添加密钥
  • 在多个终端会话间重用现有 Agent

编辑 ~/.bashrc

Terminal window
# ~/.bashrc: 由 bash(1) 为非登录 Shell 执行。
# === SSH Agent 配置(用于非交互式 Shell) ===
if [ -z "$SSH_AUTH_SOCK" ]; then
export SSH_AUTH_SOCK=$(find /tmp -type s -path "*ssh*agent*" -user "$USER" 2>/dev/null | head -1)
fi
# ============================================================
# 如果不是交互式运行,不执行任何操作
case $- in
*i*) ;;
*) return;;
esac
# ...(其余标准 bashrc 配置)...
# === SSH Agent 自动启动(交互式 Shell) ===
if [ -z "$SSH_AUTH_SOCK" ]; then
EXISTING_SOCK=$(find /tmp -type s -path "*ssh*agent*" -user "$USER" 2>/dev/null | head -1)
if [ -n "$EXISTING_SOCK" ]; then
export SSH_AUTH_SOCK="$EXISTING_SOCK"
else
eval "$(ssh-agent -s)" >/dev/null
fi
fi
if [ -n "$SSH_AUTH_SOCK" ]; then
ssh-add -l >/dev/null 2>&1
if [ $? -eq 1 ]; then
for key in ~/.ssh/id_*; do
if [ -f "$key" ] && [[ "$key" != *.pub ]]; then
ssh-add "$key" 2>/dev/null
fi
done
fi
fi
# =====================================================

Terminal window
$ eval "$(ssh-agent -s)"
# 输出:Agent pid 2245478
$ ssh-add ~/.ssh/id_ed25519
# 输出:Identity added: /home/username/.ssh/id_ed25519 (user@example.com)

测试 2:验证非交互式 Shell 访问

Section titled “测试 2:验证非交互式 Shell 访问”
/tmp/ssh-xxxxx/agent.2245477
$ bash -c 'source ~/.bashrc; echo "SSH_AUTH_SOCK: $SSH_AUTH_SOCK"; ssh-add -l'
# 输出:
# 256 SHA256:xxx... user@example.com (ED25519)

成功:非交互式 Shell 现在可以访问 SSH Agent 了!

测试 3:验证交互式 Shell 自动启动

Section titled “测试 3:验证交互式 Shell 自动启动”
Terminal window
$ bash -i -c 'ssh-add -l'
# 输出:256 SHA256:xxx... user@example.com (ED25519)

成功:密钥在新的交互式会话中自动加载!


像 Claude Code 这样的 AI 编码助手使用非交互式 Shell,当配置放在 ~/.bashrc 的交互式检查之后时,无法访问 SSH Agent。

分两部分配置 SSH Agent:

  1. 交互式检查之前:通过查找现有 Agent 套接字导出 SSH_AUTH_SOCK
  2. 交互式检查之后:为交互式会话自动启动 Agent 并加载密钥
  • 交互式终端:自动启动 SSH Agent 并加载密钥
  • 非交互式 Shell:自动发现 SSH Agent 套接字
  • Claude Code:可以执行需要 SSH 认证的 Git 操作
  • CI/CD 流水线:可以访问 SSH Agent(如果套接字存在)
  • 多个终端:共享同一个 SSH Agent 实例

现在可以工作 ✅:

  • Claude Code 执行 git commitgit pushgit pull
  • 使用 SSH/Git 的 Shell 脚本
  • 自动化部署脚本
  • 任何使用非交互式 Bash Shell 的工具

一直可以工作 ✅:

  • 终端中的手动 Git 操作
  • 交互式 SSH 连接
  • 标准开发工作流
  • 这种方法轻量且安全
  • 在交互式检查之前只导出环境变量
  • 没有副作用或性能影响
  • 兼容 WSL、Linux 和 macOS
  • 适用于任何基于 SSH 的工具(Git、rsync、scp 等)


作者注:此解决方案是在 WSL2 环境中解决 Claude Code 的 Git 集成问题时开发的。调查过程和解决方案可能对任何使用 AI 编码助手或通过 SSH 执行 Git 命令的自动化工具的人有所帮助。