# 云服务器部署
作者:Ethan.Yang
博客:https://blog.ethanyang.cn (opens new window)
脚本仓库:脚本仓库 (opens new window)
为了后面的AI学习, 建议自备云服务器私有部署AI模型, 云服务器软件部署相关命令脚本参考该仓库脚本
# 1. GIT
sudo yum install git
1
# 2. JDK
# 1. 安装
# 1. 将jdk17安装包下载到云服务器并命名 jdk-17.0.14_linux-x64_bin.tar.gz
# 2. 给脚本加权限
chmod +x install-java.sh
# 3. 执行脚本
./install-java.sh
# 4. 环境变量生效
source /etc/profile
# 5. 验证
java -version
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
#!/bin/bash
# JDK 安装脚本 - 增强版
# 适用于 CentOS 7.9 及其他 Linux 发行版
# 支持 JDK 1.8 和 JDK 17
set -e # 遇到错误立即退出
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 日志函数
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# JDK版本配置
declare -A JDK_CONFIGS
JDK_CONFIGS["8"]="jdk-8u202-linux-x64.tar.gz|https://drive.weixin.qq.com/s?k=ACMA4AfQABUsJlf2xB|1.8.0_202"
JDK_CONFIGS["17"]="jdk-17.0.14_linux-x64_bin.tar.gz|https://drive.weixin.qq.com/s?k=ACMA4AfQABU3wtEvhI|17.0.14"
# 默认配置参数
DEFAULT_INSTALL_DIR="/usr/local/java"
DEFAULT_SOURCE_DIR="$(dirname "$0")"
JDK_PACKAGE_8="jdk-8u202-linux-x64.tar.gz"
PROFILE_FILE="/etc/profile"
BASHRC_FILE="/etc/bashrc"
# 运行时变量
JDK_VERSION=""
JDK_PACKAGE=""
JDK_DOWNLOAD_URL=""
JDK_VERSION_NUMBER=""
INSTALL_DIR="$DEFAULT_INSTALL_DIR"
SOURCE_DIR="$DEFAULT_SOURCE_DIR"
SILENT_MODE=false
FORCE_INSTALL=false
CUSTOM_PACKAGE_PATH=""
usage() {
cat << EOF
使用方法: $0 [选项]
选项:
-d, --install-dir DIR 指定JDK安装目录 (默认: $DEFAULT_INSTALL_DIR)
-s, --source-dir DIR 指定JDK包下载/存放目录 (默认: $DEFAULT_SOURCE_DIR)
-p, --package-path PATH 指定JDK包的完整路径
-v, --version VERSION 指定JDK版本 (8 或 17)
-f, --force 强制安装,覆盖已存在的JDK
-q, --quiet 静默模式,减少输出
-h, --help 显示此帮助信息
示例:
$0 # 交互式安装(会询问版本选择)
$0 -v 8 # 安装JDK 8
$0 -v 17 # 安装JDK 17
$0 -d /opt/java -s /home/user # 自定义目录
$0 -p /path/to/jdk-package.tar.gz # 使用本地包
$0 -f -q -v 8 # 强制静默安装JDK 8
EOF
}
# 解析参数
while [[ $# -gt 0 ]]; do
case $1 in
-d|--install-dir)
INSTALL_DIR="$2"
shift 2
;;
-s|--source-dir)
SOURCE_DIR="$2"
shift 2
;;
-p|--package-path)
CUSTOM_PACKAGE_PATH="$2"
shift 2
;;
-v|--version)
JDK_VERSION="$2"
shift 2
;;
-f|--force)
FORCE_INSTALL=true
shift
;;
-q|--quiet)
SILENT_MODE=true
shift
;;
-h|--help)
usage
exit 0
;;
*)
log_error "未知参数: $1"
usage
exit 1
;;
esac
done
# 选择JDK版本
select_jdk_version() {
if [[ -n "$JDK_VERSION" ]]; then
# 验证指定的版本
if [[ "$JDK_VERSION" != "8" && "$JDK_VERSION" != "17" ]]; then
log_error "不支持的JDK版本: $JDK_VERSION (支持: 8, 17)"
exit 1
fi
[[ $SILENT_MODE == false ]] && log_info "使用指定的JDK版本: $JDK_VERSION"
return 0
fi
if [[ $SILENT_MODE == true ]]; then
log_error "静默模式下必须指定JDK版本 (-v 8 或 -v 17)"
exit 1
fi
echo
log_info "请选择要安装的JDK版本:"
echo "1. JDK 8 (1.8.0_202)"
echo "2. JDK 17 (17.0.14)"
echo
while true; do
read -p "请选择版本 (1/2): " -r choice
case $choice in
1)
JDK_VERSION="8"
log_info "已选择JDK 8"
break
;;
2)
JDK_VERSION="17"
log_info "已选择JDK 17"
break
;;
*)
log_warning "请输入 1 或 2"
;;
esac
done
}
# 设置JDK配置
set_jdk_config() {
local config="${JDK_CONFIGS[$JDK_VERSION]}"
if [[ -z "$config" ]]; then
log_error "不支持的JDK版本: $JDK_VERSION"
exit 1
fi
IFS='|' read -r JDK_PACKAGE JDK_DOWNLOAD_URL JDK_VERSION_NUMBER <<< "$config"
[[ $SILENT_MODE == false ]] && log_info "JDK配置: 版本=$JDK_VERSION_NUMBER, 包名=$JDK_PACKAGE"
}
# 检查是否为root用户
check_root() {
if [[ $EUID -ne 0 ]]; then
log_error "此脚本需要root权限运行"
exit 1
fi
}
# 检测系统信息
detect_system() {
if [[ -f /etc/os-release ]]; then
. /etc/os-release
OS=$NAME
VER=$VERSION_ID
elif type lsb_release >/dev/null 2>&1; then
OS=$(lsb_release -si)
VER=$(lsb_release -sr)
else
OS=$(uname -s)
VER=$(uname -r)
fi
[[ $SILENT_MODE == false ]] && log_info "检测到系统: $OS $VER"
}
# 检查已安装的Java
check_existing_java() {
if command -v java >/dev/null 2>&1; then
JAVA_VERSION=$(java -version 2>&1 | head -n 1 | cut -d'"' -f2)
log_warning "检测到已安装的Java版本: $JAVA_VERSION"
if [[ $FORCE_INSTALL == false ]]; then
read -p "是否继续安装? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log_info "安装已取消"
exit 0
fi
fi
fi
}
# 安装依赖包
install_dependencies() {
[[ $SILENT_MODE == false ]] && log_info "检查并安装依赖包..."
if command -v yum >/dev/null 2>&1; then
yum update -y >/dev/null 2>&1 || true
yum install -y tar gzip >/dev/null 2>&1 || true
elif command -v apt-get >/dev/null 2>&1; then
apt-get update >/dev/null 2>&1 || true
apt-get install -y tar gzip >/dev/null 2>&1 || true
fi
}
# 提示手动下载JDK包
prompt_manual_download() {
# 确保源目录存在
if [[ ! -d "$SOURCE_DIR" ]]; then
[[ $SILENT_MODE == false ]] && log_info "创建目录: $SOURCE_DIR"
mkdir -p "$SOURCE_DIR"
if [[ $? -ne 0 ]]; then
log_error "无法创建目录: $SOURCE_DIR"
exit 1
fi
fi
local package_path="$SOURCE_DIR/$JDK_PACKAGE"
# 检查是否已存在
if [[ -f "$package_path" ]]; then
[[ $SILENT_MODE == false ]] && log_info "JDK包已存在: $package_path"
return 0
fi
# 获取脚本的完整路径
local script_dir="$(cd "$(dirname "$0")" && pwd)"
# 提示手动下载
echo
log_warning "=== 需要手动下载JDK包 ==="
log_info "下载地址: $JDK_DOWNLOAD_URL"
log_info "文件名: $JDK_PACKAGE"
log_info "保存路径: $script_dir"
echo
log_info "请按以下步骤操作:"
echo "1. 访问上述下载地址"
echo "2. 登录微信网盘并下载文件"
echo "3. 将下载的文件重命名为: $JDK_PACKAGE"
echo "4. 将文件上传到服务器的目录: $script_dir"
echo
# 等待用户下载
while [[ ! -f "$package_path" ]]; do
read -p "下载并上传完成后按回车继续,或输入 'q' 退出: " -r
if [[ $REPLY == "q" || $REPLY == "Q" ]]; then
log_info "安装已取消"
exit 0
fi
if [[ ! -f "$package_path" ]]; then
log_warning "未找到文件: $package_path"
log_info "请确认文件已正确上传到指定目录: $script_dir"
fi
done
log_success "JDK包准备完成"
}
# 验证JDK包
validate_package() {
local package_path
if [[ -n "$CUSTOM_PACKAGE_PATH" ]]; then
package_path="$CUSTOM_PACKAGE_PATH"
else
package_path="$SOURCE_DIR/$JDK_PACKAGE"
fi
if [[ ! -f "$package_path" ]]; then
log_error "JDK包不存在: $package_path"
exit 1
fi
# 验证包格式
if ! tar -tzf "$package_path" >/dev/null 2>&1; then
log_error "JDK包格式无效或已损坏: $package_path"
exit 1
fi
# 将日志输出重定向到stderr,避免污染返回值
[[ $SILENT_MODE == false ]] && log_success "JDK包验证通过" >&2
echo "$package_path"
}
# 安装JDK
install_jdk() {
local package_path=$1
[[ $SILENT_MODE == false ]] && log_info "开始安装JDK $JDK_VERSION_NUMBER 到: $INSTALL_DIR"
# 创建安装目录
mkdir -p "$INSTALL_DIR"
# 备份现有安装
if [[ -d "$INSTALL_DIR" ]] && [[ $(ls -A "$INSTALL_DIR" 2>/dev/null) ]]; then
if [[ $FORCE_INSTALL == true ]]; then
[[ $SILENT_MODE == false ]] && log_warning "清理现有安装目录"
rm -rf "$INSTALL_DIR"/*
else
log_error "安装目录不为空: $INSTALL_DIR (使用 -f 强制覆盖)"
exit 1
fi
fi
# 解压JDK
[[ $SILENT_MODE == false ]] && log_info "解压JDK包..."
if [[ $SILENT_MODE == true ]]; then
tar -xzf "$package_path" -C "$INSTALL_DIR" --strip-components=1 >/dev/null 2>&1
else
tar -xzf "$package_path" -C "$INSTALL_DIR" --strip-components=1
fi
# 验证安装
if [[ ! -f "$INSTALL_DIR/bin/java" ]]; then
log_error "JDK安装失败,java可执行文件不存在"
exit 1
fi
log_success "JDK解压完成"
}
# 配置环境变量
configure_environment() {
[[ $SILENT_MODE == false ]] && log_info "配置环境变量..."
# 备份配置文件
if [[ -f "$PROFILE_FILE" ]]; then
cp "$PROFILE_FILE" "$PROFILE_FILE.backup.$(date +%Y%m%d_%H%M%S)"
fi
# 移除旧的Java配置
sed -i '/JAVA_HOME/d' "$PROFILE_FILE" 2>/dev/null || true
sed -i '/CLASSPATH/d' "$PROFILE_FILE" 2>/dev/null || true
# 添加新的Java配置
if [[ "$JDK_VERSION" == "17" ]]; then
# JDK 17 不需要CLASSPATH
cat >> "$PROFILE_FILE" << EOF
# Java Environment - Added by JDK installer
export JAVA_HOME=$INSTALL_DIR
export PATH=\$JAVA_HOME/bin:\$PATH
EOF
else
# JDK 8 需要CLASSPATH
cat >> "$PROFILE_FILE" << EOF
# Java Environment - Added by JDK installer
export JAVA_HOME=$INSTALL_DIR
export PATH=\$JAVA_HOME/bin:\$PATH
export CLASSPATH=\$JAVA_HOME/jre/lib/ext:\$JAVA_HOME/lib/tools.jar
EOF
fi
# 同时配置bashrc(某些系统需要)
if [[ -f "$BASHRC_FILE" ]]; then
sed -i '/JAVA_HOME/d' "$BASHRC_FILE" 2>/dev/null || true
sed -i '/CLASSPATH/d' "$BASHRC_FILE" 2>/dev/null || true
if [[ "$JDK_VERSION" == "17" ]]; then
cat >> "$BASHRC_FILE" << EOF
# Java Environment - Added by JDK installer
export JAVA_HOME=$INSTALL_DIR
export PATH=\$JAVA_HOME/bin:\$PATH
EOF
else
cat >> "$BASHRC_FILE" << EOF
# Java Environment - Added by JDK installer
export JAVA_HOME=$INSTALL_DIR
export PATH=\$JAVA_HOME/bin:\$PATH
export CLASSPATH=\$JAVA_HOME/jre/lib/ext:\$JAVA_HOME/lib/tools.jar
EOF
fi
fi
# 立即在当前会话中生效环境变量
export JAVA_HOME="$INSTALL_DIR"
export PATH="$JAVA_HOME/bin:$PATH"
if [[ "$JDK_VERSION" == "8" ]]; then
export CLASSPATH="$JAVA_HOME/jre/lib/ext:$JAVA_HOME/lib/tools.jar"
fi
# 重新加载配置文件
source "$PROFILE_FILE" 2>/dev/null || true
log_success "环境变量配置完成"
[[ $SILENT_MODE == false ]] && log_info "环境变量已在当前会话中生效"
}
# 验证安装
verify_installation() {
[[ $SILENT_MODE == false ]] && log_info "验证安装..."
# 重新加载环境变量
source "$PROFILE_FILE" 2>/dev/null || true
export JAVA_HOME="$INSTALL_DIR"
export PATH="$JAVA_HOME/bin:$PATH"
# 检查Java版本
if "$INSTALL_DIR/bin/java" -version >/dev/null 2>&1; then
local java_version=$("$INSTALL_DIR/bin/java" -version 2>&1 | head -n 1)
log_success "Java安装成功!"
[[ $SILENT_MODE == false ]] && echo "$java_version"
# 检查javac
if "$INSTALL_DIR/bin/javac" -version >/dev/null 2>&1; then
local javac_version=$("$INSTALL_DIR/bin/javac" -version 2>&1)
[[ $SILENT_MODE == false ]] && echo "$javac_version"
fi
else
log_error "Java安装验证失败"
exit 1
fi
}
# 显示安装信息
show_installation_info() {
if [[ $SILENT_MODE == false ]]; then
local classpath_info=""
if [[ "$JDK_VERSION" == "8" ]]; then
classpath_info="CLASSPATH=\$JAVA_HOME/jre/lib/ext:\$JAVA_HOME/lib/tools.jar"
fi
cat << EOF
${GREEN}=== JDK $JDK_VERSION_NUMBER 安装完成 ===${NC}
安装路径: $INSTALL_DIR
Java版本: $("$INSTALL_DIR/bin/java" -version 2>&1 | head -n 1)
${YELLOW}注意事项:${NC}
1. 环境变量已在当前会话中生效,可直接使用 java 命令
2. 新开终端会话会自动加载环境变量
3. 验证安装: java -version
4. 配置文件备份在: $PROFILE_FILE.backup.*
${BLUE}环境变量:${NC}
JAVA_HOME=$INSTALL_DIR
PATH=\$JAVA_HOME/bin:\$PATH
$classpath_info
EOF
fi
}
# 清理临时文件
cleanup() {
if [[ -z "$CUSTOM_PACKAGE_PATH" && -f "$SOURCE_DIR/$JDK_PACKAGE" ]]; then
read -p "是否删除下载的JDK包? (y/N): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
rm -f "$SOURCE_DIR/$JDK_PACKAGE"
log_info "临时文件已清理"
fi
fi
}
# 主函数
main() {
[[ $SILENT_MODE == false ]] && log_info "开始JDK安装程序..."
check_root
detect_system
select_jdk_version
set_jdk_config
check_existing_java
install_dependencies
# 如果没有指定自定义包路径,则提示手动下载
if [[ -z "$CUSTOM_PACKAGE_PATH" ]]; then
prompt_manual_download
fi
local package_path=$(validate_package)
install_jdk "$package_path"
configure_environment
verify_installation
show_installation_info
if [[ $SILENT_MODE == false ]]; then
cleanup
fi
log_success "JDK $JDK_VERSION_NUMBER 安装完成!"
}
# 错误处理
trap 'log_error "安装过程中发生错误,退出码: $?"' ERR
# 执行主函数
main "$@"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
# 2. 卸载
#!/bin/bash
# JDK 删除脚本 - 增强版
# 适用于 CentOS 7.9 及其他 Linux 发行版
# 彻底清理JDK安装和环境配置
set -e # 遇到错误立即退出
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 检测终端是否支持颜色
if [[ ! -t 1 ]] || [[ "$TERM" == "dumb" ]] || [[ -z "$TERM" ]] || [[ "$TERM" == "unknown" ]]; then
# 禁用颜色
RED=''
GREEN=''
YELLOW=''
BLUE=''
NC=''
fi
# 日志函数
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 默认配置参数
DEFAULT_INSTALL_DIR="/usr/local/java"
PROFILE_FILE="/etc/profile"
BASHRC_FILE="/etc/bashrc"
USER_PROFILE="$HOME/.bashrc"
USER_BASH_PROFILE="$HOME/.bash_profile"
USER_PROFILE_FILE="$HOME/.profile"
# 运行时变量
INSTALL_DIR="$DEFAULT_INSTALL_DIR"
SILENT_MODE=false
FORCE_REMOVE=false
BACKUP_CONFIGS=true
usage() {
cat << EOF
使用方法: $0 [选项]
选项:
-d, --install-dir DIR 指定JDK安装目录 (默认: $DEFAULT_INSTALL_DIR)
-f, --force 强制删除,不询问确认
-q, --quiet 静默模式,减少输出
--no-backup 不备份配置文件
-h, --help 显示此帮助信息
示例:
$0 # 交互式删除(会询问确认)
$0 -f # 强制删除
$0 -d /opt/java # 删除指定目录的JDK
$0 -f -q # 强制静默删除
$0 --no-backup # 删除时不备份配置文件
EOF
}
# 解析参数
while [[ $# -gt 0 ]]; do
case $1 in
-d|--install-dir)
INSTALL_DIR="$2"
shift 2
;;
-f|--force)
FORCE_REMOVE=true
shift
;;
-q|--quiet)
SILENT_MODE=true
shift
;;
--no-backup)
BACKUP_CONFIGS=false
shift
;;
-h|--help)
usage
exit 0
;;
*)
log_error "未知参数: $1"
usage
exit 1
;;
esac
done
# 检查是否为root用户
check_root() {
if [[ $EUID -ne 0 ]]; then
log_error "此脚本需要root权限运行"
exit 1
fi
}
# 检测当前Java安装
detect_java() {
[[ $SILENT_MODE == false ]] && log_info "检测当前Java安装..."
# 检查java命令
if command -v java >/dev/null 2>&1; then
JAVA_VERSION=$(java -version 2>&1 | head -n 1 | cut -d'"' -f2)
JAVA_HOME_CURRENT=$(readlink -f $(which java) | sed 's|/bin/java||')
[[ $SILENT_MODE == false ]] && log_info "检测到Java版本: $JAVA_VERSION"
[[ $SILENT_MODE == false ]] && log_info "Java安装路径: $JAVA_HOME_CURRENT"
else
[[ $SILENT_MODE == false ]] && log_warning "未检测到Java命令"
fi
# 检查JAVA_HOME环境变量
if [[ -n "$JAVA_HOME" ]]; then
[[ $SILENT_MODE == false ]] && log_info "当前JAVA_HOME: $JAVA_HOME"
else
[[ $SILENT_MODE == false ]] && log_warning "未设置JAVA_HOME环境变量"
fi
# 检查指定的安装目录
if [[ -d "$INSTALL_DIR" ]]; then
[[ $SILENT_MODE == false ]] && log_info "找到JDK安装目录: $INSTALL_DIR"
else
[[ $SILENT_MODE == false ]] && log_warning "JDK安装目录不存在: $INSTALL_DIR"
fi
}
# 确认删除操作
confirm_removal() {
if [[ $FORCE_REMOVE == true || $SILENT_MODE == true ]]; then
return 0
fi
echo
log_warning "=== 即将执行以下删除操作 ==="
echo "1. 删除JDK安装目录: $INSTALL_DIR"
echo "2. 清理环境变量配置 (JAVA_HOME, PATH, CLASSPATH)"
echo "3. 清理以下配置文件中的Java相关配置:"
echo " - $PROFILE_FILE"
echo " - $BASHRC_FILE"
echo " - $USER_PROFILE (如果存在)"
echo " - $USER_BASH_PROFILE (如果存在)"
echo " - $USER_PROFILE_FILE (如果存在)"
if [[ $BACKUP_CONFIGS == true ]]; then
echo "4. 备份配置文件 (添加.backup.日期时间后缀)"
fi
echo
read -p "确认执行删除操作? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log_info "删除操作已取消"
exit 0
fi
}
# 备份配置文件
backup_config_file() {
local config_file="$1"
if [[ -f "$config_file" && $BACKUP_CONFIGS == true ]]; then
local backup_file="$config_file.backup.$(date +%Y%m%d_%H%M%S)"
cp "$config_file" "$backup_file"
[[ $SILENT_MODE == false ]] && log_info "备份配置文件: $config_file -> $backup_file"
fi
}
# 清理配置文件中的Java环境变量
clean_java_config() {
local config_file="$1"
if [[ ! -f "$config_file" ]]; then
return 0
fi
[[ $SILENT_MODE == false ]] && log_info "清理配置文件: $config_file"
# 备份配置文件
backup_config_file "$config_file"
# 创建临时文件
local temp_file=$(mktemp)
# 删除Java相关的环境变量配置
# 删除包含JAVA_HOME、CLASSPATH的行,以及相关的export和PATH设置
grep -v -E '^[[:space:]]*export[[:space:]]+JAVA_HOME' "$config_file" > "$temp_file" || true
grep -v -E '^[[:space:]]*export[[:space:]]+CLASSPATH' "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file" || true
grep -v -E '^[[:space:]]*JAVA_HOME[[:space:]]*=' "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file" || true
grep -v -E '^[[:space:]]*CLASSPATH[[:space:]]*=' "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file" || true
# 处理PATH中的JAVA_HOME引用
sed -i 's|:\$JAVA_HOME/bin||g' "$temp_file" 2>/dev/null || true
sed -i 's|\$JAVA_HOME/bin:||g' "$temp_file" 2>/dev/null || true
sed -i 's|:\${JAVA_HOME}/bin||g' "$temp_file" 2>/dev/null || true
sed -i 's|\${JAVA_HOME}/bin:||g' "$temp_file" 2>/dev/null || true
# 删除Java相关的注释行
grep -v -E '^[[:space:]]*#.*[Jj]ava.*[Ee]nvironment' "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file" || true
grep -v -E '^[[:space:]]*#.*JDK.*installer' "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file" || true
# 删除连续的空行,只保留单个空行
awk '/^$/ { if (++n <= 1) print; next }; { n=0; print }' "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
# 替换原文件
mv "$temp_file" "$config_file"
[[ $SILENT_MODE == false ]] && log_success "已清理配置文件: $config_file"
}
# 删除JDK安装目录
remove_jdk_directory() {
if [[ -d "$INSTALL_DIR" ]]; then
[[ $SILENT_MODE == false ]] && log_info "删除JDK安装目录: $INSTALL_DIR"
# 检查目录是否包含Java相关文件
if [[ -f "$INSTALL_DIR/bin/java" || -f "$INSTALL_DIR/bin/javac" ]]; then
rm -rf "$INSTALL_DIR"
[[ $SILENT_MODE == false ]] && log_success "JDK安装目录已删除"
else
log_warning "目录 $INSTALL_DIR 不包含Java文件,跳过删除"
fi
else
[[ $SILENT_MODE == false ]] && log_warning "JDK安装目录不存在: $INSTALL_DIR"
fi
}
# 清理环境变量配置
clean_environment_configs() {
[[ $SILENT_MODE == false ]] && log_info "清理环境变量配置..."
# 清理系统级配置文件
clean_java_config "$PROFILE_FILE"
clean_java_config "$BASHRC_FILE"
# 清理用户级配置文件(如果存在)
if [[ -f "$USER_PROFILE" ]]; then
clean_java_config "$USER_PROFILE"
fi
if [[ -f "$USER_BASH_PROFILE" ]]; then
clean_java_config "$USER_BASH_PROFILE"
fi
if [[ -f "$USER_PROFILE_FILE" ]]; then
clean_java_config "$USER_PROFILE_FILE"
fi
}
# 清理当前会话的环境变量
clean_current_session() {
[[ $SILENT_MODE == false ]] && log_info "清理当前会话的环境变量..."
unset JAVA_HOME
unset CLASSPATH
# 从PATH中移除Java相关路径
if [[ -n "$PATH" ]]; then
# 移除包含java的路径
PATH=$(echo "$PATH" | tr ':' '\n' | grep -v -E '/java/|/jdk|/jre' | tr '\n' ':' | sed 's/:$//')
export PATH
fi
[[ $SILENT_MODE == false ]] && log_success "当前会话环境变量已清理"
}
# 验证删除结果
verify_removal() {
[[ $SILENT_MODE == false ]] && log_info "验证删除结果..."
local issues_found=false
# 检查java命令是否还存在
if command -v java >/dev/null 2>&1; then
local remaining_java=$(which java)
log_warning "仍然检测到Java命令: $remaining_java"
issues_found=true
fi
# 检查JAVA_HOME是否还存在
if [[ -n "$JAVA_HOME" ]]; then
log_warning "JAVA_HOME环境变量仍然存在: $JAVA_HOME"
issues_found=true
fi
# 检查安装目录是否还存在
if [[ -d "$INSTALL_DIR" ]]; then
log_warning "JDK安装目录仍然存在: $INSTALL_DIR"
issues_found=true
fi
if [[ $issues_found == false ]]; then
[[ $SILENT_MODE == false ]] && log_success "JDK删除验证通过"
else
log_warning "删除过程中发现一些问题,请检查上述警告信息"
fi
}
# 显示删除完成信息
show_completion_info() {
if [[ $SILENT_MODE == false ]]; then
cat << EOF
${GREEN}=== JDK 删除完成 ===${NC}
删除的安装目录: $INSTALL_DIR
清理的配置文件:
- $PROFILE_FILE
- $BASHRC_FILE
EOF
if [[ -f "$USER_PROFILE" ]]; then
echo " - $USER_PROFILE"
fi
if [[ -f "$USER_BASH_PROFILE" ]]; then
echo " - $USER_BASH_PROFILE"
fi
if [[ -f "$USER_PROFILE_FILE" ]]; then
echo " - $USER_PROFILE_FILE"
fi
cat << EOF
${YELLOW}注意事项:${NC}
1. 环境变量已从当前会话中清理
2. 请重新登录或重启终端使所有更改完全生效
3. 如果需要恢复配置,可以使用备份文件 (*.backup.*)
4. 验证删除: 执行 'java -version' 应该提示命令不存在
${BLUE}如需重新安装JDK:${NC}
请使用 install-java.sh 脚本进行安装
EOF
fi
}
# 主函数
main() {
[[ $SILENT_MODE == false ]] && log_info "开始JDK删除程序..."
check_root
detect_java
confirm_removal
remove_jdk_directory
clean_environment_configs
clean_current_session
verify_removal
show_completion_info
log_success "JDK删除完成!"
}
# 错误处理
trap 'log_error "删除过程中发生错误,退出码: $?"' ERR
# 执行主函数
main "$@"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
# 3. MAVEN
# 4. Docker
将shell脚本(install_docker.sh)上传到docker-installation文件夹中, 执行以下命令
# 为添加权限
chmod +x install_docker.sh
# 执行
./install_docker.sh
1
2
3
4
2
3
4
# 脚本解释
#!/bin/bash
# 设置颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m' # No Color
# 输出带颜色的信息函数
info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
error() {
echo -e "${RED}[ERROR]${NC} $1"
exit 1
}
# 检查是否以root用户运行
if [ "$(id -u)" -ne 0 ]; then
warning "此脚本需要root权限运行,将尝试使用sudo"
exec sudo "$0" "$@"
exit $?
fi
info "开始安装 Docker 环境..."
# 显示系统信息
info "系统信息:"
echo " 内核版本: $(uname -r)"
echo " 操作系统: $(grep PRETTY_NAME /etc/os-release | cut -d '=' -f2 | tr -d '\"')"
# 检查是否已安装Docker
if command -v docker &> /dev/null; then
INSTALLED_DOCKER_VERSION=$(docker --version | cut -d ' ' -f3 | cut -d ',' -f1)
warning "检测到系统已安装 Docker,版本为: $INSTALLED_DOCKER_VERSION"
read -p "是否卸载已安装的 Docker 并安装新版本?(y/n): " UNINSTALL_DOCKER
if [[ "$UNINSTALL_DOCKER" =~ ^[Yy]$ ]]; then
info "开始卸载 Docker..."
systemctl stop docker &> /dev/null
yum remove -y docker-ce docker-ce-cli containerd.io docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine &> /dev/null
rm -rf /var/lib/docker
info "Docker 卸载完成"
else
info "保留已安装 Docker,退出安装"
exit 0
fi
fi
# 更新系统包
info "更新系统包..."
yum update -y || error "系统更新失败"
# 安装依赖包
info "安装依赖包..."
yum install -y yum-utils device-mapper-persistent-data lvm2 || error "依赖包安装失败"
# 添加阿里云 Docker CE 仓库源
info "添加阿里云 Docker CE 仓库..."
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo || error "添加Docker仓库失败"
# 安装 Docker CE(最新稳定版)
info "安装 Docker CE..."
yum install -y docker-ce docker-ce-cli containerd.io || error "Docker安装失败"
# 安装 Docker Compose(使用清华大学镜像,解决 GitHub 下载失败问题)
info "安装 Docker Compose..."
DOCKER_COMPOSE_VERSION="v2.24.1"
ARCH=$(uname -m)
# 安装Docker Compose
info "安装Docker Compose v2.24.1..."
curl -L https://gitee.com/fustack/docker-compose/releases/download/v2.24.1/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose || error "Docker Compose下载失败"
chmod +x /usr/local/bin/docker-compose || error "无法设置Docker Compose可执行权限"
# 启动 Docker 服务
info "启动 Docker 服务..."
systemctl start docker || error "Docker 服务启动失败"
# 设置 Docker 开机自启
info "设置 Docker 开机自启..."
systemctl enable docker || error "设置 Docker 开机自启失败"
# 配置 Docker 镜像加速器,使用阿里云官方加速地址及其他常用镜像加速
info "配置 Docker 镜像加速器..."
mkdir -p /etc/docker
cat > /etc/docker/daemon.json << EOF
{
"registry-mirrors": [
"https://mirror.ccs.tencentyun.com"
]
}
EOF
# 重启 Docker 使配置生效
info "重启 Docker 服务以应用配置..."
systemctl restart docker || error "Docker 重启失败"
# 验证安装
info "验证 Docker 版本..."
docker --version
info "验证 Docker Compose 版本..."
docker-compose --version
info "Docker 环境安装完成!"
info "当前配置的镜像加速地址为:"
echo " - https://registry.aliyuncs.com (阿里云)"
echo " - https://docker.mirrors.ustc.edu.cn (中国科技大学)"
echo " - https://hub-mirror.c.163.com (网易)"
info "如果拉取镜像速度慢或失败,请检查网络和镜像地址的可用性。"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
创建文件夹
# 在根目录创建文件夹存放后续docker相关脚本
mkdir docker-installation
# 存放相关软件的docker-compose文件
mkdir software
# 存放docker安装的软件的配置文件的宿主机挂载文件
mkdir configs
# 存放相关软件日志
mkdir logs
docker-installation/
├── install_docker.sh # 安装docker脚本
├── install_software.sh # 主安装脚本,交互选择并调用对应compose文件
├── software/ # 各软件docker-compose配置文件存放目录
│ ├── docker-compose-nginx.yml
│ ├── docker-compose-mysql.yml
│ ├── docker-compose-redis.yml
│ ├── docker-compose-elasticsearch.yml
│ └── ... # 其他软件的compose配置
├── configs/ # 各软件的自定义配置文件目录
│ ├── nginx/
│ │ └── nginx.conf
│ ├── mysql/
│ │ └── my.cnf
│ └── redis/
│ └── redis.conf
├── logs/ # 日志文件存放目录(可选)
└── README.md # 使用说明文档
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 5. 交互命令shell脚本 方便Docker软件安装
新增软件的docker-compose时, 可以完善该脚本
#!/bin/bash
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m' # No Color
info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
error() {
echo -e "${RED}[ERROR]${NC} $1"
exit 1
}
# 检查docker是否安装
if ! command -v docker &> /dev/null; then
error "未检测到Docker,请先安装Docker环境!"
fi
# 检查docker-compose是否安装
if ! command -v docker-compose &> /dev/null; then
error "未检测到docker-compose,请先安装docker-compose!"
fi
echo "请选择要安装的软件:"
echo "1) Portainer"
echo "2) Redis"
echo "99) 退出"
read -rp "请输入选项编号: " choice
case $choice in
1)
info "正在使用 docker-compose 安装 Portainer..."
docker-compose -f software/docker-compose-portainer.yml up -d || error "启动Portainer失败"
info "Portainer安装完成,访问地址:http://localhost:9000"
;;
2)
info "正在使用 docker-compose 安装 Redis..."
docker-compose -f software/docker-compose-redis.yml up -d || error "启动Redis失败"
info "Redis安装完成,端口16379,密码请查看配置文件configs/redis/redis.conf"
;;
99)
info "退出安装"
exit 0
;;
*)
error "无效的选项"
;;
esac
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# 增加权限
chmod +x install_software.sh
1
2
2
# 6. Portainer
在software目录下创建 docker-compose-portainer.yml
# docker-compose-portainer.yml
version: "3.9" # Docker Compose 文件版本
services:
portainer: # Portainer 服务配置
image: portainer/portainer-ce:latest # 使用官方最新的 Portainer CE 镜像
container_name: portainer # 容器名称,方便管理
restart: unless-stopped # 容器异常退出自动重启,除非主动停止
ports:
- "9000:9000" # 映射宿主机9000端口到容器9000端口,访问Web UI
volumes:
- /var/run/docker.sock:/var/run/docker.sock # 绑定 Docker Socket,实现宿主机Docker管理
- portainer_data:/data # 持久化Portainer数据 /var/lib/docker/volumes/portainer_data/_data/
networks:
- my-network # 指定自定义网络
volumes:
portainer_data: # 定义数据卷,用于存储 Portainer 数据
networks:
my-network: # 自定义网络配置
driver: bridge # 使用桥接网络驱动(默认网络类型)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
执行运行脚本
./install_software.sh
# 选择 1 安装Portainer
1
2
2
安全组放开9000端口
# 7. Redis
在software目录下创建 docker-compose-redis.yml
# docker-compose-redis.yml
version: "3.9" # Docker Compose 文件版本
services:
redis: # Redis 服务配置
image: redis:6.2 # 使用官方 Redis 镜像(可根据需要更换版本)
container_name: redis # 容器名称
restart: always # 异常退出时自动重启
hostname: redis
privileged: true
ports:
- 16379:6379 # 映射 Redis 默认端口
volumes:
- ../configs/redis/redis.conf:/usr/local/etc/redis/redis.conf # 挂载自定义配置文件
- redis_data:/data # Redis 数据持久化
command: redis-server /usr/local/etc/redis/redis.conf # 启动时加载配置
healthcheck:
test: [ "CMD", "redis-cli", "ping" ]
interval: 10s
timeout: 5s
retries: 3
networks:
- my-network # 使用自定义网络,便于服务互联
volumes:
redis_data: # Redis 数据卷,用于持久化
networks:
my-network: # 自定义桥接网络
driver: bridge
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
在../configs/redis/redis.conf 创建挂载配置文件
mkdir ./configs/redis
cat > ../configs/redis/redis.conf << EOF
bind 0.0.0.0
port 6379
requirepass 自己的登录密码, 不需要可以删除
EOF
1
2
3
4
5
6
2
3
4
5
6
安装完成后放开安全组 16379