flashjdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,497 @@
1
+ #!/usr/bin/env bash
2
+ # ============================================================
3
+ # JDK 路径一键切换脚本 (macOS / Linux)
4
+ # 支持:扫描已安装JDK、手动输入路径、自动更新 JAVA_HOME 和 PATH
5
+ # 支持:本地缓存自定义扫描根目录
6
+ # ============================================================
7
+
8
+ # 自动检测版本号(从 package.json 读取,兼容 npm 全局安装和开发环境)
9
+ _detect_version() {
10
+ local script_dir
11
+ script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12
+ local pkg
13
+ for pkg in "$script_dir/../package.json" "$script_dir/package.json"; do
14
+ if [ -f "$pkg" ]; then
15
+ node -p "require('$pkg').version" 2>/dev/null && return
16
+ sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$pkg" 2>/dev/null && return
17
+ fi
18
+ done
19
+ echo "unknown"
20
+ }
21
+ VERSION=$(_detect_version)
22
+
23
+ # 缓存目录:统一存放于 ~/.config/flashjdk/
24
+ CACHE_DIR="$HOME/.config/flashjdk"
25
+ CACHE_FILE="$CACHE_DIR/custom-roots.txt"
26
+
27
+ # ════ 颜色定义 ════════════════════════════════════════════════
28
+ RED='\033[0;31m'
29
+ GREEN='\033[0;32m'
30
+ YELLOW='\033[1;33m'
31
+ CYAN='\033[0;36m'
32
+ MAGENTA='\033[0;35m'
33
+ GRAY='\033[0;90m'
34
+ NC='\033[0m'
35
+
36
+ log_info() { echo -e "${CYAN}[$(date +%H:%M:%S)] [INFO] $1${NC}"; }
37
+ log_success() { echo -e "${GREEN}[$(date +%H:%M:%S)] [SUCCESS] $1${NC}"; }
38
+ log_warn() { echo -e "${YELLOW}[$(date +%H:%M:%S)] [WARN] $1${NC}"; }
39
+ log_error() { echo -e "${RED}[$(date +%H:%M:%S)] [ERROR] $1${NC}"; }
40
+ log_title() { echo -e "${MAGENTA}$1${NC}"; }
41
+ separator() { echo -e "${GRAY}============================================================${NC}"; }
42
+
43
+ # ════ OS 检测 ════════════════════════════════════════════════
44
+ detect_os() {
45
+ case "$(uname -s)" in
46
+ Darwin) echo "mac" ;;
47
+ Linux) echo "linux" ;;
48
+ *) echo "unknown" ;;
49
+ esac
50
+ }
51
+
52
+ OS=$(detect_os)
53
+
54
+ # ════ 路径工具函数 ══════════════════════════════════════════════
55
+
56
+ # 规范化路径:去除首尾空白、引号、尾随斜杠
57
+ normalize_path() {
58
+ local p="$1"
59
+ # 去除首尾空白
60
+ p=$(echo "$p" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
61
+ # 去除首尾引号
62
+ p="${p#\"}"; p="${p%\"}"
63
+ p="${p#\'}"; p="${p%\'}"
64
+ # 去除尾随斜杠(保留根路径 /)
65
+ while [[ "$p" != "/" && ( "$p" == */ || "$p" == *\\ ) ]]; do
66
+ p="${p%/}"
67
+ p="${p%\\}"
68
+ done
69
+ echo "$p"
70
+ }
71
+
72
+ # 检查路径是否存在(双重检查:-d + ls)
73
+ test_path_safe() {
74
+ local p="$1"
75
+ [ -z "$p" ] && return 1
76
+ [ -d "$p" ] && return 0
77
+ # Fallback: 尝试 ls(有时能处理 -d 无法处理的情况)
78
+ ls "$p" >/dev/null 2>&1 && return 0
79
+ return 1
80
+ }
81
+
82
+ # ════ 默认扫描根目录(按 OS 区分)════════════════════════════
83
+ get_default_roots() {
84
+ if [ "$OS" = "mac" ]; then
85
+ echo "/Library/Java/JavaVirtualMachines"
86
+ echo "$HOME/Library/Java/JavaVirtualMachines"
87
+ echo "/usr/local/opt"
88
+ echo "/opt/homebrew/opt"
89
+ echo "$HOME/.sdkman/candidates/java"
90
+ echo "$HOME/.jdks"
91
+ else
92
+ echo "/usr/lib/jvm"
93
+ echo "/usr/local/java"
94
+ echo "/usr/local/jdk"
95
+ echo "/opt/java"
96
+ echo "/opt/jdk"
97
+ echo "$HOME/.sdkman/candidates/java"
98
+ echo "$HOME/.jdks"
99
+ fi
100
+ }
101
+
102
+ # ════ JDK 发现逻辑 ════════════════════════════════════════════
103
+
104
+ # 在单个根目录下查找 JDK,输出每个合法的 JDK Home 路径(每行一个)
105
+ _find_jdk_in_root() {
106
+ local root="$1"
107
+ [ -d "$root" ] || return
108
+
109
+ if [ "$OS" = "mac" ]; then
110
+ # macOS 标准 .jdk 包结构:xxx.jdk/Contents/Home
111
+ for bundle in "$root"/*.jdk; do
112
+ [ -d "$bundle" ] || continue
113
+ local home="$bundle/Contents/Home"
114
+ [ -x "$home/bin/java" ] && echo "$home"
115
+ done
116
+ # 扁平结构(SDKMAN、Homebrew opt、.jdks 等):直接包含 bin/java
117
+ for dir in "$root"/*/; do
118
+ dir="${dir%/}"
119
+ [ -d "$dir" ] || continue
120
+ [[ "$dir" == *.jdk ]] && continue # 已在上面处理
121
+ # Homebrew: opt/openjdk@17/libexec/openjdk.jdk/Contents/Home
122
+ if [ -d "$dir/libexec" ]; then
123
+ for inner in "$dir/libexec"/*.jdk; do
124
+ local h="$inner/Contents/Home"
125
+ [ -x "$h/bin/java" ] && echo "$h"
126
+ done
127
+ fi
128
+ [ -x "$dir/bin/java" ] && echo "$dir"
129
+ done
130
+ else
131
+ # Linux:子目录直接含 bin/java
132
+ for dir in "$root"/*/; do
133
+ dir="${dir%/}"
134
+ [ -d "$dir" ] || continue
135
+ [ -x "$dir/bin/java" ] && echo "$dir"
136
+ done
137
+ # 根目录本身也可能是 JDK(手动安装场景)
138
+ [ -x "$root/bin/java" ] && echo "$root"
139
+ fi
140
+ }
141
+
142
+ find_jdk_installations() {
143
+ local -a roots=("$@")
144
+ local tmp
145
+ tmp=$(mktemp)
146
+ for root in "${roots[@]}"; do
147
+ _find_jdk_in_root "$root" >> "$tmp"
148
+ done
149
+ # 去重排序后输出
150
+ sort -u "$tmp"
151
+ rm -f "$tmp"
152
+ }
153
+
154
+ # macOS 使用系统 java_home 工具补充扫描
155
+ _macos_java_home_list() {
156
+ command -v /usr/libexec/java_home &>/dev/null || return
157
+ /usr/libexec/java_home -V 2>&1 | grep -E '^\s+[0-9]' | awk '{print $NF}'
158
+ }
159
+
160
+ # ════ 缓存读写 ════════════════════════════════════════════════
161
+ read_cached_roots() {
162
+ [ -f "$CACHE_FILE" ] || return
163
+ while IFS= read -r line; do
164
+ [[ -n "$line" && "$line" != \#* ]] && normalize_path "$line"
165
+ done < "$CACHE_FILE"
166
+ }
167
+
168
+ save_cached_roots() {
169
+ mkdir -p "$CACHE_DIR"
170
+ # 规范化所有路径后再保存
171
+ local normalized=()
172
+ local p
173
+ for p in "$@"; do
174
+ p=$(normalize_path "$p")
175
+ [ -n "$p" ] && normalized+=("$p")
176
+ done
177
+ # 去重
178
+ printf '%s\n' "${normalized[@]}" | awk '!seen[$0]++' > "$CACHE_FILE"
179
+ log_success "已保存到:$CACHE_FILE"
180
+ }
181
+
182
+ get_all_search_roots() {
183
+ {
184
+ get_default_roots
185
+ read_cached_roots
186
+ } | awk '!seen[$0]++'
187
+ }
188
+
189
+ # ════ PATH / JAVA_HOME 更新逻辑 ══════════════════════════════
190
+
191
+ # 写入系统级配置(需要 sudo)
192
+ _write_system_profile() {
193
+ local content="$1"
194
+ local target="/etc/profile.d/flashjdk.sh"
195
+
196
+ if sudo -n true 2>/dev/null || sudo true 2>/dev/null; then
197
+ echo -e "$content" | sudo tee "$target" > /dev/null
198
+ sudo chmod 644 "$target"
199
+ log_success "系统配置已写入:$target"
200
+ else
201
+ log_warn "sudo 授权失败,跳过系统级配置,仅更新用户配置文件。"
202
+ fi
203
+ }
204
+
205
+ # 更新用户 Shell 配置文件,替换 flashjdk 托管块
206
+ _update_user_profile() {
207
+ local content="$1"
208
+ local pfile="$2"
209
+ local begin_mark="# flashjdk: managed block - do not edit"
210
+ local end_mark="# flashjdk: end"
211
+
212
+ # 若文件不存在则创建
213
+ [ -f "$pfile" ] || touch "$pfile"
214
+
215
+ # 删除旧托管块
216
+ local tmp
217
+ tmp=$(mktemp)
218
+ awk "
219
+ /$begin_mark/ { skip=1 }
220
+ !skip { print }
221
+ /$end_mark/ { skip=0 }
222
+ " "$pfile" > "$tmp" && mv "$tmp" "$pfile"
223
+
224
+ # 追加新块
225
+ {
226
+ echo ""
227
+ echo -e "$content"
228
+ } >> "$pfile"
229
+
230
+ log_success "用户配置已更新:$pfile"
231
+ }
232
+
233
+ update_system_jdk_path() {
234
+ local new_jdk_root="$1"
235
+ local new_bin="$new_jdk_root/bin"
236
+
237
+ if [ ! -d "$new_bin" ]; then
238
+ log_error "bin 目录不存在:$new_bin"
239
+ return 1
240
+ fi
241
+
242
+ local block
243
+ block="# flashjdk: managed block - do not edit
244
+ export JAVA_HOME=\"$new_jdk_root\"
245
+ export PATH=\"\$JAVA_HOME/bin:\$PATH\"
246
+ # flashjdk: end"
247
+
248
+ # 系统级(/etc/profile.d/,对所有用户生效)
249
+ _write_system_profile "$block"
250
+
251
+ # 用户级:根据当前 Shell 选择合适的配置文件
252
+ local shell_name
253
+ shell_name=$(basename "${SHELL:-bash}")
254
+ case "$shell_name" in
255
+ zsh)
256
+ _update_user_profile "$block" "$HOME/.zshrc"
257
+ ;;
258
+ bash)
259
+ _update_user_profile "$block" "$HOME/.bashrc"
260
+ # macOS bash 还需更新 .bash_profile
261
+ [ "$OS" = "mac" ] && _update_user_profile "$block" "$HOME/.bash_profile"
262
+ ;;
263
+ *)
264
+ _update_user_profile "$block" "$HOME/.profile"
265
+ ;;
266
+ esac
267
+
268
+ # 立即在当前会话生效
269
+ export JAVA_HOME="$new_jdk_root"
270
+ export PATH="$new_bin:$PATH"
271
+ log_success "当前会话 JAVA_HOME 和 PATH 已立即生效。"
272
+
273
+ echo "$new_bin"
274
+ }
275
+
276
+ # ════ CLI 命令:-list(列出所有 JDK 环境)═════════════════════
277
+ cmd_list() {
278
+ local -a all_roots=()
279
+ while IFS= read -r line; do
280
+ [ -n "$line" ] && all_roots+=("$line")
281
+ done < <(get_all_search_roots)
282
+
283
+ local -a jdk_list=()
284
+ while IFS= read -r line; do
285
+ [ -n "$line" ] && jdk_list+=("$line")
286
+ done < <(find_jdk_installations "${all_roots[@]}")
287
+
288
+ if [ "$OS" = "mac" ]; then
289
+ while IFS= read -r line; do
290
+ [ -n "$line" ] || continue
291
+ local dup=0
292
+ for existing in "${jdk_list[@]}"; do
293
+ [ "$existing" = "$line" ] && dup=1 && break
294
+ done
295
+ [ $dup -eq 0 ] && jdk_list+=("$line")
296
+ done < <(_macos_java_home_list)
297
+ fi
298
+
299
+ echo ""
300
+ log_info "当前 JAVA_HOME:${JAVA_HOME:-(未设置)}"
301
+ echo ""
302
+
303
+ if [ ${#jdk_list[@]} -gt 0 ]; then
304
+ log_success "扫描到 ${#jdk_list[@]} 个 JDK 环境:"
305
+ echo ""
306
+ for i in "${!jdk_list[@]}"; do
307
+ local marker=" "
308
+ [ "${jdk_list[$i]}" = "$JAVA_HOME" ] && marker="*"
309
+ echo -e " ${marker} [${CYAN}$((i+1))${NC}] ${jdk_list[$i]}"
310
+ done
311
+ echo ""
312
+ echo -e " ${GRAY}(* 表示当前使用的 JDK)${NC}"
313
+ else
314
+ log_warn "未扫描到 JDK 环境。"
315
+ echo ""
316
+ echo -e " 请使用 ${GREEN}flashjdk -set <路径>${NC} 添加扫描根目录。"
317
+ fi
318
+ echo ""
319
+ }
320
+
321
+ # ════ CLI 命令:-set <路径>(添加扫描根目录)═══════════════════
322
+ cmd_set() {
323
+ local new_path="$1"
324
+
325
+ if [ -z "$new_path" ]; then
326
+ log_error "缺少路径参数。用法:flashjdk -set <路径>"
327
+ echo ""
328
+ exit 1
329
+ fi
330
+
331
+ new_path=$(normalize_path "$new_path")
332
+
333
+ if ! test_path_safe "$new_path"; then
334
+ log_error "路径不存在:$new_path"
335
+ exit 1
336
+ fi
337
+
338
+ local -a cached=()
339
+ while IFS= read -r line; do
340
+ [ -n "$line" ] && cached+=("$line")
341
+ done < <(read_cached_roots)
342
+
343
+ if printf '%s\n' "${cached[@]}" | grep -qx "$new_path"; then
344
+ log_warn "该路径已在缓存中,无需重复添加。"
345
+ exit 0
346
+ fi
347
+
348
+ cached+=("$new_path")
349
+ save_cached_roots "${cached[@]}"
350
+ log_success "已添加扫描根目录:$new_path"
351
+ }
352
+
353
+ # ════ CLI 命令:-change(选择并切换 JDK)════════════════════════
354
+ cmd_change() {
355
+ local -a all_roots=()
356
+ while IFS= read -r line; do
357
+ [ -n "$line" ] && all_roots+=("$line")
358
+ done < <(get_all_search_roots)
359
+
360
+ echo ""
361
+ log_info "正在扫描 JDK 环境..."
362
+
363
+ local -a jdk_list=()
364
+ while IFS= read -r line; do
365
+ [ -n "$line" ] && jdk_list+=("$line")
366
+ done < <(find_jdk_installations "${all_roots[@]}")
367
+
368
+ if [ "$OS" = "mac" ]; then
369
+ while IFS= read -r line; do
370
+ [ -n "$line" ] || continue
371
+ local dup=0
372
+ for existing in "${jdk_list[@]}"; do
373
+ [ "$existing" = "$line" ] && dup=1 && break
374
+ done
375
+ [ $dup -eq 0 ] && jdk_list+=("$line")
376
+ done < <(_macos_java_home_list)
377
+ fi
378
+
379
+ if [ ${#jdk_list[@]} -eq 0 ]; then
380
+ log_warn "未扫描到 JDK 环境。"
381
+ echo ""
382
+ echo -e " 请使用 ${GREEN}flashjdk -set <路径>${NC} 添加扫描根目录后重试。"
383
+ exit 1
384
+ fi
385
+
386
+ echo ""
387
+ log_info "当前 JAVA_HOME:${JAVA_HOME:-(未设置)}"
388
+ echo ""
389
+
390
+ log_success "扫描到以下 JDK 版本:"
391
+ echo ""
392
+ for i in "${!jdk_list[@]}"; do
393
+ local marker=" "
394
+ [ "${jdk_list[$i]}" = "$JAVA_HOME" ] && marker="*"
395
+ echo -e " ${marker} [${CYAN}$((i+1))${NC}] ${jdk_list[$i]}"
396
+ done
397
+ echo ""
398
+ echo -e " ${GRAY}(* 表示当前使用的 JDK)${NC}"
399
+ echo ""
400
+
401
+ echo -e "${YELLOW}请输入要切换的 JDK 序号(输入 Q 取消):${NC}"
402
+
403
+ while true; do
404
+ read -rp ">>> " user_input
405
+ user_input="$(echo "$user_input" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
406
+
407
+ if [ -z "$user_input" ]; then
408
+ log_error "输入为空,请重新输入。"
409
+ continue
410
+ fi
411
+ if [ "$(echo "$user_input" | tr '[:lower:]' '[:upper:]')" = "Q" ]; then
412
+ log_info "已取消。"
413
+ exit 0
414
+ fi
415
+
416
+ if [[ "$user_input" =~ ^[0-9]+$ ]]; then
417
+ local idx=$((user_input - 1))
418
+ if [ "$idx" -ge 0 ] && [ "$idx" -lt ${#jdk_list[@]} ]; then
419
+ local selected="${jdk_list[$idx]}"
420
+
421
+ if [ ! -d "$selected" ]; then
422
+ log_error "路径不存在:$selected"
423
+ exit 1
424
+ fi
425
+ if [ ! -x "$selected/bin/java" ]; then
426
+ log_error "bin/java 不存在,请确认选择的是 JDK 根目录。"
427
+ exit 1
428
+ fi
429
+
430
+ echo ""
431
+ separator
432
+ log_info "正在切换 JDK 到:$selected"
433
+ update_system_jdk_path "$selected"
434
+
435
+ echo ""
436
+ separator
437
+ log_info "正在验证 java -version..."
438
+ echo ""
439
+ "$selected/bin/java" -version 2>&1
440
+ echo ""
441
+ log_success "JDK 切换完成!"
442
+
443
+ case "$(basename "${SHELL:-bash}")" in
444
+ zsh) log_info "如需手动刷新:source ~/.zshrc" ;;
445
+ bash) log_info "如需手动刷新:source ~/.bashrc" ;;
446
+ *) log_info "如需手动刷新:source ~/.profile" ;;
447
+ esac
448
+ exit 0
449
+ else
450
+ log_error "序号无效,请重新输入。"
451
+ fi
452
+ else
453
+ log_error "请输入数字序号。"
454
+ fi
455
+ done
456
+ }
457
+
458
+ # ════ 帮助信息 ══════════════════════════════════════════════════
459
+ show_help() {
460
+ echo ""
461
+ echo -e " ${MAGENTA}flashJDK${NC} v${VERSION} — JDK 版本一键切换工具 [${OS}]"
462
+ echo ""
463
+ echo -e " ${GREEN}用法:${NC}"
464
+ echo ""
465
+ echo -e " flashjdk -v 查看版本号"
466
+ echo -e " flashjdk -list 列出所有扫描到的 JDK 环境"
467
+ echo -e " flashjdk -set <路径> 添加自定义扫描根目录"
468
+ echo -e " flashjdk -change 列出所有 JDK 版本并选择切换"
469
+ echo ""
470
+ echo -e " ${GRAY}缓存文件:$CACHE_FILE${NC}"
471
+ echo ""
472
+ }
473
+
474
+ # ════ CLI 参数分发 ══════════════════════════════════════════════
475
+ case "${1:-}" in
476
+ -v|--version)
477
+ echo "v${VERSION}"
478
+ ;;
479
+ -list)
480
+ cmd_list
481
+ ;;
482
+ -set)
483
+ cmd_set "${2:-}"
484
+ ;;
485
+ -change)
486
+ cmd_change
487
+ ;;
488
+ "")
489
+ show_help
490
+ ;;
491
+ *)
492
+ echo "未知参数: $1"
493
+ echo ""
494
+ show_help
495
+ exit 1
496
+ ;;
497
+ esac