#!/bin/bash # ============================================ # MySQL CLI 工具安装脚本(官方 MySQL 客户端) # 从 Oracle MySQL 官方仓库安装真正的 MySQL 客户端 # 替代 Debian Trixie 默认的 mariadb-client # ============================================ set -e MYSQL_VERSION="8.4.4" BIN_INSTALL_DIR="/usr/local/bin" # 颜色定义 GREEN='\033[0;32m' YELLOW='\033[1;33m' RED='\033[0;31m' NC='\033[0m' info() { echo -e "${GREEN}[info]${NC} $1"; } warn() { echo -e "${YELLOW}[warn]${NC} $1"; } error() { echo -e "${RED}[error]${NC} $1"; } # ------------------------------------------ # 检查是否已安装正确的 MySQL 客户端 # ------------------------------------------ check_mysql_client() { if command -v mysql >/dev/null 2>&1; then local ver ver=$(mysql --version 2>&1) if echo "$ver" | grep -qi "mariadb"; then warn "当前是 MariaDB 客户端: $ver" warn "(MariaDB 用 --ssl=0,MySQL 用 --ssl-mode=DISABLED)" warn "将替换为官方 MySQL 客户端..." return 1 fi if mysql --help 2>&1 | grep -q "ssl-mode"; then info "✅ 官方 MySQL 客户端已安装: $ver" return 0 else warn "MySQL 客户端版本过旧,不支持 --ssl-mode" return 1 fi fi return 1 } # ------------------------------------------ # 清理磁盘空间 # ------------------------------------------ cleanup_space() { local needed_kb=${1:-51200} # 默认需要 50MB local avail_kb avail_kb=$(df /var/cache/apt/archives/ 2>/dev/null | awk 'NR==2 {print $4}') # 单位 KB if [ -z "$avail_kb" ] || [ "$avail_kb" -lt "$needed_kb" ]; then warn "磁盘空间不足,尝试清理..." apt-get clean 2>/dev/null || true rm -rf /tmp/* /var/tmp/* 2>/dev/null || true avail_kb=$(df /var/cache/apt/archives/ 2>/dev/null | awk 'NR==2 {print $4}') if [ -n "$avail_kb" ] && [ "$avail_kb" -lt "$needed_kb" ]; then warn "清理后空间仍不足 ($((avail_kb / 1024)) MB 可用)" return 1 fi fi return 0 } # ------------------------------------------ # 方式一:从 MySQL 官方 APT 仓库安装 # ------------------------------------------ install_from_apt() { info "方式一:从 MySQL 官方 APT 仓库安装..." # 检查磁盘空间 if ! cleanup_space; then warn "磁盘空间不足,跳过 APT 安装方式" return 1 fi # 确保 wget 可用 if ! command -v wget >/dev/null 2>&1; then apt-get update -qq && apt-get install -y -qq wget 2>/dev/null || { warn "wget 不可用,跳过 APT 安装方式" return 1 } fi local mysql_repo_deb="/tmp/mysql-apt-config.deb" # 清理旧配置 rm -f /etc/apt/sources.list.d/mysql.list /etc/apt/sources.list.d/mysql-*.list # 下载 MySQL APT 配置包 info "下载 MySQL APT 配置包..." wget -q "https://repo.mysql.com/mysql-apt-config_0.8.33-1_all.deb" -O "$mysql_repo_deb" || { warn "下载 MySQL APT 配置包失败" return 1 } # 非交互式安装配置包 DEBIAN_FRONTEND=noninteractive dpkg -i "$mysql_repo_deb" 2>/dev/null || true # 修正 sources.list 中的 codename # MySQL APT 仓库不支持 Debian Trixie,强制使用 bookworm if [ -f /etc/apt/sources.list.d/mysql.list ]; then sed -i "s/$(lsb_release -sc 2>/dev/null || echo 'unknown')/bookworm/g" \ /etc/apt/sources.list.d/mysql.list 2>/dev/null || true fi # 更新 APT 缓存 info "更新 APT 缓存..." apt-get update -qq 2>/dev/null || { warn "APT 更新失败" return 1 } # 安装 MySQL 客户端(优先 mysql-client,备用 mysql-community-client) info "安装 mysql-client..." apt-get install -y -qq mysql-client 2>/dev/null || { warn "mysql-client 安装失败,尝试 mysql-community-client..." apt-get install -y -qq mysql-community-client 2>/dev/null || { warn "APT 安装 MySQL 客户端失败" return 1 } } return 0 } # ------------------------------------------ # 方式二:直接下载 MySQL 客户端二进制 # ------------------------------------------ install_from_binary() { info "方式二:直接下载 MySQL 客户端二进制..." # 检查磁盘空间(二进制包大约 300MB 解压,但只需要 2 个文件 ~5MB) local needed_kb=102400 # 100MB for download + extract local avail_kb avail_kb=$(df /tmp 2>/dev/null | awk 'NR==2 {print $4}') if [ -n "$avail_kb" ] && [ "$avail_kb" -lt "$needed_kb" ]; then error "磁盘空间不足 ($((avail_kb / 1024)) MB 可用,需要 ~100 MB)" return 1 fi # 确保 wget 可用 if ! command -v wget >/dev/null 2>&1; then error "wget 不可用,无法下载" return 1 fi local tmp_dir tmp_dir=$(mktemp -d) local tarball="${tmp_dir}/mysql-${MYSQL_VERSION}-linux-glibc2.28-x86_64.tar.xz" local base_url="https://dev.mysql.com/get/Downloads/MySQL-8.4" info "下载 MySQL ${MYSQL_VERSION} 客户端 (约 300MB)..." wget -q --show-progress "${base_url}/$(basename "$tarball")" -O "$tarball" || { error "下载失败" rm -rf "$tmp_dir" return 1 } # 确保 xz 解压工具可用(.tar.xz 格式需要) if ! command -v xz >/dev/null 2>&1; then warn "xz 未安装,尝试安装 xz-utils..." apt-get install -y -qq xz-utils 2>/dev/null || { warn "apt 安装 xz-utils 失败,改用 Python lzma 模块解压..." # Python 内置了 lzma 模块,无需额外安装 } fi info "解压(仅提取 mysql 和 mysqladmin)..." local mysql_bin mysqladmin_bin local extract_dir="${tmp_dir}/extract" mkdir -p "$extract_dir" if command -v xz >/dev/null 2>&1; then # 有 xz:直接用 tar 解压 mysql_bin=$(tar -tJf "$tarball" 2>/dev/null | grep -E 'bin/mysql$' | head -1) mysqladmin_bin=$(tar -tJf "$tarball" 2>/dev/null | grep -E 'bin/mysqladmin$' | head -1) if [ -n "$mysql_bin" ] && [ -n "$mysqladmin_bin" ]; then tar -xJf "$tarball" -C "$extract_dir" "$mysql_bin" "$mysqladmin_bin" else tar -xJf "$tarball" -C "$extract_dir" fi else # 没有 xz:用 Python lzma 模块解压 info "使用 Python lzma 模块解压..." python3 -c " import lzma, tarfile, sys, os tarball = '$tarball' extract_to = '$extract_dir' with lzma.open(tarball) as f: with tarfile.open(fileobj=f, mode='r|') as tar: for member in tar: # 只提取 bin/mysql 和 bin/mysqladmin if member.name.endswith('/bin/mysql') or member.name.endswith('/bin/mysqladmin'): tar.extract(member, path=extract_to) " 2>/dev/null || { # 如果 Python 也失败,解压全部 info "Python 选择性提取失败,解压全部文件..." python3 -c " import lzma, tarfile tarball = '$tarball' extract_to = '$extract_dir' with lzma.open(tarball) as f: with tarfile.open(fileobj=f, mode='r|') as tar: tar.extractall(path=extract_to) " 2>/dev/null || { error "Python 解压也失败" rm -rf "$tmp_dir" return 1 } } fi # 从解压目录复制二进制文件 local mysql_src mysqladmin_src mysql_src=$(find "$extract_dir" -name 'mysql' -type f -path '*/bin/mysql' | head -1) mysqladmin_src=$(find "$extract_dir" -name 'mysqladmin' -type f -path '*/bin/mysqladmin' | head -1) if [ -n "$mysql_src" ] && [ -n "$mysqladmin_src" ]; then cp "$mysql_src" "${BIN_INSTALL_DIR}/mysql" cp "$mysqladmin_src" "${BIN_INSTALL_DIR}/mysqladmin" else # 最后尝试:直接找任何 mysql 二进制 mysql_src=$(find "$extract_dir" -name 'mysql' -type f | head -1) mysqladmin_src=$(find "$extract_dir" -name 'mysqladmin' -type f | head -1) if [ -z "$mysql_src" ]; then error "解压后找不到 mysql 二进制文件" rm -rf "$tmp_dir" return 1 fi cp "$mysql_src" "${BIN_INSTALL_DIR}/mysql" [ -n "$mysqladmin_src" ] && cp "$mysqladmin_src" "${BIN_INSTALL_DIR}/mysqladmin" fi chmod +x "${BIN_INSTALL_DIR}/mysql" "${BIN_INSTALL_DIR}/mysqladmin" rm -rf "$tmp_dir" info "二进制安装完成" return 0 } # ------------------------------------------ # 方式三:提示手动安装 # ------------------------------------------ manual_install_hint() { echo "" warn "============================================" warn " 自动安装均失败,请手动下载安装:" warn "============================================" echo "" echo " 1. 进入容器: docker compose exec dev bash" echo "" echo " 2. 下载二进制:" echo " wget https://dev.mysql.com/get/Downloads/MySQL-8.4/mysql-${MYSQL_VERSION}-linux-glibc2.28-x86_64.tar.xz" echo "" echo " 3. 解压并复制:" echo " tar -xf mysql-${MYSQL_VERSION}-linux-glibc2.28-x86_64.tar.xz \\" echo " --strip-components 1 -C /usr/local/bin \\" echo " mysql-${MYSQL_VERSION}-linux-glibc2.28-x86_64/bin/mysql \\" echo " mysql-${MYSQL_VERSION}-linux-glibc2.28-x86_64/bin/mysqladmin" echo "" echo " 4. 验证:" echo " mysql --version" echo " # 应输出: mysql Ver 8.4.4 for Linux on x86_64 ..." echo "" echo " 5. 连接测试:" echo " mysql -h mysql -P 3306 -u root -proot --ssl-mode=DISABLED" echo "" } # ========================================== # 主流程 # ========================================== echo "" echo "======================================" echo " MySQL CLI 安装 (官方客户端)" echo "======================================" echo "" # 步骤 1:检查是否已安装正确的版本 if check_mysql_client; then exit 0 fi # 步骤 2:清理旧版 MariaDB 客户端(确保后续 mysql 命令走新版本) if dpkg -l mariadb-client 2>/dev/null | grep -q "^ii"; then info "移除旧版 MariaDB 客户端..." apt-get remove -y -qq mariadb-client mariadb-client-core 2>/dev/null || true fi # 步骤 3:优先从官方 APT 仓库安装 if install_from_apt; then info "✅ 方式一成功:从 APT 仓库安装" else warn "方式一失败,尝试方式二(二进制下载)..." if install_from_binary; then info "✅ 方式二成功:从二进制下载安装" else error "❌ 所有安装方案均失败" manual_install_hint exit 1 fi fi # 步骤 4:最终验证 echo "" if command -v mysql >/dev/null 2>&1; then mysql_ver=$(mysql --version) info "✅ MySQL 客户端已就绪" info "📊 版本: $mysql_ver" echo "" echo "📋 使用说明:" echo " 连接命令: mysql -h <主机> -P <端口> -u <用户> -p<密码> --ssl-mode=DISABLED" echo " Docker 内连接: mysql -h mysql -P 3306 -u root -proot --ssl-mode=DISABLED" echo "" else error "❌ MySQL 客户端安装失败(未知错误)" exit 1 fi