sdkman

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

SDKMAN Version Switching

SDKMAN版本切换

SDKMAN manages parallel installs of JDKs and related tools under
$SDKMAN_DIR/candidates/<tool>/<version>/
(default
$SDKMAN_DIR
is
~/.sdkman
). This skill explains how to switch versions correctly from Bash tool calls — the naive approach silently does nothing.
Throughout this document
$SDKMAN_DIR
means
${SDKMAN_DIR:-$HOME/.sdkman}
. Users may relocate the install via that env var; never hard-code
~/.sdkman
.
SDKMAN 用于在
$SDKMAN_DIR/candidates/<tool>/<version>/
路径下管理JDK及相关工具的并行安装(默认
$SDKMAN_DIR
~/.sdkman
)。本技能将说明如何通过Bash工具调用正确切换版本——简单粗暴的方法可能会毫无效果。
本文档中所有
$SDKMAN_DIR
均指代
${SDKMAN_DIR:-$HOME/.sdkman}
。用户可通过该环境变量修改安装路径;请勿硬编码
~/.sdkman

Default Policy

默认策略

Unless the user explicitly asks otherwise:
  • Ephemeral, not persistent. Use Pattern A (preferred) or B. They affect only the current Bash invocation. Never run
    sdk default
    (Pattern C) just because a user said "switch to Java 17" — that changes every future shell the user opens.
  • Install with confirmation only. If a version isn't installed, stop and ask — do not auto-run
    sdk install
    . JDK downloads are hundreds of MB, persistent, and vendor-specific.
  • Follow
    .sdkmanrc
    without asking.
    A
    .sdkmanrc
    is the repo author's explicit intent for anyone entering the repo. Honor it via Pattern D; only pause to ask if the declared version is missing.
除非用户明确要求其他操作:
  • 临时生效,而非持久化。使用模式A(优先推荐)或模式B。它们仅对当前Bash会话生效。绝不要仅因为用户说“切换到Java 17”就执行
    sdk default
    (模式C)——这会改变用户未来打开的所有Shell环境。
  • 仅在确认后安装。若目标版本未安装,请暂停并询问用户——不要自动执行
    sdk install
    。JDK下载文件可达数百MB,且是持久化存储的,同时不同厂商的版本存在差异。
  • 无需询问,遵循
    .sdkmanrc
    配置
    .sdkmanrc
    是仓库作者为所有进入该仓库的用户明确指定的环境要求。通过模式D遵循该配置;仅当声明的版本缺失时,才暂停询问用户。

Precondition

前置条件

Before assuming this skill applies, confirm SDKMAN actually exists:
bash
SDKMAN_DIR="${SDKMAN_DIR:-$HOME/.sdkman}"
[ -s "$SDKMAN_DIR/bin/sdkman-init.sh" ] && echo "sdkman ok" || echo "no sdkman"
If absent, say so and stop — don't invent paths. The machine may use system packages (
apt
,
brew
),
asdf
,
mise
,
jenv
, or a manual
JAVA_HOME
instead; ask the user which. SDKMAN itself runs on macOS, Linux, WSL, and POSIX shells on Windows (Git Bash, Cygwin, MSYS) — not native
cmd
/PowerShell.
在使用本技能前,请先确认SDKMAN已存在:
bash
SDKMAN_DIR="${SDKMAN_DIR:-$HOME/.sdkman}"
[ -s "$SDKMAN_DIR/bin/sdkman-init.sh" ] && echo "sdkman ok" || echo "no sdkman"
若SDKMAN不存在,请告知用户并停止操作——不要自行编造路径。该机器可能使用系统包管理工具(
apt
brew
)、
asdf
mise
jenv
或手动配置
JAVA_HOME
;请询问用户使用的是哪种方式。SDKMAN可在macOS、Linux、WSL以及Windows上的POSIX Shell(Git Bash、Cygwin、MSYS)中运行——不支持原生
cmd
/PowerShell。

The One Thing That Trips Agents Up

容易让Agent出错的关键点

sdk
is a shell function exported by
$SDKMAN_DIR/bin/sdkman-init.sh
, not a binary on
PATH
. Two consequences:
  1. Every Bash tool call starts a fresh shell. Running
    sdk use java <id>
    in one call does not affect the next — the function isn't defined there unless you re-source it.
  2. sdk use
    is shell-local.
    Even interactively it only changes the current shell; nothing persists across shells.
So: do not run
sdk use
and expect the next tool call to inherit it. Pick a pattern below.
sdk
是由
$SDKMAN_DIR/bin/sdkman-init.sh
导出的Shell函数,而非
PATH
中的可执行二进制文件。这会导致两个结果:
  1. 每次Bash工具调用都会启动一个全新的Shell。在一次调用中执行
    sdk use java <id>
    不会影响下一次调用——除非重新加载该函数,否则新Shell中不会定义它。
  2. sdk use
    仅对当前Shell生效
    。即使在交互式Shell中执行,它也只会改变当前Shell的环境;不会在不同Shell之间持久化。
因此:不要执行
sdk use
后就期望下一次工具调用能继承该环境。请选择以下模式之一。

Loading
sdk
Idempotently

幂等加载
sdk
函数

Some agent runtimes pre-define
sdk
in each shell (Claude Code replays a shell snapshot that includes
.zshrc
/
.bashrc
-sourced SDKMAN init; login shells behave similarly). Plain non-interactive bash does not. The portable idiom that works in both cases — check first, source only if missing:
bash
type sdk >/dev/null 2>&1 || source "${SDKMAN_DIR:-$HOME/.sdkman}/bin/sdkman-init.sh"
Re-sourcing when already loaded is safe but wasteful. Patterns A / C / D below use this check-first form.
部分Agent运行时会在每个Shell中预定义
sdk
函数(Claude Code会重放包含
.zshrc
/
.bashrc
加载SDKMAN初始化脚本的Shell快照;登录Shell的行为类似)。普通非交互式Bash则不会。以下是可在两种场景下通用的写法——先检查是否已加载,仅在缺失时才加载:
bash
type sdk >/dev/null 2>&1 || source "${SDKMAN_DIR:-$HOME/.sdkman}/bin/sdkman-init.sh"
重复加载已存在的函数是安全的,但会造成资源浪费。以下模式A/C/D均使用这种“先检查再加载”的写法。

Pattern A — One-Shot Command (primary)

模式A — 一次性命令(首选)

The default choice for running something under a specific version.
sdk use
validates the version exists, gives clear errors when it doesn't, and sets the full SDKMAN-managed environment (not just
JAVA_HOME
). Everything stays in a single Bash invocation so shell state persists across the statements:
bash
type sdk >/dev/null 2>&1 || source "${SDKMAN_DIR:-$HOME/.sdkman}/bin/sdkman-init.sh"
sdk use java <jdk-id> && mvn test
Replace
<jdk-id>
with a value that is actually installed (see Discovering What's Installed). Self-contained, no global state left behind.
这是在特定版本环境下运行任务的默认选择。
sdk use
会验证版本是否存在,当版本不存在时给出清晰的错误提示,并且会设置完整的SDKMAN管理环境(不仅仅是
JAVA_HOME
)。所有操作都在同一个Bash会话中完成,因此Shell状态会在语句间保持:
bash
type sdk >/dev/null 2>&1 || source "${SDKMAN_DIR:-$HOME/.sdkman}/bin/sdkman-init.sh"
sdk use java <jdk-id> && mvn test
<jdk-id>
替换为实际已安装的版本值(参见“查看已安装版本”部分)。这种方式独立完整,不会留下全局状态。

Pattern B — Set JAVA_HOME Directly (fallback)

模式B — 直接设置JAVA_HOME(备用)

Every installed JDK lives at
$SDKMAN_DIR/candidates/java/<jdk-id>/
. Use this fallback when a tool reads
JAVA_HOME
in a detached way (long-running background daemon, a subprocess Pattern A's shell won't reach) or when the environment genuinely cannot load the
sdk
function:
bash
export JAVA_HOME="${SDKMAN_DIR:-$HOME/.sdkman}/candidates/java/<jdk-id>"
export PATH="$JAVA_HOME/bin:$PATH"
java -version
Skips
sdk
's validation — if you typo the id, this silently points at a nonexistent directory. Prefer A whenever possible.
每个已安装的JDK都位于
$SDKMAN_DIR/candidates/java/<jdk-id>/
路径下。当工具以独立方式读取
JAVA_HOME
(如长期运行的后台守护进程,Pattern A的Shell无法影响到的子进程),或者环境确实无法加载
sdk
函数时,可使用这种备用方式:
bash
export JAVA_HOME="${SDKMAN_DIR:-$HOME/.sdkman}/candidates/java/<jdk-id>"
export PATH="$JAVA_HOME/bin:$PATH"
java -version
这种方式跳过了
sdk
的验证——如果输入错误的id,会静默指向不存在的目录。因此只要可能,优先选择模式A。

Pattern C — Change the User's Default (explicit request only)

模式C — 修改用户的默认版本(仅在明确请求时使用)

sdk default java <jdk-id>
rewrites
$SDKMAN_DIR/candidates/java/current
and affects every future shell the user opens — shared state beyond this task. Do not use this pattern just because a user said "switch to Java 17". Only use it when the user explicitly asks for a permanent change (e.g., "set Java 21 as my default", "change the system default").
bash
type sdk >/dev/null 2>&1 || source "${SDKMAN_DIR:-$HOME/.sdkman}/bin/sdkman-init.sh"
sdk default java <jdk-id>
sdk default java <jdk-id>
会重写
$SDKMAN_DIR/candidates/java/current
,并影响用户未来打开的所有Shell——这会产生超出当前任务范围的共享状态。不要仅因为用户说“切换到Java 17”就使用该模式。仅当用户明确要求永久更改时(例如“将Java 21设置为我的默认版本”、“修改系统默认版本”)才使用它。
bash
type sdk >/dev/null 2>&1 || source "${SDKMAN_DIR:-$HOME/.sdkman}/bin/sdkman-init.sh"
sdk default java <jdk-id>

Pattern D — Project-Scoped with
.sdkmanrc
(auto-follow)

模式D — 基于
.sdkmanrc
的项目级配置(自动遵循)

If the repo has a
.sdkmanrc
, follow it without asking — this is the repo author's explicit instruction to anyone entering the project:
bash
type sdk >/dev/null 2>&1 || source "${SDKMAN_DIR:-$HOME/.sdkman}/bin/sdkman-init.sh"
cd /path/to/project && sdk env && ./gradlew build
If
sdk env
reports a version in
.sdkmanrc
is not installed, stop and ask the user whether to install it (see When a Version Isn't Installed below). Do not run
sdk env install
unprompted.
如果仓库中存在
.sdkmanrc
,无需询问即可遵循该配置——这是仓库作者为所有进入该项目的用户明确指定的环境要求:
bash
type sdk >/dev/null 2>&1 || source "${SDKMAN_DIR:-$HOME/.sdkman}/bin/sdkman-init.sh"
cd /path/to/project && sdk env && ./gradlew build
如果
sdk env
报告
.sdkmanrc
中声明的版本未安装,请停止操作并询问用户是否安装该版本(参见下文“当目标版本未安装时”)。不要未经提示就执行
sdk env install

Discovering What's Installed

查看已安装版本

Version strings include a vendor suffix (
-tem
Temurin,
-zulu
Azul,
-amzn
Corretto,
-graal
/
-graalce
GraalVM,
-librca
Liberica,
-oracle
, …) — guessing produces
Stop! java <x> is not available.
Always check first:
bash
SDKMAN_DIR="${SDKMAN_DIR:-$HOME/.sdkman}"
ls "$SDKMAN_DIR/candidates/java/"               # installed <jdk-id> values
readlink "$SDKMAN_DIR/candidates/java/current"  # current default
版本字符串包含厂商后缀
-tem
代表Temurin,
-zulu
代表Azul,
-amzn
代表Corretto,
-graal
/
-graalce
代表GraalVM,
-librca
代表Liberica,
-oracle
等)——猜测版本会导致
Stop! java <x> is not available.
错误。请务必先查看已安装版本:
bash
SDKMAN_DIR="${SDKMAN_DIR:-$HOME/.sdkman}"
ls "$SDKMAN_DIR/candidates/java/"               # 查看已安装的<jdk-id>值
readlink "$SDKMAN_DIR/candidates/java/current"  # 查看当前默认版本

When a Version Isn't Installed

当目标版本未安装时

If the target version isn't under
$SDKMAN_DIR/candidates/<tool>/
:
  1. List what IS installed (the
    ls
    above).
  2. If the user wants to see remote options:
    sdk list java
    (requires network).
  3. Stop and ask. Either pick a compatible installed version, or confirm
    sdk install java <jdk-id>
    with the user — specifying vendor, since the user may have a policy (
    -tem
    ,
    -amzn
    ,
    -graalce
    , …). Do not run
    sdk install
    autonomously.
如果目标版本不在
$SDKMAN_DIR/candidates/<tool>/
路径下:
  1. 列出已安装的版本(使用上述
    ls
    命令)。
  2. 如果用户想查看远程可用版本:执行
    sdk list java
    (需要网络)。
  3. 停止操作并询问用户。要么选择一个兼容的已安装版本,要么在用户确认后执行
    sdk install java <jdk-id>
    ——请指定厂商,因为用户可能有版本策略(
    -tem
    -amzn
    -graalce
    等)。不要自主执行
    sdk install

Quick Decision Guide

快速决策指南

SituationPattern
Run one build/test under a specific JDKA
Repo has
.sdkmanrc
D (auto-follow)
Tool reads
JAVA_HOME
in a detached process
B
User asked for a permanent default changeC
Target version isn't installedStop, list options, ask
场景模式
在特定JDK环境下运行一次构建/测试A
仓库中存在
.sdkmanrc
D(自动遵循)
工具以独立进程方式读取
JAVA_HOME
B
用户要求永久修改默认版本C
目标版本未安装停止操作,列出可选版本,询问用户

Other Candidates

其他候选工具

The patterns above work for every SDKMAN candidate — swap
java
for the candidate name and use the matching env var when Pattern B is needed:
CandidateEnv var
java
JAVA_HOME
gradle
GRADLE_HOME
maven
MAVEN_HOME
(some tools also honor
M2_HOME
)
kotlin
KOTLIN_HOME
Other candidates (
ant
,
scala
,
groovy
,
springboot
, …) follow the same convention:
<CANDIDATE>_HOME
points at
$SDKMAN_DIR/candidates/<candidate>/<version>/
.
上述模式适用于所有SDKMAN候选工具——将
java
替换为候选工具名称,并在使用模式B时使用对应的环境变量:
候选工具环境变量
java
JAVA_HOME
gradle
GRADLE_HOME
maven
MAVEN_HOME
(部分工具也支持
M2_HOME
kotlin
KOTLIN_HOME
其他候选工具(
ant
scala
groovy
springboot
等)遵循相同的约定:
<CANDIDATE>_HOME
指向
$SDKMAN_DIR/candidates/<candidate>/<version>/
路径。