上周帮一位同行收拾新配的开发机JDK 21 装完之后他随口提了一句“下个月那个老系统还得用 8 跑要不再装一个”。这句话让我想起自己几年前在 Windows 上同时维护三四个 JDK 版本的日子改环境变量、重启终端、重启 IDE一天能被这种事情磨掉半小时。后来我把 SDKMAN 挪进 WSL这套流程就基本定型了几年下来没怎么再折腾过。SDKMAN 是 JVM 生态里最省心的版本管理器之一Java、Kotlin、Groovy、Scala、Maven、Gradle 这些组件都能用它一条命令装好、切换、卸载还能把版本锁定写进仓库。但它有个绕不开的前提它是一套纯 Bash 脚本官方覆盖的平台是 Linux 和 macOSWindows 上没有原生安装包也没有计划做。所以真正的问题不是“怎么装 SDKMAN”而是“在 Windows 上给它准备一个什么样的 Unix 环境”。这篇内容就把这件事从头到尾走一遍。WSL 和 Git Bash 两条路线都写包括每条路能走多远、在哪一步容易卡住、卡住之后怎么判断是环境问题还是 SDKMAN 自身的问题。适合手上有多个 JDK 版本要来回切、日常用 Windows 做 JVM 开发、又不想被环境变量反复折腾的同行哪怕你只是想在干净机器上试一次 SDKMAN也可以按顺序照着走。1. 为什么 Windows 上装 SDKMAN 要绕一层 Unix 环境1.1 SDKMAN 本质是 Bash 脚本加一套目录约定很多人第一次接触 SDKMAN会以为它是个编译好的可执行程序装完就能在任意终端里敲sdk。实际情况是sdk是一个Bash 函数不是一个二进制文件。它被定义在~/.sdkman/bin/sdkman-init.sh里安装脚本做的最重要的一件事就是往你的 shell 配置文件里追加一行source $HOME/.sdkman/bin/sdkman-init.sh。这意味着两件事第一sdk只在被 source 过的 shell 里存在第二任何不认 Bash 语法的环境天生就跑不动它。再看它的目录约定。所有候选组件都装在~/.sdkman/candidates/候选名/版本号/下面比如 Java 就是~/.sdkman/candidates/java/21.0.2-tem/。当前生效的版本并不是靠环境变量硬指过去而是在同级目录维护一个叫current的符号链接指向具体版本目录PATH和JAVA_HOME都指向current。切换版本的实质动作就是把这个软链接重新指一下。理解了这两点Windows 上的困难就清楚了。sdk依赖 Bash 的函数、数组、[[ ]]判断安装包解压依赖unzip和tar版本切换依赖创建符号链接。这三样在原生 Windows 的命令行里一个都不完整。所以 WSL 也好、Git Bash 也罢本质上都是在补这层缺失的 Unix 语义区别只在于补得够不够彻底。1.2 WSL 与 Git Bash 的定位差异WSL 提供的是一个真实的 Linux 内核环境文件系统是 ext4uname返回的是Linuxln -s是真软链接unzip、tar、curl一应俱全。对 SDKMAN 来说它跟跑在一台普通 Linux 机器上没有区别官方文档里的所有命令都能一字不改地执行。代价是需要开启虚拟化相关功能占用额外内存首次安装要下载一个发行版镜像。Git Bash 则是另一种思路。它基于 MSYS2把一套 GNU 工具链编译成 Windows 可执行文件再提供一个 POSIX 兼容层。它轻量装完就能用不用重启系统~/.sdkman目录直接落在 Windows 的用户目录下。但这层兼容层是有缝隙的uname返回的是MINGW64_NT-10.0-xxxxx这样的字符串符号链接行为受 Windows 权限策略影响路径转换时不时给你惊喜。SDKMAN 的很多判断逻辑正是靠uname和软链接吃饭的。所以两条路线的定位差异很明显WSL 是“在 Windows 里跑一个正常 Linux”SDKMAN 属于顺带就通了Git Bash 是“在 Windows 里模拟 Linux”SDKMAN 大部分能跑但边界需要自己摸清楚。这个区别决定了后面所有的坑都集中在 Git Bash 那条路上。1.3 两条路线的选型对照表选哪条路不看喜好看你每天实际要做什么。我把两条路线在几个关键维度上的表现整理了一下这里写的是我这台机器上反复验证过的结果你在自己的环境里可能会有细微出入但大方向一致。维度WSL 路线Git Bash 路线安装难度需要开启虚拟化、装发行版首次约十几分钟装完 Git for Windows 即可几分钟SDKMAN 兼容度完全兼容官方文档命令可直接用大部分命令可用下载和解压阶段有概率卡壳符号链接支持原生支持current切换立即生效受权限策略影响可能退化成复制或失败文件系统性能ext4 内操作快跨到/mnt/c显著变慢直接在 NTFS 上小文件多时解压偏慢与 Windows 工具的互操作需要\\wsl.localhost\路径桥接路径天然共享IDE 直接可见适合的场景服务端构建、多版本并行、需要贴近生产环境只想快速试装、轻量工具链、临时验证如果你的日常工作包含在本地跑服务、用容器、或者需要跟生产环境的 Linux 保持一致那就别犹豫直接上 WSL。如果你只是想在 Windows 上装个 Maven、偶尔切个 Kotlin 版本Git Bash 够用省下来的配置时间也是实打实的。我个人的做法是两条都装WSL 里那套是主力Git Bash 里留一份用于临时验证和小工具但两边都设了同一个SDKMAN_DIR之外的独立目录避免互相污染。2. 动手前的环境体检与依赖清单2.1 系统版本与虚拟化前提走 WSL 路线之前先确认系统版本。wsl --install这条一体化命令在较新的 Windows 10 和 Windows 11 上才可用老版本需要手动开启“适用于 Linux 的 Windows 子系统”和“虚拟机平台”两个可选功能然后重启。判断方法很简单打开 PowerShell 敲wsl --status能正常输出默认发行版和版本号就说明组件齐了如果提示命令不存在那就是功能没开。虚拟化这一层经常被忽略。WSL2 跑在轻量虚拟机里BIOS 里的 CPU 虚拟化必须打开。有些人机器买回来虚拟化是关的装的时候一路报错查了半天以为是系统问题其实是固件设置。任务管理器里“性能”页的 CPU 信息会直接写着“虚拟化已启用/已禁用”一眼就能看出来比翻 BIOS 省事。至于 Git Bash 路线前置条件只有一个装上 Git for Windows版本别太老。它的安装包自带 Bash、curl、tar 和一堆 GNU 工具不需要额外配置。安装时有个选项叫“Use Git from Git Bash only”和“Git from the command line and also from 3rd-party software”选后者会把 Git 相关目录写进系统PATH后面在 IDE 里调用 Git 更方便我一般选这个。2.2 依赖工具逐项确认SDKMAN 的安装脚本对环境的检查其实很宽松但只要缺一个关键工具报错信息往往指向别处排查起来费劲。所以装之前花两分钟把这些确认一遍能省掉后面半小时的困惑。curl或wget下载安装脚本和组件包用。WSL 里默认都有Git Bash 里curl自带wget一般没有。unzip解压候选组件的压缩包。WSL 里可能需要apt install unzip补一下很多精简镜像里没有预装。tar和gzip部分组件是.tar.gz格式靠它解。WSL 默认有Git Bash 也有。zip个别组件在打包阶段需要一般用不到缺了不影响使用。sed、grep、awk脚本内部大量使用WSL 和 Git Bash 都自带。在 WSL 里补依赖就是一行命令的事sudo apt update sudo apt install -y curl zip unzipDebian 系之外的发行版把apt换成对应的包管理器就行。Git Bash 里如果发现unzip缺失最简单的办法是去找一个独立的unzip.exe放进C:\Program Files\Git\usr\bin\下面或者干脆先别管等真正下载组件报错时再补因为不是每个候选组件都用 zip 打包。2.3 目录规划别把 SDKMAN_DIR 丢在 /mnt/c这一条是我踩过最贵的坑值得单独拿出来说。WSL 有一个特性访问 Windows 盘符是通过/mnt/c/这类挂载点实现的底层走的是跨系统文件协议大量小文件读写性能极差。而 SDKMAN 装一个 JDK 会产生几千个文件装在/mnt/c下面解压时间能从几十秒变成好几分钟后续sdk use切换也会明显卡顿。更麻烦的是权限映射有时候会出现解压出来的文件权限错乱。所以 WSL 里的 SDKMAN 一定要装在 Linux 自己的文件系统里也就是~/.sdkman对应实际路径大概在/home/你的用户名/.sdkman。如果你之前改过SDKMAN_DIR指到 Windows 盘改回来。Git Bash 那边情况反过来它的$HOME默认就是C:\Users\你的用户名所有东西天然落在 NTFS 上没法选。这也是我前面说“解压偏慢”的原因属于方案本身的固有成本接受就好。还有一个隐形的坑WSL 的~/.sdkman和 Git Bash 的~/.sdkman是两个完全不同的目录虽然都叫这个名字。如果你两边都装别指望它们共享版本也别把其中一个的bin加到另一个的PATH里否则会出现“命令找到了但版本对不上”的诡异现象。3. 路线一在 WSL 里把 SDKMAN 跑起来3.1 发行版安装与首次初始化确定了走 WSL安装本身没什么技术含量但有几个设置项建议一次定好省得后面返工。先列一下当前环境确认默认版本是 2wsl --list --verbose wsl --set-default-version 2第一条会列出已安装的发行版和它们的 WSL 版本。如果某个发行版显示是 1用wsl --set-version 发行版名 2单独升级WSL1 的文件系统语义和 WSL2 差别不小SDKMAN 在上面虽然也能跑但遇到问题时排查思路完全不同不建议混着用。首次进入发行版时会被要求设置用户名和密码。这里有个细节用户名不要用大写字母也不要用中文。SDKMAN 的路径里会带用户名某些组件解压时对路径字符集敏感用纯小写英文能避开一整类莫名其妙的问题比如sdk命令执行到一半报“找不到文件”实际是路径里的字符在转换时被吃掉了。进入系统后先更新一次软件源这一步在刚装好的发行版上几乎必须做否则apt install很可能拉不到包sudo apt update sudo apt upgrade -y3.2 安装 SDKMAN 的三条命令与验证SDKMAN 的安装方式非常朴素把官网的脚本拉下来直接交给 bash 执行curl -s https://get.sdkman.io | bash如果curl不可用换成wget -qO- https://get.sdkman.io | bash效果一样。脚本会做几件事检查依赖、把运行文件下载到~/.sdkman、往 shell 配置文件里追加初始化语句、最后打印一句提示让你重启终端或者手动 source。万一你不想把远程脚本直接管道给 bash这个谨慎是对的可以先下载再看curl -s https://get.sdkman.io -o /tmp/sdkman-install.sh less /tmp/sdkman-install.sh bash /tmp/sdkman-install.sh安装完成后当前 shell 里sdk还不存在需要手动加载一次source $HOME/.sdkman/bin/sdkman-init.sh sdk version第二条命令是验证的关键。正常输出会是 SDKMAN 加一个版本号比如SDKMAN 5.18.2这种格式。如果提示sdk: command not found说明初始化脚本没被 source 成功或者你所在的 shell 不是 Bash。这种情况先看echo $SHELL如果是zsh要去~/.zshrc里确认有没有对应的 source 语句安装脚本通常会自动写但如果你是在安装之后才换的默认 shell就不会补写。3.3 让 sdk 在每个 shell 里都能用source只对当前会话生效关掉终端就没了。安装脚本一般会往~/.bashrc追加一行但实际使用中有几种情况会让它失效用sh启动而不是bash、用某些 IDE 内置终端、通过ssh或wsl命令直接执行非交互式命令。我的做法是两条腿走路。交互式使用靠~/.bashrc非交互式场景比如脚本里要调sdk则在脚本开头显式 source 一次#!/usr/bin/env bash set -e source $HOME/.sdkman/bin/sdkman-init.sh sdk use java 21.0.2-temset -e是顺手加的脚本里任何一步失败就立刻退出比默默跑完再回头查日志高效得多。另外提醒一句sdk是函数不能像二进制那样用绝对路径调用也不适合放进需要 root 权限的脚本里因为 SDKMAN 的设计就是按用户维度管理的。如果你用的是 zsh 或 fish处理方式类似只是配置文件换成~/.zshrc。fish 对 Bash 语法的兼容性有限SDKMAN 有第三方适配脚本但不如直接用 bash 稳妥我一般建议在 fish 里通过bash -c包一层或者干脆把默认 shell 保持为 bash。3.4 Java 版本管理与 JAVA_HOME 的落点装好之后第一件事通常是装 JDK。先看一眼有哪些版本可选sdk list java输出是一张长表格按发行商分组每行一个版本后面标注了是否已安装、是否有本地缓存。表格里版本号后面的后缀是发行商标识比如tem、zulu、graalce这些不同发行商的构建策略、更新节奏、支持的架构都不一样。选哪个没有标准答案我一般主力用tem遇到需要特定商业支持的场景再换。装一个具体版本sdk install java 21.0.2-tem安装过程会下载、解压、把目录放到~/.sdkman/candidates/java/21.0.2-tem最后问你要不要设为默认。第一次装直接回车确认就行。装完之后验证java -version echo $JAVA_HOMEJAVA_HOME应该指向~/.sdkman/candidates/java/current而current是一个指向具体版本的软链接。这个设计的好处是切换版本只需要改软链接所有引用JAVA_HOME的工具自动跟着变不用改任何配置文件。再装第二个版本感受一下切换sdk install java 17.0.10-tem sdk use java 17.0.10-tem java -versionuse只在当前 shell 生效新开一个终端会回到默认版本。想让某个版本长期生效用sdk default java 17.0.10-tem。这个区分很实用临时验证兼容性用use确定项目基线用default。查看当前状态用sdk current不带参数会列出所有已装组件的当前版本。3.5 Windows 侧工具怎么吃到这些 JDKWSL 里的 JDK 装在 Linux 文件系统里Windows 上的程序默认看不到。但有一个桥接路径可以用WSL 的根文件系统在 Windows 里通过\\wsl.localhost\发行版名\访问老一点的系统上是\\wsl$\发行版名\。所以那个 JDK 目录在 Windows 侧的完整路径大概是\\wsl.localhost\Debian\home\你的用户名\.sdkman\candidates\java\21.0.2-tem在大多数 IDE 的“添加 JDK”对话框里直接粘这个 UNC 路径是可以识别出来的。但我不太推荐日常这么用原因有两个一是跨文件系统访问有延迟IDE 建索引时会明显变慢二是 WSL 没启动的时候这个路径不可达IDE 会报 JDK 失效。更稳妥的分工是WSL 里那套 JDK 专门服务于命令行构建和脚本Windows 侧单独装一个 JDK 给 IDE 用两边版本号对齐即可。IDE 需要的只是编译和索引能力不需要和命令行共用同一份物理文件。这样各跑各的互不干扰出了问题也好定位。反过来在 WSL 里调用 Windows 程序是原生支持的比如explorer.exe .可以直接打开当前目录的资源管理器。这个特性在调试路径问题时很好用能立刻看出你的工程到底在哪一层。4. 路线二在 Git Bash 里跑 SDKMAN 的可行边界4.1 Git Bash 的 MSYS2 底子与它带来的差异Git Bash 不是一个独立的终端模拟器它是 MSYS2 环境加上一套 Git 工具的打包。MSYS2 提供 POSIX 兼容层把 Linux 风格的路径、信号、部分系统调用翻译成 Windows 的对应实现。大部分情况下这层翻译已经做得很好ls、grep、sed、管道、重定向都工作正常日常写脚本几乎感觉不到差异。但 SDKMAN 恰好踩在几个翻译不彻底的边界上。最典型的是平台识别。SDKMAN 内部要判断当前系统是 Linux 还是 macOS、是 x86 还是 ARM判断依据就是uname的输出。在 Git Bash 里敲一下uname -s uname -m第一条通常返回MINGW64_NT-10.0-22631这样的字符串第二条返回x86_64。SDKMAN 的匹配逻辑里没有MINGW这个分支于是平台判定落空表现在用户侧就是下载链接拼不出来报 404 或者提示版本不可用。这个不是配置问题是环境语义不匹配绕不过去。第二个边界是符号链接。前面说过切换版本的实质是重建current这个软链接。Windows 创建符号链接需要相应权限普通用户默认没有除非开了开发者模式或者用管理员身份运行。MSYS2 的处理策略是要么真的创建链接权限足够时要么直接复制文件权限不足且配置允许时。复制一份完整的 JDK 是什么概念几百兆空间加上几十秒等待而且current目录下会残留旧版本的文件切换后java -version还显示老版本非常迷惑。4.2 安装过程与前置补件尽管有这些限制安装本身通常还是能完成的。流程和 Linux 一样curl -s https://get.sdkman.io | bash source $HOME/.sdkman/bin/sdkman-init.sh sdk version$HOME在 Git Bash 里指向C:\Users\你的用户名所以 SDKMAN 会装在C:\Users\你的用户名\.sdkman。这个路径本身没问题全是英文前提是你的 Windows 用户名是英文不会触发字符集问题。安装脚本执行过程中可能会提示缺少unzip。Git for Windows 较新的版本里通常已经带了which unzip能看到就行看不到的话装个独立的unzip.exe扔进 Git 的usr\bin目录。这一步别省因为大部分 JDK 发行版提供的是 zip 格式没有解压工具会直接失败在解压阶段而报错信息往往只说“解压失败”不告诉你缺工具。另一个前置项是 shell 配置。Git Bash 启动时读~/.bash_profile如果没有这个文件再去读~/.bashrc。安装脚本一般往.bashrc写绝大多数情况下能生效但如果你自己创建过.bash_profile且里面没有 source.bashrc就会发现每次开新窗口sdk都不在。这种情况在.bash_profile里补一行就行[ -f ~/.bashrc ] source ~/.bashrc4.3 平台识别与符号链接两个硬伤前面提到的两个边界实际影响程度不一样得分开评估。平台识别的问题表现是下载阶段失败。你在 Git Bash 里执行sdk install java 21.0.2-tem命令开始跑、开始显示进度、然后卡在下载或者直接 404。这时候判断方法很直接开调试模式看它到底请求了哪个地址。编辑~/.sdkman/etc/config把这一行改成 true如果文件里没有就手动加sdkman_debug_modetrue保存之后重新执行安装命令屏幕上会打印出完整的请求 URL。看一眼 URL 里平台那一段是什么如果是空的或者明显不对就印证了平台识别的问题。这一层可以在本地手动补齐先用浏览器或下载工具把对应版本的压缩包拿下来再用本地安装的方式喂给 SDKMANsdk install java 21.0.2-tem /c/Users/你的用户名/Downloads/jdk-21.0.2.zip这种带本地路径的安装方式在多数版本里可用具体行为以sdk help install的输出为准。它解决的是“下载不了”的问题但解压和后续切换仍要过符号链接那一关。符号链接的问题表现更隐蔽安装看起来成功了但切换版本不生效。执行sdk default java 17.0.10-tem之后java -version还是老版本或者~/.sdkman/candidates/java/current变成了一整个目录而不是链接。这时候可以看看链接到底是什么ls -la ~/.sdkman/candidates/java/正常应该是current - 21.0.2-tem这样的箭头如果显示的是完整目录列表那就是被复制了。原因是 MSYS 的符号链接策略。可以通过环境变量要求它必须建真链接export MSYSwinsymlinks:nativestrict但注意这个设置会让创建链接失败时报错而不是退化复制而失败的前提是你没有链接权限。所以真正要解决还是得开 Windows 的开发者模式设置里搜“开发者设置”打开“开发人员模式”让普通用户具备创建符号链接的权限。开完之后删掉current目录重新执行一次sdk default链接就能正常建出来。我的实际体会是Git Bash 路线的成本不在安装在“安装完之后的每一次切换”。如果你只需要固定一个 JDK 版本、装完就不动了那这两个问题都可以绕过但如果你每天都要在版本之间跳那份消耗就不划算了。4.4 什么时候值得坚持 Git Bash说了这么多限制不是说这条路不行。它在几种场景下反而更合适。第一种是纯工具类组件。像 Maven、Gradle 这种本身是 Java 程序加大批 jar 包没有平台相关的二进制文件Git Bash 下装起来顺风顺水切换也不依赖符号链接的复杂性因为current一般能正常建。我用 Git Bash 里的 SDKMAN 主要就是为了管这两个。第二种是临时验证。比如想快速看看某个 Kotlin 版本编译出来的东西有什么变化不想为一个临时需求去开 WSL 装发行版Git Bash 五分钟能搞定。第三种是路径互通的刚需。有些构建脚本同时调用 Windows 工具和命令行工具路径来回转换放在 WSL 里反而要多写一堆/mnt/c的转换。这种场景下 Git Bash 的天然路径共享是优势。判断标准可以简化成一句话需要长期、频繁地切 Java 版本选 WSL只需要装一两个固定版本的 JVM 工具Git Bash 更省事。5. 装好之后日常命令与工程化用法5.1 查询、安装、切换的四条核心命令SDKMAN 的命令不多日常高频的就那么几个记住就能覆盖八成场景。命令作用使用频率sdk list 候选名查看可安装版本和已装状态高sdk install 候选名 版本安装指定版本中sdk use 候选名 版本当前会话切换高sdk default 候选名 版本设为默认持久生效中sdk current查看所有组件当前版本高sdk uninstall 候选名 版本卸载低sdk flush archives清理下载缓存释放空间低几个容易被忽略的细节。sdk list java的输出里已安装版本前面会有标记本地有缓存但没装的也会标出来看表格的时候留意一下能避免重复下载。sdk current不带参数列出全部带上候选名只显示那一个脚本里判断环境时用得上。uninstall之后目录会被删掉但如果这个版本正好是current指向的目标会留下一个断链。这时候执行一次sdk default切到别的版本断链会自动修复。这个行为我一开始不知道看到current标红还以为是装坏了。缓存目录~/.sdkman/archives会存着所有下载过的压缩包装十几个版本之后能占好几个 G。定期跑一次sdk flush archives清掉不影响已安装的版本只删安装包。5.2 用 .sdkmanrc 把版本锁进仓库多人协作的项目最怕版本不一致。A 用 17B 用 21本地都跑得好好的一合并就出现编译差异。SDKMAN 提供了一个很轻量的解法在项目根目录放一个.sdkmanrc文件把需要的版本写进去。生成这个文件不用手写进到项目目录执行sdk env init它会扫描当前 shell 里各组件正在用的版本生成一份清单内容大概是这样# Enable auto-env through the sdkman_auto_env config # Add keyvalue pairs of SDKs to use below java21.0.2-tem maven3.9.6把这个文件提交到仓库团队成员拉下来之后执行sdk env版本就自动切到一致的状态。想取消当前目录的绑定用sdk env clear。更进一步可以在~/.sdkman/etc/config里把sdkman_auto_env改成 true。开启之后每次cd进带.sdkmanrc的目录版本会自动切换离开时自动恢复。这个体验非常顺滑几乎不用再想着版本的事。需要提醒的是.sdkmanrc里的版本必须在本地装过否则sdk env会提示找不到。团队协作时最好在 README 里写一句“首次使用先执行sdk install拉齐版本”或者干脆在构建脚本的初始化阶段加个检查缺版本就自动装。这件事看起来小实际能省掉新人上手时的大量沟通。5.3 构建工具、IDE 与流水线的配合SDKMAN 管的不只是 JDK。Maven 和 Gradle 也能通过它安装sdk install maven 3.9.6 sdk install gradle 8.5装完之后mvn和gradle直接在PATH里和手动下载再配环境变量相比最大的好处是切换版本只改一个软链接不会出现“环境变量里配的是这个实际跑的是那个”的错位。IDE 那边核心原则是不要让 IDE 跟着 SDKMAN 的current走。IDE 一般在启动时读取一次 JDK 路径然后缓存下来你在命令行切换版本它感知不到结果就是命令行编译通过、IDE 里一片红。稳妥的做法是在 IDE 的 JDK 列表里把常用的几个版本都登记一次项目级别指定用哪个两边版本号对齐即可。流水线环境是 SDKMAN 最能发挥价值的地方。CI 脚本里加两行就能装出确定的版本source $HOME/.sdkman/bin/sdkman-init.sh sdk install java 21.0.2-tem sdk default java 21.0.2-tem比手动下载 JDK 压缩包、解压、配环境变量省事得多而且版本号写死在脚本里天然可复现。需要注意的是CI 环境里的 shell 可能是非交互式的~/.bashrc不会被读取所以必须显式 source这一点前面提过在流水线里尤其容易忘。如果网络状况不稳定SDKMAN 支持离线模式sdk offline enable sdk offline disable开启之后所有操作只使用本地已安装的版本不发起远程请求。这个功能在飞机上、会议演示现场救过我好几次提前把需要的版本装好现场断网也能顺畅切换。6. 常见报错与排查实录6.1 安装阶段脚本跑不完的几种情况安装脚本卡住或者中途退出原因基本都是环境缺件。我整理了几个遇到频率最高的。第一种执行curl -s https://get.sdkman.io | bash之后没有任何输出直接回到提示符。这种情况多半是curl静默失败把-s去掉再跑一次错误信息就出来了。常见的是证书问题或者连接被中间设备拦截。第二种脚本跑到一半提示unzip: command not found。前面说过补装unzip就行。WSL 里sudo apt install unzipGit Bash 里找一个unzip.exe放进usr\bin。第三种安装完成但sdk命令找不到。先确认有没有 source 初始化脚本再确认当前 shell 是不是 Bash。echo $0能看出当前是什么 shell如果输出是sh或dash那 SDKMAN 的语法就跑不通切回 bash 再试。第四种提示目录已存在、拒绝覆盖。这说明之前装过又没卸干净~/.sdkman还在。如果不是有意保留删掉重装最省事但删之前记得把~/.sdkman/etc/config里的自定义配置备份出来那个文件重装之后不会恢复。注意删除~/.sdkman会连同已安装的所有组件一起删掉执行前先sdk current记一下都装了哪些版本重装后按需补回。6.2 使用阶段命令找不到与版本不生效装完之后的问题主要分两类一类是命令层面一类是版本层面。命令层面最典型的是新开终端sdk: command not found。这基本可以断定是 shell 配置文件没生效。检查顺序cat ~/.bashrc | grep sdkman看看有没有那行 source有的话再确认当前 shell 读的是不是这个文件都对但还是不行就手动 source 一次看有没有报错。手动 source 报错通常是文件损坏重新装一遍最快。版本层面最典型的是sdk use java 17.0.10-tem执行成功但java -version还是老版本。这种在 Git Bash 下概率很高原因是current软链接没建成。前面详细讲过排查方法核心是看ls -la的结果里有没有箭头。还有一种情况是命令找不到但PATH看着是对的。比如mvn: command not found而echo $PATH里明明有~/.sdkman/candidates/maven/current/bin。这时候检查那个目录是不是真的存在——如果current指向了一个已经被卸载的版本路径在PATH里但目录是空的自然找不到可执行文件。用readlink -f ~/.sdkman/candidates/maven/current看它到底指向哪指向不存在的目录就重新sdk default一次。另外还有一个不太容易想到的在 IDE 内置终端里 sdk 不可用。IDE 启动时继承的是系统环境变量不会去读你的.bashrc。解决办法是在 IDE 的终端设置里指定 shell 为登录 shell或者干脆在需要的时候手动 source 一次。6.3 排查速查表遇到问题先别急着搜按这张表对一遍八成能定位到方向。现象最可能的原因处理方式sdk: command not found初始化脚本未加载source $HOME/.sdkman/bin/sdkman-init.sh并检查 shell 配置文件安装时报 404平台识别失败Git Bash 常见开sdkman_debug_mode看请求地址改用本地包安装或换 WSL解压失败缺unzip或磁盘空间不足补装unzipdf -h看剩余空间切换后版本不变current软链接未生效ls -la查看开开发者模式后重建下载极慢或中断网络链路问题重试、开离线模式复用本地缓存、改在空闲时段操作PATH有但命令找不到current指向了已卸载版本readlink -f查看目标重新sdk default.sdkmanrc不自动切换未开启sdkman_auto_env编辑~/.sdkman/etc/config改为 true缓存占用越来越大历史安装包未清理sdk flush archives最后再补一个排查习惯遇到判断不了的问题先把sdkman_debug_mode打开让脚本把所有动作打印出来。SDKMAN 的脚本逻辑本身写得挺清晰顺着输出看一遍基本能看出是哪一步走岔了。比起在网上翻十几篇互相矛盾的帖子这一步花的时间反而最少。我在自己的机器上把这两条路线都留着WSL 那套是主力Git Bash 那套主要管 Maven 和 Gradle 这类纯 Java 工具。真实用下来决定体验差异的不是安装那十分钟而是后面每一次切换版本的顺滑程度——这也是我后来不再纠结、直接把 Java 全部交给 WSL 的原因。至于 Git Bash只要你不指望它完全替代 Linux把它当成一个带壳的工具箱用起来其实很舒服。
阅读完成 · 觉得有帮助?