外观
MySQL 8.0 安装管理脚本
约 4545 字大约 15 分钟
ShellMySQL安装卸载
2026-05-18
基于官方二进制包的 MySQL 8.0 安装管理脚本,支持安装与卸载双模式,交互式版本选择、自动检测 glibc 版本、自定义安装路径。适用于 Linux Generic x86_64 平台。
功能特性
| 特性 | 说明 |
|---|---|
| 📋 版本选择 | 交互式菜单,支持 8.0.35 ~ 8.0.45 全部版本 |
| 🔍 自动检测 | 自动识别系统 glibc 版本(2.17 / 2.28),选择对应安装包 |
| 📂 自定义路径 | 支持自定义安装目录,默认 /usr/local/mysql |
| 🗑️ 旧版清理 | 自动检测并卸载 MariaDB / 旧版 MySQL |
| 🔒 密码管理 | 支持自定义密码,未设置则输出临时密码 |
| 🛡️ 安全配置 | 交互式选择是否关闭防火墙和 SELinux |
| ⚙️ 服务配置 | 支持 SysV init(/etc/init.d)和 systemd 两种服务管理方式 |
| 🗑️ 一键卸载 | 自动检测安装信息,数据目录二次确认保护,清理所有残留 |
| 📋 详细日志 | 全程记录操作日志 |
快速开始
使用 curl:
curl -sL https://script.merma.cn/scripts/shell/InstallScript/install_mysql.sh -o /tmp/install_mysql.sh && bash /tmp/install_mysql.sh; rm -f /tmp/install_mysql.sh使用 wget:
wget -qO /tmp/install_mysql.sh https://script.merma.cn/scripts/shell/InstallScript/install_mysql.sh && bash /tmp/install_mysql.sh; rm -f /tmp/install_mysql.sh演示效果
root@localhost ~ # ./install_mysql.sh
╔═══════════════════════════════════════════╗
║ MySQL 8.0 管理脚本 ║
║ 适用于 Linux Generic x86_64 ║
╚═══════════════════════════════════════════╝
请选择操作:
[1] 安装 MySQL 8.0
[2] 卸载 MySQL
[0] 退出
请输入选择 [1/2/0]: 1
========== MySQL 8.0 版本选择 ==========
[1] 8.0.35 [2] 8.0.36 [3] 8.0.37 [4] 8.0.39
[5] 8.0.40 [6] 8.0.41 [7] 8.0.42 [8] 8.0.43
[9] 8.0.44 [10] 8.0.45
请选择版本编号 [1-10]: 10
[INFO] 已选择 MySQL 8.0.45
========== 检测系统 glibc 版本 ==========
[INFO] 当前系统 glibc 版本: 2.34
[INFO] 将使用 glibc2.28 版本的安装包
========== 安装路径配置 ==========
请输入 MySQL 的安装目录,MySQL 文件将直接放在该目录下
例如输入 /mnt/application/mysql,则 bin/lib/data 等均在此目录内
请输入安装目录 [默认: /usr/local/mysql]: /mnt/application/mysql
[INFO] MySQL 安装目录: /mnt/application/mysql
[INFO] 数据目录: /mnt/application/mysql/data
========== 防火墙与 SELinux 配置 ==========
是否关闭防火墙 (firewalld)?[y/N]: y
[INFO] 防火墙已关闭并禁用
是否关闭 SELinux?[y/N]: y
[INFO] SELinux 已临时关闭并设置为永久禁用
========== 检测旧版 MySQL / MariaDB ==========
[INFO] 未检测到旧版 MySQL/MariaDB
========== 下载 MySQL 8.0.45 ==========
[INFO] 正在下载...
438MiB 0:00:37 [11.7MB/s] [==================================>] 100%
[INFO] 下载完成
========== 安装 MySQL ==========
[INFO] 正在解压安装包...
[████████████████████████████████████████] 100%
[INFO] MySQL 文件已安装到 /mnt/application/mysql
========== 生成配置文件 ==========
[INFO] 配置文件已写入 /etc/my.cnf
========== 初始化 MySQL 数据库 ==========
[INFO] 正在执行 mysqld --initialize ...
[INFO] 初始化完成,已获取临时密码
========== 配置系统服务 ==========
[INFO] mysql.server 已复制到 /etc/init.d/mysql
请选择服务管理方式:
[1] /etc/init.d/mysql (SysV init,推荐,兼容性好)
[2] systemctl (systemd,现代系统原生方式)
请选择 [1/2,默认1]: 1
Starting MySQL. SUCCESS!
[INFO] MySQL 服务已通过 /etc/init.d/mysql 启动
[INFO] 已添加开机自启到 /etc/rc.local
========== 设置 MySQL root 密码 ==========
[INFO] 等待 MySQL 服务就绪...
[INFO] MySQL 服务已就绪
请输入新的 root 密码 (直接回车则保留临时密码):
[INFO] root 密码修改成功
========== 验证安装 ==========
[INFO] MySQL 客户端验证通过: mysql Ver 8.0.45 for Linux on x86_64 (MySQL Community Server - GPL)
[INFO] MySQL 服务运行正常
========== 安装完成 ==========
╔═══════════════════════════════════════════════════════╗
║ MySQL 安装摘要 ║
╠═══════════════════════════════════════════════════════╣
║ 版本 MySQL 8.0.45 ║
║ glibc 2.28 ║
║ 安装目录 /mnt/application/mysql ║
║ 数据目录 /mnt/application/mysql/data ║
║ 配置文件 /etc/my.cnf ║
║ 错误日志 /mnt/application/mysql/data/mysql.err ║
║ Socket /tmp/mysql.sock ║
║ 端口 3306 ║
║ init脚本 /etc/init.d/mysql ║
║ ║
║ root 密码 (已设置自定义密码) ║
║ ║
║ 启动 /etc/init.d/mysql start ║
║ 停止 /etc/init.d/mysql stop ║
║ 重启 /etc/init.d/mysql restart ║
║ 状态 /etc/init.d/mysql status ║
║ 登录 mysql -u root -p ║
╚═══════════════════════════════════════════════════════╝
SQL 操作参考:
-- 首次登录后必须执行(修改密码 + 开放远程访问)
ALTER USER 'root'@'localhost' IDENTIFIED BY '新密码';
UPDATE mysql.user SET host='%' WHERE user='root' AND host='localhost';
FLUSH PRIVILEGES;
-- 创建新用户并授权
CREATE USER '用户名'@'%' IDENTIFIED BY '密码';
GRANT ALL PRIVILEGES ON 库名.* TO '用户名'@'%';
FLUSH PRIVILEGES;支持的版本
| glibc 2.28 | glibc 2.17 |
|---|---|
| 8.0.35 ~ 8.0.45 | 8.0.35 ~ 8.0.45 |
脚本会自动检测系统 glibc 版本并选择对应的安装包。glibc >= 2.28 使用 glibc2.28 包,glibc >= 2.17 但 < 2.28 使用 glibc2.17 包。
系统要求
- 操作系统: Linux x86_64 (CentOS 7+, RHEL 7+, Rocky Linux, AlmaLinux 等)
- 权限: root 用户
- 网络: 需要能访问 downloads.mysql.com
- 工具: wget 或 curl,python3(用于摘要对齐,无则自动降级)
源码
点击展开完整脚本源码
#!/bin/bash
set -euo pipefail
# ============================================================
# MySQL 8.0 一键安装脚本
# 支持版本选择、glibc自动检测、自定义安装路径
# 适用于 Linux Generic x86_64 平台
# ============================================================
RED=$'\033[0;31m'
GREEN=$'\033[0;32m'
YELLOW=$'\033[1;33m'
CYAN=$'\033[0;36m'
NC=$'\033[0m'
LOG_FILE="/tmp/mysql_install_$(date +%Y%m%d_%H%M%S).log"
VERSIONS=(8.0.35 8.0.36 8.0.37 8.0.39 8.0.40 8.0.41 8.0.42 8.0.43 8.0.44 8.0.45)
MYSQL_VERSION=""
GLIBC_TAG=""
INSTALL_DIR="/usr/local/mysql"
DATA_DIR=""
TEMP_PASSWORD=""
USER_PASSWORD=""
DOWNLOAD_URL=""
SERVICE_START_CMD=""
log() { echo -e "${GREEN}[INFO]${NC} $1" | tee -a "$LOG_FILE"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1" | tee -a "$LOG_FILE"; }
err() { echo -e "${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE"; }
title() { echo -e "\n${CYAN}========== $1 ==========${NC}" | tee -a "$LOG_FILE"; }
# === 检查root权限 ===
check_root() {
if [ "$(id -u)" -ne 0 ]; then
err "请使用 root 用户运行此脚本"
exit 1
fi
}
# === 版本选择菜单 ===
show_menu() {
title "MySQL 8.0 版本选择"
echo ""
local cols=4
local i=1
for ver in "${VERSIONS[@]}"; do
printf " %-4s%-20s" "[$i]" "$ver"
if (( i % cols == 0 )); then echo ""; fi
i=$((i+1))
done
echo ""
echo ""
while true; do
read -rp "请选择版本编号 [1-${#VERSIONS[@]}]: " choice
if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le "${#VERSIONS[@]}" ]; then
MYSQL_VERSION="${VERSIONS[$((choice-1))]}"
log "已选择 MySQL $MYSQL_VERSION"
break
else
warn "无效输入,请重新选择"
fi
done
}
# === 检测glibc版本 ===
detect_glibc() {
title "检测系统 glibc 版本"
local glibc_ver
glibc_ver=$(ldd --version 2>&1 | head -1 | grep -oP '\d+\.\d+$' || true)
if [ -z "$glibc_ver" ]; then
err "无法检测 glibc 版本"
exit 1
fi
log "当前系统 glibc 版本: $glibc_ver"
local major minor
major=$(echo "$glibc_ver" | cut -d. -f1)
minor=$(echo "$glibc_ver" | cut -d. -f2)
if [ "$major" -gt 2 ] || ([ "$major" -eq 2 ] && [ "$minor" -ge 28 ]); then
GLIBC_TAG="2.28"
elif [ "$major" -eq 2 ] && [ "$minor" -ge 17 ]; then
GLIBC_TAG="2.17"
else
err "系统 glibc 版本过低 ($glibc_ver),最低要求 2.17"
exit 1
fi
DOWNLOAD_URL="https://downloads.mysql.com/archives/get/p/23/file/mysql-${MYSQL_VERSION}-linux-glibc${GLIBC_TAG}-x86_64.tar.xz"
log "将使用 glibc${GLIBC_TAG} 版本的安装包"
log "下载地址: $DOWNLOAD_URL"
}
# === 询问安装路径 ===
ask_install_path() {
title "安装路径配置"
echo -e " 请输入 MySQL 的安装目录,MySQL 文件将直接放在该目录下"
echo -e " ${RED}例如输入 /mnt/application/mysql,则 bin/lib/data 等均在此目录内${NC}"
echo ""
read -rp "请输入安装目录 [默认: /usr/local/mysql]: " custom_path
if [ -n "$custom_path" ]; then
INSTALL_DIR="${custom_path%/}"
fi
DATA_DIR="${INSTALL_DIR}/data"
log "MySQL 安装目录: $INSTALL_DIR"
log "数据目录: $DATA_DIR"
if [ -d "$INSTALL_DIR" ]; then
warn "目录 $INSTALL_DIR 已存在!"
read -rp "是否继续覆盖安装?[y/N]: " confirm
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
err "用户取消安装"
exit 0
fi
fi
}
# === 防火墙和SELinux处理 ===
ask_firewall() {
title "防火墙与 SELinux 配置"
read -rp "是否关闭防火墙 (firewalld)?[y/N]: " fw_choice
if [[ "$fw_choice" =~ ^[Yy]$ ]]; then
systemctl stop firewalld 2>/dev/null && systemctl disable firewalld 2>/dev/null
log "防火墙已关闭并禁用"
else
log "保留防火墙,请确保已开放 MySQL 端口 (默认3306)"
fi
read -rp "是否关闭 SELinux?[y/N]: " se_choice
if [[ "$se_choice" =~ ^[Yy]$ ]]; then
setenforce 0 2>/dev/null || true
sed -i 's/^SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config 2>/dev/null || true
log "SELinux 已临时关闭并设置为永久禁用"
else
log "保留 SELinux,请确保已配置 MySQL 相关策略"
fi
}
# === 卸载旧版MySQL/MariaDB ===
remove_old_mysql() {
title "检测旧版 MySQL / MariaDB"
local found=false
local mariadb_pkgs=$(rpm -qa 2>/dev/null | grep -i mariadb || true)
local mysql_pkgs=$(rpm -qa 2>/dev/null | grep -i mysql | grep -v "install_mysql" || true)
if [ -n "$mariadb_pkgs" ]; then
found=true
warn "检测到已安装的 MariaDB 包:"
echo "$mariadb_pkgs"
fi
if [ -n "$mysql_pkgs" ]; then
found=true
warn "检测到已安装的 MySQL 包:"
echo "$mysql_pkgs"
fi
if [ "$found" = true ]; then
read -rp "是否卸载以上旧版本?[Y/n]: " rm_choice
if [[ ! "$rm_choice" =~ ^[Nn]$ ]]; then
if [ -n "$mariadb_pkgs" ]; then
echo "$mariadb_pkgs" | xargs rpm -e --nodeps 2>/dev/null || true
log "MariaDB 已卸载"
fi
if [ -n "$mysql_pkgs" ]; then
echo "$mysql_pkgs" | xargs rpm -e --nodeps 2>/dev/null || true
log "旧版 MySQL 已卸载"
fi
else
warn "跳过卸载,继续安装(可能产生冲突)"
fi
else
log "未检测到旧版 MySQL/MariaDB"
fi
}
# === 下载安装包 ===
download_package() {
title "下载 MySQL $MYSQL_VERSION"
local pkg_name="mysql-${MYSQL_VERSION}-linux-glibc${GLIBC_TAG}-x86_64.tar.xz"
local tmp_dir="/usr/src"
if [ -f "${tmp_dir}/${pkg_name}" ]; then
log "安装包已存在: ${tmp_dir}/${pkg_name},跳过下载"
else
log "正在下载: $DOWNLOAD_URL"
if command -v wget &>/dev/null; then
if wget --help 2>&1 | grep -q -- '--show-progress'; then
wget --show-progress -O "${tmp_dir}/${pkg_name}" "$DOWNLOAD_URL" || {
err "下载失败,请检查网络、证书或下载地址: $DOWNLOAD_URL"
exit 1
}
else
wget -O "${tmp_dir}/${pkg_name}" "$DOWNLOAD_URL" || {
err "下载失败,请检查网络、证书或下载地址: $DOWNLOAD_URL"
exit 1
}
fi
elif command -v curl &>/dev/null; then
curl -fL --progress-bar -o "${tmp_dir}/${pkg_name}" "$DOWNLOAD_URL" || {
err "下载失败,请检查网络、证书或下载地址: $DOWNLOAD_URL"
exit 1
}
else
err "未找到 wget 或 curl,无法下载"
exit 1
fi
log "下载完成"
fi
}
# === 解压安装 ===
install_mysql() {
title "安装 MySQL"
local pkg_name="mysql-${MYSQL_VERSION}-linux-glibc${GLIBC_TAG}-x86_64.tar.xz"
local tmp_dir="/usr/src"
log "正在解压安装包..."
local pkg_path="${tmp_dir}/${pkg_name}"
local pkg_size
pkg_size=$(stat -c%s "$pkg_path" 2>/dev/null || echo 0)
if command -v pv &>/dev/null && [ "$pkg_size" -gt 0 ]; then
pv -s "$pkg_size" "$pkg_path" | tar xJ -C "${tmp_dir}/"
else
# 用 tar + 后台进度模拟(基于已解压文件数)
tar xf "$pkg_path" -C "${tmp_dir}/" &
local tar_pid=$!
local total_files
total_files=$(tar tf "$pkg_path" 2>/dev/null | wc -l || echo 1000)
local extracted_count=0
while kill -0 "$tar_pid" 2>/dev/null; do
extracted_count=$(find "${tmp_dir}/mysql-${MYSQL_VERSION}-linux-glibc${GLIBC_TAG}-x86_64" -type f 2>/dev/null | wc -l || echo 0)
local pct=$(( extracted_count * 100 / total_files ))
[ "$pct" -gt 100 ] && pct=99
local filled=$(( pct * 40 / 100 ))
local bar=""
for ((j=0; j<filled; j++)); do bar+="█"; done
for ((j=filled; j<40; j++)); do bar+="░"; done
printf "\r [%s] %3d%%" "$bar" "$pct"
sleep 0.5
done
wait "$tar_pid"
printf "\r [%s] %3d%%\n" "$(printf '█%.0s' {1..40})" 100
fi
local extracted_dir="${tmp_dir}/mysql-${MYSQL_VERSION}-linux-glibc${GLIBC_TAG}-x86_64"
if [ ! -d "$extracted_dir" ]; then
err "解压失败,目录不存在: $extracted_dir"
exit 1
fi
if [ -d "$INSTALL_DIR" ] && [ "$(ls -A "$INSTALL_DIR" 2>/dev/null)" ]; then
rm -rf "$INSTALL_DIR"
fi
mkdir -p "$(dirname "$INSTALL_DIR")"
mv "$extracted_dir" "$INSTALL_DIR"
if ! getent group mysql &>/dev/null; then
groupadd -r mysql
fi
if ! id mysql &>/dev/null; then
useradd -r -g mysql -s /bin/false -M mysql
fi
mkdir -p "$DATA_DIR"
chown -R mysql:mysql "$INSTALL_DIR"
log "MySQL 文件已安装到 $INSTALL_DIR"
}
# === 写入配置文件 ===
write_config() {
title "生成配置文件"
cat > /etc/my.cnf << EOF
[mysqld]
basedir=${INSTALL_DIR}
datadir=${DATA_DIR}
pid-file=${DATA_DIR}/mysqld.pid
log-error=${DATA_DIR}/mysql.err
socket=/tmp/mysql.sock
port=3306
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
default_authentication_plugin=mysql_native_password
max_connections=200
lower_case_table_names=1
[client]
socket=/tmp/mysql.sock
default-character-set=utf8mb4
EOF
log "配置文件已写入 /etc/my.cnf"
}
# === 初始化数据库 ===
init_database() {
title "初始化 MySQL 数据库"
log "正在执行 mysqld --initialize ..."
"${INSTALL_DIR}/bin/mysqld" --initialize --user=mysql \
--basedir="$INSTALL_DIR" --datadir="$DATA_DIR" 2>&1 | tee -a "$LOG_FILE"
TEMP_PASSWORD=$(grep 'temporary password' "${DATA_DIR}/mysql.err" | awk '{print $NF}' || true)
if [ -z "$TEMP_PASSWORD" ]; then
warn "未能从日志中提取临时密码,请检查 ${DATA_DIR}/mysql.err"
else
log "初始化完成,已获取临时密码"
fi
}
# === 配置系统服务和环境变量 ===
setup_service() {
title "配置系统服务"
# 复制 mysql.server 到 /etc/init.d/
local init_script="${INSTALL_DIR}/support-files/mysql.server"
if [ ! -f "$init_script" ]; then
err "未找到 mysql.server: $init_script"
exit 1
fi
# 始终复制 mysql.server 到 /etc/init.d/
mkdir -p /etc/init.d
cp "$init_script" /etc/init.d/mysql
chmod +x /etc/init.d/mysql
log "mysql.server 已复制到 /etc/init.d/mysql"
# 让用户选择服务管理方式
echo ""
echo -e " 请选择服务管理方式:"
echo -e " ${CYAN}[1]${NC} /etc/init.d/mysql (SysV init,推荐,兼容性好)"
echo -e " ${CYAN}[2]${NC} systemctl (systemd,现代系统原生方式)"
echo ""
read -rp "请选择 [1/2,默认1]: " svc_choice
svc_choice="${svc_choice:-1}"
SERVICE_START_CMD=""
if [[ "$svc_choice" == "2" ]]; then
# 写入 systemd unit 文件
cat > /etc/systemd/system/mysql.service << EOF
[Unit]
Description=MySQL 8.0 Server
After=network.target
[Service]
Type=simple
User=mysql
Group=mysql
WorkingDirectory=${INSTALL_DIR}
ExecStart=${INSTALL_DIR}/bin/mysqld --defaults-file=/etc/my.cnf
ExecStop=${INSTALL_DIR}/bin/mysqladmin -uroot shutdown
Restart=on-failure
RestartSec=5
LimitNOFILE=65535
LimitNPROC=65535
TimeoutStartSec=300
TimeoutStopSec=60
PrivateTmp=false
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable mysql
systemctl start mysql
SERVICE_START_CMD="systemctl start mysql"
log "MySQL 服务已通过 systemd 启动并设置为开机自启"
else
# SysV init 方式,直接用 /etc/init.d/mysql,不走 systemd
/etc/init.d/mysql start
SERVICE_START_CMD="/etc/init.d/mysql start"
log "MySQL 服务已通过 /etc/init.d/mysql 启动"
# 开机自启:写入 rc.local
local rc_local="/etc/rc.local"
if [ ! -f "$rc_local" ]; then
echo '#!/bin/bash' > "$rc_local"
chmod +x "$rc_local"
fi
if ! grep -q "/etc/init.d/mysql start" "$rc_local" 2>/dev/null; then
if grep -q "^exit 0" "$rc_local" 2>/dev/null; then
sed -i '/^exit 0/i /etc/init.d/mysql start' "$rc_local"
else
echo "/etc/init.d/mysql start" >> "$rc_local"
fi
chmod +x "$rc_local"
log "已添加开机自启到 $rc_local"
fi
fi
# 环境变量
if ! grep -q "${INSTALL_DIR}/bin" /etc/profile 2>/dev/null; then
echo "export PATH=\$PATH:${INSTALL_DIR}/bin" >> /etc/profile
log "环境变量已添加到 /etc/profile"
fi
export PATH="$PATH:${INSTALL_DIR}/bin"
# 软链接到 /usr/local/bin
ln -sf "${INSTALL_DIR}/bin/"* /usr/local/bin/ 2>/dev/null || true
log "已创建 bin/* 软链接到 /usr/local/bin/"
}
# === 设置root密码 ===
set_password() {
title "设置 MySQL root 密码"
# 等待 MySQL 就绪(最多30秒)
log "等待 MySQL 服务就绪..."
local retry=0
until "${INSTALL_DIR}/bin/mysqladmin" -u root -p"${TEMP_PASSWORD}" ping --connect-timeout=2 &>/dev/null; do
retry=$((retry+1))
if [ "$retry" -ge 15 ]; then
err "MySQL 服务未就绪,请检查日志: ${DATA_DIR}/mysql.err"
exit 1
fi
sleep 2
done
log "MySQL 服务已就绪"
echo ""
read -rsp "请输入新的 root 密码 (直接回车则保留临时密码): " USER_PASSWORD
echo ""
if [ -z "$USER_PASSWORD" ]; then
warn "未设置新密码,将保留初始化生成的临时密码"
return
fi
log "正在修改 root 密码..."
if "${INSTALL_DIR}/bin/mysql" --connect-expired-password -u root -p"${TEMP_PASSWORD}" \
-e "ALTER USER 'root'@'localhost' IDENTIFIED BY '${USER_PASSWORD}'; FLUSH PRIVILEGES;" 2>>"$LOG_FILE"; then
log "root 密码修改成功"
TEMP_PASSWORD=""
else
err "密码修改失败,请手动修改"
warn "临时密码: $TEMP_PASSWORD"
fi
}
# === 验证安装 ===
verify_install() {
title "验证安装"
local mysql_ver
mysql_ver=$("${INSTALL_DIR}/bin/mysql" -V 2>/dev/null || true)
if [ -n "$mysql_ver" ]; then
log "MySQL 客户端验证通过: $mysql_ver"
else
err "MySQL 客户端验证失败"
exit 1
fi
if systemctl is-active mysql &>/dev/null || /etc/init.d/mysql status &>/dev/null; then
log "MySQL 服务运行正常"
else
err "MySQL 服务未运行,请检查日志: ${DATA_DIR}/mysql.err"
exit 1
fi
}
# === 输出安装摘要 ===
print_summary() {
title "安装完成"
local pw_line=""
if [ -n "$TEMP_PASSWORD" ]; then
pw_line="$TEMP_PASSWORD"
elif [ -n "$USER_PASSWORD" ]; then
pw_line="(已设置自定义密码)"
else
pw_line="(未知,请查看 ${DATA_DIR}/mysql.err)"
fi
local start_cmd="${SERVICE_START_CMD:-/etc/init.d/mysql start}"
local stop_cmd="${start_cmd/start/stop}"
local restart_cmd="${start_cmd/start/restart}"
local status_cmd="${start_cmd/start/status}"
# 计算字符串显示宽度(中文/全角算2,ASCII算1)
_dw() {
[ -z "$1" ] && echo 0 && return
if command -v python3 &>/dev/null; then
python3 -c "
import unicodedata, sys
s = sys.argv[1]
print(sum(2 if unicodedata.east_asian_width(c) in ('W','F') else 1 for c in s))
" "$1"
else
# fallback: bash逐字符,非ASCII一律算2
local s="$1" n=0 i=0
while [ $i -lt ${#s} ]; do
local cp
cp=$(printf '%d' "'${s:$i:1}" 2>/dev/null || echo 32)
[ "$cp" -gt 127 ] && n=$((n+2)) || n=$((n+1))
i=$((i+1))
done
echo $n
fi
}
# 收集所有内容行,动态计算最大宽度
local rows=(
" 版本 MySQL ${MYSQL_VERSION}"
" glibc ${GLIBC_TAG}"
" 安装目录 ${INSTALL_DIR}"
" 数据目录 ${DATA_DIR}"
" 配置文件 /etc/my.cnf"
" 错误日志 ${DATA_DIR}/mysql.err"
" Socket /tmp/mysql.sock"
" 端口 3306"
" init脚本 /etc/init.d/mysql"
""
" root 密码 ${pw_line}"
" [!] 首次登录后请立即修改密码!"
""
" 启动 ${start_cmd}"
" 停止 ${stop_cmd}"
" 重启 ${restart_cmd}"
" 状态 ${status_cmd}"
" 登录 mysql -u root -p"
)
local W=0
for r in "${rows[@]}"; do
local dw; dw=$(_dw "$r")
[ "$dw" -gt "$W" ] && W=$dw
done
W=$((W + 2)) # 右侧留2格空白
local title_text="MySQL 安装摘要"
local title_dw; title_dw=$(_dw "$title_text")
local title_pad=$(( (W - title_dw) / 2 ))
_row() {
local content="$1"
local dw; dw=$(_dw "$content")
local pad=$(( W - dw ))
[ "$pad" -lt 0 ] && pad=0
printf "${GREEN}║${NC}%s%${pad}s${GREEN}║${NC}\n" "$content" ""
}
# 带颜色的行:visible 用于计算宽度,colored 用于显示(含颜色码)
_row_c() {
local visible="$1"
local colored="$2"
local dw; dw=$(_dw "$visible")
local pad=$(( W - dw ))
[ "$pad" -lt 0 ] && pad=0
echo -e "${GREEN}║${NC}${colored}$(printf '%*s' $pad '')${GREEN}║${NC}"
}
echo ""
echo -e "${GREEN}╔$(printf '═%.0s' $(seq 1 $W))╗${NC}"
printf "${GREEN}║${NC}%${title_pad}s%s%$(( W - title_dw - title_pad ))s${GREEN}║${NC}\n" "" "$title_text" ""
echo -e "${GREEN}╠$(printf '═%.0s' $(seq 1 $W))╣${NC}"
for r in "${rows[@]}"; do
if [ "$r" = " root 密码 ${pw_line}" ]; then
_row_c " root 密码 ${pw_line}" " ${YELLOW}root 密码 ${pw_line}${NC}"
elif [ "$r" = " ⚠ 首次登录后请立即修改密码!" ]; then
_row_c " ⚠ 首次登录后请立即修改密码!" " ${YELLOW}⚠ 首次登录后请立即修改密码!${NC}"
else
_row "$r"
fi
done
echo -e "${GREEN}╚$(printf '═%.0s' $(seq 1 $W))╝${NC}"
# SQL 提示(合并首次执行 + 常用操作)
echo ""
echo -e "${YELLOW}SQL 操作参考:${NC}"
echo ""
echo -e "${RED} -- 首次登录后必须执行(修改密码 + 开放远程访问)${NC}"
echo -e "${RED} ALTER USER 'root'@'localhost' IDENTIFIED BY '新密码';${NC}"
echo -e "${RED} UPDATE mysql.user SET host='%' WHERE user='root' AND host='localhost';${NC}"
echo -e "${RED} FLUSH PRIVILEGES;${NC}"
echo ""
echo -e "${CYAN} -- 创建新用户并授权${NC}"
echo -e " CREATE USER '用户名'@'%' IDENTIFIED BY '密码';"
echo -e " GRANT ALL PRIVILEGES ON 库名.* TO '用户名'@'%';"
echo -e " FLUSH PRIVILEGES;"
echo ""
log "安装日志: $LOG_FILE"
}
# === 卸载 MySQL ===
uninstall_mysql() {
title "卸载 MySQL"
# 从 /etc/my.cnf 读取安装信息
local uninstall_dir uninstall_data
uninstall_dir=$(grep -E '^basedir\s*=' /etc/my.cnf 2>/dev/null | cut -d= -f2 | tr -d ' ' || true)
uninstall_data=$(grep -E '^datadir\s*=' /etc/my.cnf 2>/dev/null | cut -d= -f2 | tr -d ' ' || true)
# 检测 MySQL 是否存在
local mysql_found=false
[ -f /etc/my.cnf ] && mysql_found=true
[ -f /etc/init.d/mysql ] && mysql_found=true
[ -f /etc/systemd/system/mysql.service ] && mysql_found=true
systemctl is-active mysql &>/dev/null 2>&1 && mysql_found=true
if [ "$mysql_found" = false ]; then
warn "未检测到 MySQL 安装(/etc/my.cnf、/etc/init.d/mysql、systemd unit 均不存在)"
echo ""
echo -e " ${CYAN}[1]${NC} 手动指定目录继续卸载"
echo -e " ${CYAN}[0]${NC} 返回主菜单"
echo ""
read -rp "请选择 [1/0]: " not_found_choice
if [[ "$not_found_choice" != "1" ]]; then
return
fi
fi
# 未检测到则让用户输入
if [ -z "$uninstall_dir" ]; then
[ "$mysql_found" = false ] || warn "未从 /etc/my.cnf 检测到安装目录"
read -rp "请输入 MySQL 安装目录 [默认: /usr/local/mysql]: " uninstall_dir
uninstall_dir="${uninstall_dir:-/usr/local/mysql}"
fi
if [ -z "$uninstall_data" ]; then
uninstall_data="${uninstall_dir}/data"
fi
log "安装目录: $uninstall_dir"
log "数据目录: $uninstall_data"
echo ""
echo -e "${RED}即将卸载 MySQL,此操作不可逆!${NC}"
read -rp "确认继续卸载?[y/N]: " confirm
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
log "用户取消卸载"
exit 0
fi
# 停止服务
title "停止 MySQL 服务"
if systemctl is-active mysql &>/dev/null 2>&1; then
systemctl stop mysql && log "systemctl 已停止 MySQL"
systemctl disable mysql 2>/dev/null || true
fi
if [ -f /etc/init.d/mysql ]; then
/etc/init.d/mysql stop 2>/dev/null || true
log "/etc/init.d/mysql 已停止"
fi
# 删除数据目录(二次确认)
title "删除数据目录"
if [ -d "$uninstall_data" ]; then
echo -e "${RED}[高危] 数据目录 $uninstall_data 包含所有数据库文件,删除后无法恢复!${NC}"
read -rp "是否删除数据目录?[y/N]: " del_data
if [[ "$del_data" =~ ^[Yy]$ ]]; then
read -rp "再次确认删除数据目录 $uninstall_data?[yes/N]: " del_data2
if [ "$del_data2" = "yes" ]; then
rm -rf "$uninstall_data"
log "数据目录已删除: $uninstall_data"
else
warn "已跳过数据目录删除"
fi
else
warn "已跳过数据目录删除,数据保留在: $uninstall_data"
fi
else
log "数据目录不存在,跳过"
fi
# 删除安装目录
title "删除安装目录"
if [ -d "$uninstall_dir" ]; then
rm -rf "$uninstall_dir"
log "安装目录已删除: $uninstall_dir"
else
warn "安装目录不存在: $uninstall_dir"
fi
# 删除配置文件
title "清理配置与服务文件"
[ -f /etc/my.cnf ] && rm -f /etc/my.cnf && log "已删除 /etc/my.cnf"
[ -f /etc/init.d/mysql ] && rm -f /etc/init.d/mysql && log "已删除 /etc/init.d/mysql"
[ -f /etc/systemd/system/mysql.service ] && {
rm -f /etc/systemd/system/mysql.service
systemctl daemon-reload 2>/dev/null || true
log "已删除 /etc/systemd/system/mysql.service"
}
# 删除 /usr/local/bin/ 软链接
for f in mysql mysqldump mysqladmin mysqlcheck mysqlimport mysqlshow; do
if [ -L "/usr/local/bin/$f" ]; then
rm -f "/usr/local/bin/$f"
log "已删除软链接: /usr/local/bin/$f"
fi
done
# 删除 mysql 用户和组
id mysql &>/dev/null && userdel mysql 2>/dev/null && log "已删除 mysql 用户"
getent group mysql &>/dev/null && groupdel mysql 2>/dev/null && log "已删除 mysql 用户组"
# 清理 /etc/rc.local
if [ -f /etc/rc.local ] && grep -q "/etc/init.d/mysql start" /etc/rc.local 2>/dev/null; then
sed -i '/\/etc\/init\.d\/mysql start/d' /etc/rc.local
log "已清理 /etc/rc.local 中的开机自启条目"
fi
# 清理 /etc/profile 中的 PATH
if grep -q "${uninstall_dir}/bin" /etc/profile 2>/dev/null; then
sed -i "\|${uninstall_dir}/bin|d" /etc/profile
log "已清理 /etc/profile 中的 PATH 条目"
fi
title "卸载完成"
log "MySQL 已完全卸载"
[ -d "$uninstall_data" ] && warn "数据目录已保留: $uninstall_data"
}
# === 主流程 ===
main() {
echo -e "${CYAN}"
echo " ╔═══════════════════════════════════════════╗"
echo " ║ MySQL 8.0 管理脚本 ║"
echo " ║ 适用于 Linux Generic x86_64 ║"
echo " ╚═══════════════════════════════════════════╝"
echo -e "${NC}"
check_root
while true; do
echo ""
echo -e " 请选择操作:"
echo -e " ${CYAN}[1]${NC} 安装 MySQL 8.0"
echo -e " ${CYAN}[2]${NC} 卸载 MySQL"
echo -e " ${CYAN}[0]${NC} 退出"
echo ""
read -rp "请输入选择 [1/2/0]: " action
case "$action" in
1)
show_menu
detect_glibc
ask_install_path
ask_firewall
remove_old_mysql
download_package
install_mysql
write_config
init_database
setup_service
set_password
verify_install
print_summary
break
;;
2)
uninstall_mysql
;;
0)
log "已退出"
exit 0
;;
*)
warn "无效输入,请重新选择"
;;
esac
done
}
main "$@"