如何在 VPS 上配置 Claude Code 状态栏
通过 statusLine 脚本在提示符下显示主机名、目录、Git 分支和模型,快速确认当前 VPS,避免在多个 SSH 会话中操作错误服务器。
Claude Code 状态栏显示的内容
Claude Code 状态栏是提示符下方的一行文本,用于显示您编写的脚本输出。将一个 statusLine 代码块添加到 settings.json,并将其指向某个命令。Claude Code 运行该命令,通过标准输入将会话状态以 JSON 形式传递给它,然后输出该命令写入标准输出的内容。
这就是完整的接口约定。脚本从标准输入读取 JSON,并将文本输出到标准输出。脚本在您的计算机上运行,输出内容不会发送给模型,因此不会消耗 token。
在只有一个项目的笔记本电脑上,状态栏只是装饰。在 3 台服务器上,它则是一道安全防线。每个 Claude Code 会话在每个终端中的显示方式都相同,因此 4 个没有标签的 SSH 窗口很容易让迁移操作落到错误的服务器上。让状态栏以主机名开头,就能避免这类错误。
statusLine 设置在 settings.json 中的位置
将其写入用户设置 ~/.claude/settings.json。该设置适用于此计算机上的所有项目。仓库中的项目设置位于 .claude/settings.json,也可以使用,并且对该目录具有更高优先级。
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh"
}
}type 始终为 "command"。command 值会通过 shell 执行,因此可以是脚本路径或普通命令。在编写脚本前,先确认连接配置正常:
{
"statusLine": {
"type": "command",
"command": "hostname -s"
}
}启动 Claude Code 并发送一条消息。此时,提示符下方的状态栏会显示服务器的短主机名。如果状态栏仍为空,问题出在设置或信任对话框,而不是脚本。请参阅下文的“为什么状态栏仍为空”。
截至 August 2026,有 3 个可选键。padding 用字符数添加水平间距,默认值为 0。refreshInterval 会在常规触发条件之外,每 N 秒重新运行一次命令,最小值为 1。只有当状态栏显示时钟或其他会在会话空闲期间变化的内容时,才需要使用此设置。hideVimModeIndicator 会隐藏内置的 -- INSERT -- 文本,适用于自定义脚本已经渲染 vim 模式的情况。
状态栏脚本会收到哪些数据?
不要相信任何地方列出的字段,包括本页。请捕获脚本实际收到的对象。编写一个一次性使用的脚本,将 stdin 保存到文件:
cat > ~/.claude/statusline-capture.sh <<'EOF'
#!/bin/bash
cat > /tmp/statusline-input.json
echo "captured"
EOF
chmod +x ~/.claude/statusline-capture.sh让 statusLine.command 指向该文件,启动会话并发送一条消息。状态栏会读取 captured。现在查看收到的内容:
jq . /tmp/statusline-input.json这样即可获得当前构建版本的确切数据结构。今后每次更新改变数据结构时,都可以重新执行此操作。
截至 2026 年 8 月,稳定部分是嵌套对象,而不是扁平键。model 包含 id 和 display_name。workspace 包含 current_dir 和 project_dir:current_dir 表示会话当前所在的位置,project_dir 表示会话启动的位置;工作目录在会话期间发生变化后,两者会不同。顶层的 cwd 与 workspace.current_dir 包含相同的值。context_window 包含令牌计数以及预先计算的 used_percentage。cost 包含 total_cost_usd 和时长计数器。session_id 在整个会话生命周期内保持不变,并且在不同会话之间唯一,这对后续缓存很重要。
以下 3 条规则可以让脚本在数据结构变化后继续运行。
某些键不存在,而不是值为 null。 只有对应功能启用时,vim、agent、pr、worktree 和 effort 才会出现。vim 模式关闭时,使用 jq -r 读取 .vim.mode 会输出字面字符串 null,状态栏会向读者显示 null。请在每个选择器后追加 // empty,这样缺失的键就不会输出任何内容。
某些值在早期阶段为 null。 首次 API 响应前,context_window.used_percentage 和 context_window.current_usage 为 null;current_usage 在 /compact 后会恢复为 null,直到下一次调用重新填充它。因此,状态栏上的上下文百分比需要使用 // 0,否则每个会话开始后的几秒内都会读取 null。将该数字放到状态栏之前,了解上下文窗口实际如何填充会很有帮助。
JSON 中不包含 git 分支。 没有任何字段提供该信息。状态栏显示的任何分支名称,都来自脚本自行运行 git。
一个失败时降级而不是中断的状态栏脚本
这是可直接复制粘贴的版本。它会输出主机名、工作目录、git 分支和模型名称。每个字段都有回退值,因此即使输入的是空 JSON 对象,也能生成可用的状态行。
#!/bin/bash
# ~/.claude/statusline.sh
input=$(cat)
# Read one field. Prints nothing when the key is missing or null.
field() { printf '%s' "$input" | jq -r "$1 // empty" 2>/dev/null; }
HOST=$(hostname -s 2>/dev/null)
[ -z "$HOST" ] && HOST="host"
DIR=$(field '.workspace.current_dir')
[ -z "$DIR" ] && DIR=$(field '.cwd')
[ -z "$DIR" ] && DIR="$PWD"
MODEL=$(field '.model.display_name')
[ -z "$MODEL" ] && MODEL="claude"
SHORT="$DIR"
if [ -n "$HOME" ]; then
case "$DIR" in
"$HOME") SHORT="~" ;;
"$HOME"/*) SHORT="~/${DIR#"$HOME"/}" ;;
esac
fi
BRANCH=""
if git -C "$DIR" rev-parse --git-dir >/dev/null 2>&1; then
BRANCH=$(git -C "$DIR" branch --show-current 2>/dev/null)
[ -z "$BRANCH" ] && BRANCH="detached"
fi
CYAN=$'\033[36m'
YELLOW=$'\033[33m'
DIM=$'\033[2m'
RESET=$'\033[0m'
LINE="${CYAN}${HOST}${RESET} ${SHORT}"
[ -n "$BRANCH" ] && LINE="${LINE} ${YELLOW}${BRANCH}${RESET}"
LINE="${LINE} ${DIM}${MODEL}${RESET}"
printf '%s\n' "$LINE"每次读取都通过 field 完成,field 会追加 // empty,因此键被重命名或删除时会得到空字符串,下一行会提供默认值。目录会依次从 workspace.current_dir 回退到 cwd,再回退到 $PWD。分支来自 git -C "$DIR",而不是直接使用 git,因此分支始终与状态栏显示的目录一致。
保存文件,然后添加可执行权限:
chmod +x ~/.claude/statusline.sh可执行位不是可选项。Claude Code 会通过 shell 执行该命令,因此缺少 +x 的脚本会因 Permission denied 失败,不产生 stdout,状态行也会保持空白,且不会显示任何错误。
jq 用于在命令行解析 JSON,但全新安装的 Ubuntu 服务器中未预装该工具:
sudo apt update && sudo apt install -y jq然后将该设置指向此脚本,并使用上方第一个 settings.json 代码块。
在信任脚本前先进行测试
手动运行两次。第一次使用普通的会话对象:
echo '{"model":{"display_name":"Opus"},"workspace":{"current_dir":"/srv/api"},"session_id":"t1"}' | ~/.claude/statusline.sh输出主机名,然后是 /srv/api,最后是 Opus。不会显示分支,因为你计算机上的 /srv/api 可能不是 Git 仓库。
第二次进行降级测试。这一步经常被忽略:
echo '{}' | ~/.claude/statusline.sh模式变更可能传入的最坏情况是空对象。这一行仍会输出主机名、$PWD 提供的当前目录,以及模型名称所在位置的 claude。不会发生崩溃,也不会输出 null。通过此测试的脚本可以应对字段重命名,因为对脚本而言,字段重命名和字段缺失是同一种情况。
您应看到的效果
状态栏会单独显示在内置页脚徽标上方,不会替换这些徽标。在正常配置中,它占一行:先显示青色的短主机名,然后显示工作目录,其中主目录会折叠为 ~;如果该目录是 Git 仓库,则以黄色显示分支名称;最后显示变暗的模型名称。整体效果应接近 web-01 ~/api main Opus,这四部分使用不同颜色。
会话启动时(包括恢复会话)、收到新的助手消息后、/compact 完成后、权限模式更改时、Vim 模式切换时,以及在设置了 refreshInterval 的情况下每次计时触发时,该行都会重新运行脚本。更新采用 300 ms 防抖,因此一批连续变更只会运行脚本一次。在自动补全、帮助菜单和权限提示期间,该栏会隐藏,之后恢复显示。
主机名应放在最前面
如果您让代理在多台服务器上运行,终端是唯一能告诉您当前所在位置的界面,但终端提供的信息并不可靠。在 tmux 窗格中打开第二个 ssh 连接时,窗口标题通常仍显示旧名称,因为设置标题的 shell 并不知道连接已经切换。让 Claude Code 在 VPS 的分离 tmux 会话中运行,一天后重新连接,屏幕上仍没有任何信息可以区分构建服务器和生产服务器。
状态栏不同。它由 Claude Code 针对每个会话单独渲染,使用的是该会话持有的数据。它不会从错误的窗格继承信息,也不会因为 shell 提示符未刷新而保持过期。状态栏显示的就是代理正在写入文件的服务器。
为每台服务器设置不同的颜色,这样无需先阅读文字就能识别服务器。在 LINE= 赋值语句上方添加以下两行:
CODE=$(printf '%s' "$HOST" | cksum | cut -d' ' -f1)
HOST_COLOR=$(printf '\033[%dm' "$((31 + CODE % 6))")然后用 ${HOST_COLOR} 替代 ${CYAN}。cksum 会输出主机名的校验和,因此同一个名称始终会映射到相同的颜色。颜色范围为 31 到 36,依次从红色到青色。将同一个脚本复制到每台服务器上,每台服务器就会显示自己的标识。
目录也出于同样的原因值得显示。在 ssh 命令中,/srv/api 和 /srv/api-staging 只差一个按键,但实际影响可能相差整个故障处理过程。另外两个值得占用状态栏空间的是模型和分支。模型可以让您知道恢复的是哪个会话,分支则可以让您确认代理是否即将提交到 main。
在小屏幕上,这些信息更加重要,因为没有窗口标题可用。如果您使用这种环境,请参阅通过手机驱动 Claude Code。
保持脚本运行快速
您的脚本会在每条助手消息后运行,而 Claude Code 会在收到新更新时取消正在运行的任务。因此,运行缓慢的脚本会显示过时文本,或者不显示任何文本。
每次 jq 调用只需几毫秒。git 才是变慢的部分:在大型代码仓库中,缓存未命中时,git status 需要数百毫秒。上面的脚本会有意避免使用 git status,而是调用 git branch --show-current;后者读取 .git/HEAD,可以立即返回。
如果您添加了更耗时的操作,请将结果缓存到文件中,并每隔几秒刷新一次。使用会话标识作为文件键:
CACHE="/tmp/statusline-$(field '.session_id')"使用 session_id,不要使用 $$。$$ 是脚本的进程 ID,每次调用都会不同,因此以它为键的缓存永远不会命中,您每次都要承担完整的执行开销。session_id 在整个会话期间保持稳定,并且在不同会话之间不同,因此,两个位于不同代码仓库中的 Claude Code 会话无法读取对方缓存的分支名称。会话会按设计保持这种隔离;如果要让一个会话向另一个会话传递工作,就必须执行明确的操作。这正是从一个 Claude Code 会话向另一个会话发送消息的用途。
还有一个值得注意的限制:tput cols 在 statusline 脚本中不起作用。Claude Code 会捕获输出,而不是将脚本附加到终端,因此无法检测终端宽度。Claude Code 会在运行命令前设置 COLUMNS 和 LINES 环境变量;从 v2.1.153 开始支持此行为。因此,需要决定输出多少内容时,请读取 $COLUMNS。
状态栏为何保持空白
完全没有任何显示。 使用 ls -l ~/.claude/statusline.sh 检查执行权限,然后使用上面的模拟输入手动运行脚本。如果脚本在 shell 中输出一行,但在 Claude Code 中不显示,请先使用 claude --debug。该命令会记录本次会话首次运行状态栏时的退出码和 stderr。
调试日志显示 Status line command skipped: workspace trust not accepted。 状态栏会执行 shell 命令,因此受与 hooks 相同的工作区信任门控限制。在您接受该目录的信任对话框之前,该命令不会运行。这在 VPS 上很常见,因为每个新克隆的目录都可能是 Claude Code 尚未识别的目录。在该目录中重启 Claude Code,然后接受对话框。
所有内容都是空白,并且已设置 disableAllHooks。 settings.json 中的 "disableAllHooks": true 也会禁用状态栏,因为它使用相同的 shell 执行门控。删除该设置,或将其设为 false。
该行输出 null。 jq 选择器访问了缺失或值为 null 的键,而 jq -r 会将 null 输出为 4 个字符的 null。为文本添加 // empty,为数字添加 // 0。
编辑脚本后,该行立即变为空白。 退出码非零或没有任何输出的命令都会使该行变为空白。常见原因是末尾存在类似 [ -n "$BRANCH" ] && LINE="..." 的行:分支为空时该命令会以 1 退出,并使整个脚本使用该退出码。将 printf 保持为最后一行,或添加 exit 0。
转义码显示为字面文本,例如状态栏中的 \e]8;;。使用 printf '%b',不要使用 echo -e。可点击的 OSC 8 链接还需要终端支持,tmux 或 SSH 也可能移除这些序列,因此在远程服务器上使用普通颜色更可靠。
该行右侧的内容被截断。 系统通知和详细模式的令牌计数器会从右侧共用该行,终端较窄时就会发生重叠。请缩短输出。如需准确统计使用量,而不是查看状态栏中的数字,请参阅 Claude Code 如何计算令牌。
FAQ
Claude Code 的状态栏设置在哪里?
设置位于 settings.json 中,使用 statusLine 块,其中 type 设置为 "command",command 设置为脚本路径或 shell 命令。用户设置位于 ~/.claude/settings.json,对该计算机上的所有项目生效。项目设置位于仓库中的 .claude/settings.json,并且对该目录具有更高优先级。设置会自动重新加载,但更改要到下一次更新触发时才会显示,例如发送下一条消息时。
为什么我的 Claude Code 状态栏为空?
几乎所有情况都可以归结为4个原因。脚本没有执行权限,因此 shell 返回 Permission denied,没有内容写入 stdout。工作区信任对话框从未被接受,并且 claude --debug 记录了 Status line command skipped: workspace trust not accepted。disableAllHooks 为 true,因此在同一检查条件下禁用状态栏。或者脚本以非零状态退出,导致该行为空。先手动测试:echo '{}' | ~/.claude/statusline.sh 必须输出内容。
状态栏 JSON 是否包含 git 分支?
不包含。JSON 提供会话状态,例如模型、工作区目录、上下文窗口编号和成本。其中没有任何 git 信息。状态栏中的分支来自您自己的脚本调用 git branch --show-current。使用 git -C "$DIR" 从 JSON 传入目录,这样分支始终与状态栏显示的目录一致。
状态栏会消耗 token 或拖慢会话吗?
不会消耗 token,因为脚本在本地运行,其输出不会发送给模型。运行速度由您负责。该命令会在每条 assistant 消息后运行,并使用 300 ms 防抖;如果新的更新到达,Claude Code 会取消正在运行的任务。因此,执行时间达到1秒的脚本会显示过时内容。避免在大型仓库中使用 git status,并将耗时操作的结果缓存到以 session_id 为键的文件中。
如何在每台服务器上显示不同的状态栏?
保留一个脚本,并让它读取计算机信息。上面的脚本使用 hostname -s 作为回退值输出 $HOSTNAME,因此将同一个文件复制到每台服务器后,每台服务器都会显示正确的标签;校验和颜色机制还会为每个主机名分配独立颜色。如果某台服务器需要不同的布局,请在该服务器上使用的仓库项目设置中放置 statusLine 块,因为项目设置对该目录的优先级高于用户设置。