sdkman
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSDKMAN Version Switching
SDKMAN版本切换
SDKMAN manages parallel installs of JDKs and related tools under (default is ). This skill explains how to switch versions correctly from Bash tool calls — the naive approach silently does nothing.
$SDKMAN_DIR/candidates/<tool>/<version>/$SDKMAN_DIR~/.sdkmanThroughout this documentmeans$SDKMAN_DIR. Users may relocate the install via that env var; never hard-code${SDKMAN_DIR:-$HOME/.sdkman}.~/.sdkman
SDKMAN 用于在路径下管理JDK及相关工具的并行安装(默认为)。本技能将说明如何通过Bash工具调用正确切换版本——简单粗暴的方法可能会毫无效果。
$SDKMAN_DIR/candidates/<tool>/<version>/$SDKMAN_DIR~/.sdkman本文档中所有均指代$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 (Pattern C) just because a user said "switch to Java 17" — that changes every future shell the user opens.
sdk default - Install with confirmation only. If a version isn't installed, stop and ask — do not auto-run . JDK downloads are hundreds of MB, persistent, and vendor-specific.
sdk install - Follow without asking. A
.sdkmanrcis 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..sdkmanrc
除非用户明确要求其他操作:
- 临时生效,而非持久化。使用模式A(优先推荐)或模式B。它们仅对当前Bash会话生效。绝不要仅因为用户说“切换到Java 17”就执行(模式C)——这会改变用户未来打开的所有Shell环境。
sdk default - 仅在确认后安装。若目标版本未安装,请暂停并询问用户——不要自动执行。JDK下载文件可达数百MB,且是持久化存储的,同时不同厂商的版本存在差异。
sdk install - 无需询问,遵循配置。
.sdkmanrc是仓库作者为所有进入该仓库的用户明确指定的环境要求。通过模式D遵循该配置;仅当声明的版本缺失时,才暂停询问用户。.sdkmanrc
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 (, ), , , , or a manual instead; ask the user which. SDKMAN itself runs on macOS, Linux, WSL, and POSIX shells on Windows (Git Bash, Cygwin, MSYS) — not native /PowerShell.
aptbrewasdfmisejenvJAVA_HOMEcmd在使用本技能前,请先确认SDKMAN已存在:
bash
SDKMAN_DIR="${SDKMAN_DIR:-$HOME/.sdkman}"
[ -s "$SDKMAN_DIR/bin/sdkman-init.sh" ] && echo "sdkman ok" || echo "no sdkman"若SDKMAN不存在,请告知用户并停止操作——不要自行编造路径。该机器可能使用系统包管理工具(、)、、、或手动配置;请询问用户使用的是哪种方式。SDKMAN可在macOS、Linux、WSL以及Windows上的POSIX Shell(Git Bash、Cygwin、MSYS)中运行——不支持原生/PowerShell。
aptbrewasdfmisejenvJAVA_HOMEcmdThe One Thing That Trips Agents Up
容易让Agent出错的关键点
sdk$SDKMAN_DIR/bin/sdkman-init.shPATH- Every Bash tool call starts a fresh shell. Running in one call does not affect the next — the function isn't defined there unless you re-source it.
sdk use java <id> - is shell-local. Even interactively it only changes the current shell; nothing persists across shells.
sdk use
So: do not run and expect the next tool call to inherit it. Pick a pattern below.
sdk usesdk$SDKMAN_DIR/bin/sdkman-init.shPATH- 每次Bash工具调用都会启动一个全新的Shell。在一次调用中执行不会影响下一次调用——除非重新加载该函数,否则新Shell中不会定义它。
sdk use java <id> - 仅对当前Shell生效。即使在交互式Shell中执行,它也只会改变当前Shell的环境;不会在不同Shell之间持久化。
sdk use
因此:不要执行后就期望下一次工具调用能继承该环境。请选择以下模式之一。
sdk useLoading sdk
Idempotently
sdk幂等加载sdk
函数
sdkSome agent runtimes pre-define in each shell (Claude Code replays a shell snapshot that includes /-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:
sdk.zshrc.bashrcbash
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中预定义函数(Claude Code会重放包含/加载SDKMAN初始化脚本的Shell快照;登录Shell的行为类似)。普通非交互式Bash则不会。以下是可在两种场景下通用的写法——先检查是否已加载,仅在缺失时才加载:
sdk.zshrc.bashrcbash
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. validates the version exists, gives clear errors when it doesn't, and sets the full SDKMAN-managed environment (not just ). Everything stays in a single Bash invocation so shell state persists across the statements:
sdk useJAVA_HOMEbash
type sdk >/dev/null 2>&1 || source "${SDKMAN_DIR:-$HOME/.sdkman}/bin/sdkman-init.sh"
sdk use java <jdk-id> && mvn testReplace with a value that is actually installed (see Discovering What's Installed). Self-contained, no global state left behind.
<jdk-id>这是在特定版本环境下运行任务的默认选择。会验证版本是否存在,当版本不存在时给出清晰的错误提示,并且会设置完整的SDKMAN管理环境(不仅仅是)。所有操作都在同一个Bash会话中完成,因此Shell状态会在语句间保持:
sdk useJAVA_HOMEbash
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 . Use this fallback when a tool reads 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 function:
$SDKMAN_DIR/candidates/java/<jdk-id>/JAVA_HOMEsdkbash
export JAVA_HOME="${SDKMAN_DIR:-$HOME/.sdkman}/candidates/java/<jdk-id>"
export PATH="$JAVA_HOME/bin:$PATH"
java -versionSkips 's validation — if you typo the id, this silently points at a nonexistent directory. Prefer A whenever possible.
sdk每个已安装的JDK都位于路径下。当工具以独立方式读取(如长期运行的后台守护进程,Pattern A的Shell无法影响到的子进程),或者环境确实无法加载函数时,可使用这种备用方式:
$SDKMAN_DIR/candidates/java/<jdk-id>/JAVA_HOMEsdkbash
export JAVA_HOME="${SDKMAN_DIR:-$HOME/.sdkman}/candidates/java/<jdk-id>"
export PATH="$JAVA_HOME/bin:$PATH"
java -version这种方式跳过了的验证——如果输入错误的id,会静默指向不存在的目录。因此只要可能,优先选择模式A。
sdkPattern C — Change the User's Default (explicit request only)
模式C — 修改用户的默认版本(仅在明确请求时使用)
sdk default java <jdk-id>$SDKMAN_DIR/candidates/java/currentbash
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/currentbash
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)
.sdkmanrc模式D — 基于.sdkmanrc
的项目级配置(自动遵循)
.sdkmanrcIf the repo has a , follow it without asking — this is the repo author's explicit instruction to anyone entering the project:
.sdkmanrcbash
type sdk >/dev/null 2>&1 || source "${SDKMAN_DIR:-$HOME/.sdkman}/bin/sdkman-init.sh"
cd /path/to/project && sdk env && ./gradlew buildIf reports a version in is not installed, stop and ask the user whether to install it (see When a Version Isn't Installed below). Do not run unprompted.
sdk env.sdkmanrcsdk env install如果仓库中存在,无需询问即可遵循该配置——这是仓库作者为所有进入该项目的用户明确指定的环境要求:
.sdkmanrcbash
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.sdkmanrcsdk env installDiscovering What's Installed
查看已安装版本
Version strings include a vendor suffix ( Temurin, Azul, Corretto, / GraalVM, Liberica, , …) — guessing produces Always check first:
-tem-zulu-amzn-graal-graalce-librca-oracleStop! java <x> is not available.bash
SDKMAN_DIR="${SDKMAN_DIR:-$HOME/.sdkman}"
ls "$SDKMAN_DIR/candidates/java/" # installed <jdk-id> values
readlink "$SDKMAN_DIR/candidates/java/current" # current default版本字符串包含厂商后缀(代表Temurin,代表Azul,代表Corretto,/代表GraalVM,代表Liberica,等)——猜测版本会导致错误。请务必先查看已安装版本:
-tem-zulu-amzn-graal-graalce-librca-oracleStop! 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>/- List what IS installed (the above).
ls - If the user wants to see remote options: (requires network).
sdk list java - Stop and ask. Either pick a compatible installed version, or confirm with the user — specifying vendor, since the user may have a policy (
sdk install java <jdk-id>,-tem,-amzn, …). Do not run-graalceautonomously.sdk install
如果目标版本不在路径下:
$SDKMAN_DIR/candidates/<tool>/- 列出已安装的版本(使用上述命令)。
ls - 如果用户想查看远程可用版本:执行(需要网络)。
sdk list java - 停止操作并询问用户。要么选择一个兼容的已安装版本,要么在用户确认后执行——请指定厂商,因为用户可能有版本策略(
sdk install java <jdk-id>、-tem、-amzn等)。不要自主执行-graalce。sdk install
Quick Decision Guide
快速决策指南
| Situation | Pattern |
|---|---|
| Run one build/test under a specific JDK | A |
Repo has | D (auto-follow) |
Tool reads | B |
| User asked for a permanent default change | C |
| Target version isn't installed | Stop, list options, ask |
| 场景 | 模式 |
|---|---|
| 在特定JDK环境下运行一次构建/测试 | A |
仓库中存在 | D(自动遵循) |
工具以独立进程方式读取 | B |
| 用户要求永久修改默认版本 | C |
| 目标版本未安装 | 停止操作,列出可选版本,询问用户 |
Other Candidates
其他候选工具
The patterns above work for every SDKMAN candidate — swap for the candidate name and use the matching env var when Pattern B is needed:
java| Candidate | Env var |
|---|---|
| |
| |
| |
| |
Other candidates (, , , , …) follow the same convention: points at .
antscalagroovyspringboot<CANDIDATE>_HOME$SDKMAN_DIR/candidates/<candidate>/<version>/上述模式适用于所有SDKMAN候选工具——将替换为候选工具名称,并在使用模式B时使用对应的环境变量:
java| 候选工具 | 环境变量 |
|---|---|
| |
| |
| |
| |
其他候选工具(、、、等)遵循相同的约定:指向路径。
antscalagroovyspringboot<CANDIDATE>_HOME$SDKMAN_DIR/candidates/<candidate>/<version>/