CentOS 7 源码编译安装 Python 3.11 完整教程

CentOS 7 源码编译安装 Python 3.11 完整教程

脚本二测试通过:

资源部下载链接:

https://1816440419.share.123pan.cn/123pan/9QRqVv-XHvG


适用环境

  • 系统:CentOS 7(初始系统,未更新 yum)
  • 网络:可连接外网
  • 目标:编译安装 Python 3.11.9,解决 SQLite 3.8.3+ 的版本要求,不影响系统自带的 Python 2.7

一、更换国内 Yum 源

CentOS 7 官方源已停止维护且速度慢,先切换到阿里云镜像。

bash 复制代码
# 1. 备份系统默认源
sudo mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup

# 2. 下载阿里云 CentOS 7 源(初始系统无 wget,用 curl)
sudo curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo

# 3. 清理并重建缓存
sudo yum clean all
sudo yum makecache

二、安装 EPEL 源并切换国内镜像

Python 3.11 需要 OpenSSL 1.1+,这在 EPEL 源里。

bash 复制代码
# 1. 安装 EPEL 源
sudo yum install -y epel-release

# 2. 备份并替换为阿里云 EPEL 镜像
sudo mv /etc/yum.repos.d/epel.repo /etc/yum.repos.d/epel.repo.backup 2>/dev/null
sudo mv /etc/yum.repos.d/epel-testing.repo /etc/yum.repos.d/epel-testing.repo.backup 2>/dev/null
sudo curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo

# 3. 修正 repo 文件,强制使用 baseurl(避免残留国外 CDN 地址)
sudo sed -i 's/mirrorlist=/#mirrorlist=/g' /etc/yum.repos.d/epel.repo
sudo sed -i 's|#baseurl=http://download.fedoraproject.org/pub/epel|baseurl=http://mirrors.aliyun.com/epel|g' /etc/yum.repos.d/epel.repo

# 4. 彻底清理 EPEL 旧缓存
sudo yum clean all
sudo rm -rf /var/cache/yum/x86_64/7/epel
sudo yum makecache

三、安装编译依赖

bash 复制代码
# 1. 处理 Development Tools 组安装
sudo yum groups mark install "Development Tools"
sudo yum groups mark convert "Development Tools"
sudo yum groupinstall -y "Development Tools"

# 2. 安装基础编译依赖
sudo yum install -y gcc zlib-devel bzip2-devel libffi-devel readline-devel

# 3. 安装 SQLite 编译依赖(用于后续升级)
sudo yum install -y sqlite-devel

# 4. 安装 OpenSSL 1.1(Python 3.11 的关键依赖)
sudo yum install -y openssl-devel openssl11 openssl11-devel

# 5. 安装缺失的可选模块依赖
sudo yum install -y xz-devel libuuid-devel gdbm-devel

# 6. 安装 wget(后续下载用)
sudo yum install -y wget

依赖说明

  • sqlite-devel:虽然系统版本低,但编译时需要头文件,后续通过编译新版 SQLite 解决版本问题
  • openssl11-devel:Python 3.11 要求 OpenSSL 1.1.1+
  • xz-devel:提供 _lzma 模块支持
  • libuuid-devel:提供 _uuid 模块支持
  • gdbm-devel:提供 _dbm_gdbm 模块支持

四、升级系统 SQLite

CentOS 7 自带的 SQLite 版本为 3.7.17,而 Django 2.2+ 要求 3.8.3+。必须手动升级。

4.1 下载并编译新版 SQLite

bash 复制代码
# 1. 下载 SQLite 源码(以 3.42.0 为例,2023 年的稳定版本)
cd /tmp
wget https://www.sqlite.org/2023/sqlite-autoconf-3420000.tar.gz
tar -xzf sqlite-autoconf-3420000.tar.gz
cd sqlite-autoconf-3420000

# 2. 编译安装到 /usr/local
./configure --prefix=/usr/local
make -j $(nproc)
sudo make install

4.2 替换系统旧版 SQLite

bash 复制代码
# 1. 备份旧版本
sudo mv /usr/bin/sqlite3 /usr/bin/sqlite3.old 2>/dev/null

# 2. 创建新版本软链接
sudo ln -sf /usr/local/bin/sqlite3 /usr/bin/sqlite3

# 3. 配置动态链接库路径
echo "/usr/local/lib" | sudo tee /etc/ld.so.conf.d/sqlite3.conf
sudo ldconfig

4.3 验证升级结果

bash 复制代码
sqlite3 --version

预期输出:3.42.0 2023-05-16 ...


五、下载并编译 Python 3.11

5.1 设置编译环境变量

让编译器找到新版 OpenSSL 和新版 SQLite:

bash 复制代码
export CFLAGS="$(pkg-config --cflags openssl11) -I/usr/local/include"
export LDFLAGS="$(pkg-config --libs openssl11) -L/usr/local/lib"
export LD_RUN_PATH="/usr/local/lib"

环境变量说明

  • CFLAGS:添加 /usr/local/include 让编译器找到新版 SQLite 的头文件
  • LDFLAGS:添加 /usr/local/lib 让链接器找到新版 SQLite 的库文件
  • LD_RUN_PATH:指定运行时动态库搜索路径,避免运行时找不到新版 SQLite 库

5.2 下载 Python 源码

bash 复制代码
cd /tmp
wget https://www.python.org/ftp/python/3.11.9/Python-3.11.9.tgz
tar -xzf Python-3.11.9.tgz
cd Python-3.11.9

5.3 配置、编译、安装

重要提示 :CentOS 7 自带的 GCC 4.8.5 与 --enable-optimizations 参数不兼容,会导致编译失败,因此不能加该参数。

bash 复制代码
./configure \
    --prefix=/usr/local/python311 \
    --enable-loadable-sqlite-extensions

make -j $(nproc)
sudo make altinstall

参数说明

  • --prefix=/usr/local/python311:安装到独立目录,不覆盖系统 Python
  • --enable-loadable-sqlite-extensions:确保 sqlite3 模块可以正常加载扩展
  • altinstall:只创建 python3.11 命令,不动 /usr/bin/python,保证 yum 不受影响
  • -j $(nproc):使用所有 CPU 核心并行编译,加快速度

六、创建软链接并验证

6.1 创建软链接

bash 复制代码
sudo ln -sf /usr/local/python311/bin/python3.11 /usr/bin/python3.11
sudo ln -sf /usr/local/python311/bin/pip3.11 /usr/bin/pip3.11

6.2 验证安装

bash 复制代码
# 验证 Python 版本
python3.11 --version
# 预期输出:Python 3.11.9

# 验证 pip 版本
pip3.11 --version
# 预期输出:pip 24.0 ...

# 验证 SSL 模块
python3.11 -c "import ssl; print(ssl.OPENSSL_VERSION)"
# 预期输出:OpenSSL 1.1.1k ...

# 验证 SQLite 版本(关键!Django 需要 ≥ 3.8.3)
python3.11 -c "import sqlite3; print(sqlite3.sqlite_version)"
# 预期输出:3.42.0

# 验证其他重要模块
python3.11 -c "import lzma, uuid, bz2, ssl, sqlite3; print('所有核心模块正常')"
# 预期输出:所有核心模块正常

七、配置 pip 国内镜像源

推荐使用华为云镜像源加速包下载:

bash 复制代码
pip3.11 config set global.index-url https://repo.huaweicloud.com/repository/pypi/simple

验证配置:

bash 复制代码
pip3.11 config list

其他国内镜像源(可选):

  • 阿里云:https://mirrors.aliyun.com/pypi/simple/
  • 清华:https://pypi.tuna.tsinghua.edu.cn/simple/
  • 中科大:https://pypi.mirrors.ustc.edu.cn/simple/

八、升级 pip(可选)

bash 复制代码
pip3.11 install --upgrade pip

九、重要提醒

9.1 绝对不要替换系统 Python

CentOS 7 的 yum 等核心工具依赖 /usr/bin/python(指向 Python 2.7),使用新版本时请明确输入 python3.11pip3.11

9.2 使用虚拟环境(强烈建议)

安装第三方包时创建虚拟环境,避免污染系统级 Python 和依赖冲突:

bash 复制代码
# 创建虚拟环境
python3.11 -m venv myenv

# 激活虚拟环境
source myenv/bin/activate

# 退出虚拟环境
deactivate

9.3 SQLite 版本问题排查

如果之前已用旧版 SQLite 编译了 Python,即使后来升级了系统 SQLite,Python 仍会使用旧的库。必须重新编译 Python 并设置 LD_RUN_PATH 才能生效。

验证命令:

bash 复制代码
python3.11 -c "import sqlite3; print(sqlite3.sqlite_version)"

如果输出 3.7.17 而不是你安装的新版本,说明 Python 仍在使用旧版 SQLite,需要重新编译。

9.4 常见缺失模块处理

如果运行程序时遇到 ModuleNotFoundError: No module named '_xxx' 的错误,通常是缺少对应的 -devel 包,安装后重新编译 Python 即可:

错误模块 缺失的 devel 包 安装命令
_sqlite3 sqlite-devel sudo yum install -y sqlite-devel
_ssl openssl11-devel sudo yum install -y openssl11-devel
_bz2 bzip2-devel sudo yum install -y bzip2-devel
_lzma xz-devel sudo yum install -y xz-devel
_uuid libuuid-devel sudo yum install -y libuuid-devel
_dbm / _gdbm gdbm-devel sudo yum install -y gdbm-devel

附录一:完整操作命令汇总

以下是所有步骤的纯净版命令(无多余试错):

bash 复制代码
# ========== 第一部分:更换 Yum 源 ==========
sudo mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
sudo curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
sudo yum clean all
sudo yum makecache

# ========== 第二部分:配置 EPEL 源 ==========
sudo yum install -y epel-release
sudo mv /etc/yum.repos.d/epel.repo /etc/yum.repos.d/epel.repo.backup 2>/dev/null
sudo mv /etc/yum.repos.d/epel-testing.repo /etc/yum.repos.d/epel-testing.repo.backup 2>/dev/null
sudo curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
sudo sed -i 's/mirrorlist=/#mirrorlist=/g' /etc/yum.repos.d/epel.repo
sudo sed -i 's|#baseurl=http://download.fedoraproject.org/pub/epel|baseurl=http://mirrors.aliyun.com/epel|g' /etc/yum.repos.d/epel.repo
sudo yum clean all
sudo rm -rf /var/cache/yum/x86_64/7/epel
sudo yum makecache

# ========== 第三部分:安装编译依赖 ==========
sudo yum groups mark install "Development Tools"
sudo yum groups mark convert "Development Tools"
sudo yum groupinstall -y "Development Tools"
sudo yum install -y gcc zlib-devel bzip2-devel libffi-devel readline-devel
sudo yum install -y sqlite-devel xz-devel libuuid-devel gdbm-devel
sudo yum install -y openssl-devel openssl11 openssl11-devel
sudo yum install -y wget

# ========== 第四部分:升级系统 SQLite ==========
cd /tmp
wget https://www.sqlite.org/2023/sqlite-autoconf-3420000.tar.gz
tar -xzf sqlite-autoconf-3420000.tar.gz
cd sqlite-autoconf-3420000
./configure --prefix=/usr/local
make -j $(nproc)
sudo make install
sudo mv /usr/bin/sqlite3 /usr/bin/sqlite3.old 2>/dev/null
sudo ln -sf /usr/local/bin/sqlite3 /usr/bin/sqlite3
echo "/usr/local/lib" | sudo tee /etc/ld.so.conf.d/sqlite3.conf
sudo ldconfig

# ========== 第五部分:编译安装 Python 3.11 ==========
cd /tmp
wget https://www.python.org/ftp/python/3.11.9/Python-3.11.9.tgz
tar -xzf Python-3.11.9.tgz
cd Python-3.11.9
export CFLAGS="$(pkg-config --cflags openssl11) -I/usr/local/include"
export LDFLAGS="$(pkg-config --libs openssl11) -L/usr/local/lib"
export LD_RUN_PATH="/usr/local/lib"
./configure --prefix=/usr/local/python311 --enable-loadable-sqlite-extensions
make -j $(nproc)
sudo make altinstall

# ========== 第六部分:创建软链接并验证 ==========
sudo ln -sf /usr/local/python311/bin/python3.11 /usr/bin/python3.11
sudo ln -sf /usr/local/python311/bin/pip3.11 /usr/bin/pip3.11
python3.11 --version
pip3.11 --version
python3.11 -c "import ssl; print(ssl.OPENSSL_VERSION)"
python3.11 -c "import sqlite3; print(sqlite3.sqlite_version)"

# ========== 第七部分:配置 pip 镜像源 ==========
pip3.11 config set global.index-url https://repo.huaweicloud.com/repository/pypi/simple

附录二:自动化安装脚本

保存以下内容为 install_python311.sh,然后执行 sudo ./install_python311.sh

bash 复制代码
#!/bin/bash
#===============================================================================
# CentOS 7 自动化安装 Python 3.11.9 脚本
# 适用环境:CentOS 7 初始系统,可连接外网
# 功能:
#   1. 更换阿里云 Yum 源和 EPEL 源
#   2. 升级系统 SQLite 到 3.42.0
#   3. 编译安装 Python 3.11.9(含 SSL、SQLite、lzma、uuid 等模块)
#   4. 配置 pip 华为云镜像源
# 使用方法:
#   chmod +x install_python311.sh
#   sudo ./install_python311.sh
#===============================================================================

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 "${GREEN}[INFO]${NC} $1"
}

log_warn() {
    echo -e "${YELLOW}[WARN]${NC} $1"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $1"
    exit 1
}

log_step() {
    echo ""
    echo -e "${BLUE}============================================${NC}"
    echo -e "${BLUE}$1${NC}"
    echo -e "${BLUE}============================================${NC}"
}

# ============================================================================
# 环境检查
# ============================================================================
check_root() {
    if [ "$(id -u)" != "0" ]; then
        log_error "请使用 root 用户或 sudo 执行此脚本"
    fi
    log_info "当前为 root 用户,继续执行..."
}

check_network() {
    log_info "检查网络连接..."
    if ! ping -c 2 -W 3 www.baidu.com > /dev/null 2>&1; then
        log_error "无法连接外网,请检查网络配置"
    fi
    log_info "网络连接正常"
}

# ============================================================================
# 第一部分:更换 Yum 源
# ============================================================================
replace_yum_repo() {
    log_step "第一步:更换阿里云 Yum 源"

    # 备份原配置
    if [ -f /etc/yum.repos.d/CentOS-Base.repo ]; then
        mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
        log_info "已备份原 CentOS-Base.repo"
    fi

    # 下载阿里云源
    curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo

    # 清理并重建缓存
    yum clean all
    yum makecache

    log_info "基础 Yum 源更换完成"
}

# ============================================================================
# 第二部分:配置 EPEL 源
# ============================================================================
configure_epel() {
    log_step "第二步:配置阿里云 EPEL 源"

    # 安装 EPEL
    yum install -y epel-release

    # 备份原 EPEL 配置
    [ -f /etc/yum.repos.d/epel.repo ] && mv /etc/yum.repos.d/epel.repo /etc/yum.repos.d/epel.repo.backup 2>/dev/null || true
    [ -f /etc/yum.repos.d/epel-testing.repo ] && mv /etc/yum.repos.d/epel-testing.repo /etc/yum.repos.d/epel-testing.repo.backup 2>/dev/null || true

    # 下载阿里云 EPEL 镜像源
    curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo

    # 强制使用 baseurl,避免残留国外 CDN 地址
    sed -i 's/mirrorlist=/#mirrorlist=/g' /etc/yum.repos.d/epel.repo
    sed -i 's|#baseurl=http://download.fedoraproject.org/pub/epel|baseurl=http://mirrors.aliyun.com/epel|g' /etc/yum.repos.d/epel.repo

    # 彻底清理 EPEL 旧缓存
    yum clean all
    rm -rf /var/cache/yum/x86_64/7/epel
    yum makecache

    log_info "EPEL 源配置完成"
}

# ============================================================================
# 第三部分:安装编译依赖
# ============================================================================
install_dependencies() {
    log_step "第三步:安装编译依赖"

    # 安装 Development Tools 组
    yum groups mark install "Development Tools" 2>/dev/null || true
    yum groups mark convert "Development Tools" 2>/dev/null || true
    yum groupinstall -y "Development Tools"

    # 安装基础依赖
    yum install -y gcc zlib-devel bzip2-devel libffi-devel readline-devel

    # 安装 SQLite 编译依赖
    yum install -y sqlite-devel

    # 安装可选模块依赖
    yum install -y xz-devel libuuid-devel gdbm-devel

    # 安装 OpenSSL 1.1(Python 3.11 必需)
    yum install -y openssl-devel openssl11 openssl11-devel

    # 安装 wget
    yum install -y wget

    log_info "编译依赖安装完成"
}

# ============================================================================
# 第四部分:升级系统 SQLite
# ============================================================================
upgrade_sqlite() {
    log_step "第四步:升级系统 SQLite 到 3.42.0"

    local SQLITE_VERSION="3420000"
    local SQLITE_DIR="/tmp/sqlite-autoconf-${SQLITE_VERSION}"

    # 如果已编译过,跳过
    if [ -f /usr/local/bin/sqlite3 ]; then
        INSTALLED_VERSION=$(/usr/local/bin/sqlite3 --version | awk '{print $1}')
        if [ "$INSTALLED_VERSION" = "3.42.0" ]; then
            log_info "SQLite 3.42.0 已安装,跳过编译"
            return
        fi
    fi

    cd /tmp

    # 下载源码
    if [ ! -f "sqlite-autoconf-${SQLITE_VERSION}.tar.gz" ]; then
        log_info "下载 SQLite 源码..."
        wget -q https://www.sqlite.org/2023/sqlite-autoconf-${SQLITE_VERSION}.tar.gz
    else
        log_info "SQLite 源码包已存在,跳过下载"
    fi

    # 解压
    tar -xzf sqlite-autoconf-${SQLITE_VERSION}.tar.gz
    cd "$SQLITE_DIR"

    # 编译安装
    log_info "编译安装 SQLite..."
    ./configure --prefix=/usr/local
    make -j $(nproc)
    make install

    # 替换系统旧版本
    mv /usr/bin/sqlite3 /usr/bin/sqlite3.old 2>/dev/null || true
    ln -sf /usr/local/bin/sqlite3 /usr/bin/sqlite3

    # 配置动态链接库
    echo "/usr/local/lib" > /etc/ld.so.conf.d/sqlite3.conf
    ldconfig

    # 验证
    SQLITE_VER=$(sqlite3 --version | awk '{print $1}')
    log_info "SQLite 版本:${SQLITE_VER}"
}

# ============================================================================
# 第五部分:编译安装 Python 3.11
# ============================================================================
compile_python() {
    log_step "第五步:编译安装 Python 3.11.9"

    local PYTHON_VERSION="3.11.9"
    local PYTHON_PREFIX="/usr/local/python311"
    local SOURCE_DIR="/tmp/Python-${PYTHON_VERSION}"

    cd /tmp

    # 下载源码
    if [ ! -f "Python-${PYTHON_VERSION}.tgz" ]; then
        log_info "下载 Python ${PYTHON_VERSION} 源码..."
        wget -q https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tgz
    else
        log_info "Python 源码包已存在,跳过下载"
    fi

    # 清理旧目录(如果存在)
    if [ -d "$SOURCE_DIR" ]; then
        rm -rf "$SOURCE_DIR"
        log_info "清理旧的源码目录"
    fi

    # 解压
    tar -xzf Python-${PYTHON_VERSION}.tgz
    cd "$SOURCE_DIR"

    # 设置编译环境变量
    # 1. CFLAGS:添加 /usr/local/include 让编译器找到新版 SQLite 头文件
    # 2. LDFLAGS:添加 /usr/local/lib 让链接器找到新版 SQLite 库文件
    # 3. LD_RUN_PATH:指定运行时动态库搜索路径
    export CFLAGS="$(pkg-config --cflags openssl11) -I/usr/local/include"
    export LDFLAGS="$(pkg-config --libs openssl11) -L/usr/local/lib"
    export LD_RUN_PATH="/usr/local/lib"

    # 配置编译选项
    # 注意:不加 --enable-optimizations,因为 CentOS 7 的 GCC 4.8.5 不兼容
    log_info "配置编译选项..."
    ./configure \
        --prefix="${PYTHON_PREFIX}" \
        --enable-loadable-sqlite-extensions

    # 编译
    local CPU_CORES=$(nproc)
    log_info "使用 ${CPU_CORES} 个核心并行编译..."
    make -j ${CPU_CORES}

    # 安装
    log_info "安装 Python ${PYTHON_VERSION}..."
    make altinstall

    cd /tmp
    log_info "Python ${PYTHON_VERSION} 编译安装完成"
}

# ============================================================================
# 第六部分:创建软链接
# ============================================================================
create_links() {
    log_step "第六步:创建软链接"

    ln -sf /usr/local/python311/bin/python3.11 /usr/bin/python3.11
    ln -sf /usr/local/python311/bin/pip3.11 /usr/bin/pip3.11

    log_info "软链接创建完成"
}

# ============================================================================
# 第七部分:验证安装
# ============================================================================
verify_installation() {
    log_step "第七步:验证安装"

    # 验证 Python 版本
    PYTHON_VER=$(python3.11 --version 2>&1)
    log_info "Python 版本:${PYTHON_VER}"

    # 验证 pip 版本
    PIP_VER=$(pip3.11 --version 2>&1)
    log_info "pip 版本:${PIP_VER}"

    # 验证 SSL 模块
    SSL_VER=$(python3.11 -c "import ssl; print(ssl.OPENSSL_VERSION)" 2>&1)
    log_info "SSL 版本:${SSL_VER}"

    # 验证 SQLite 版本(关键检查)
    SQLITE_VER=$(python3.11 -c "import sqlite3; print(sqlite3.sqlite_version)" 2>&1)
    log_info "SQLite 版本:${SQLITE_VER}"

    # 检查 SQLite 版本是否满足 Django 要求(≥ 3.8.3)
    SQLITE_MAJOR=$(echo "$SQLITE_VER" | cut -d. -f1)
    SQLITE_MINOR=$(echo "$SQLITE_VER" | cut -d. -f2)
    if [ "$SQLITE_MAJOR" -ge 3 ] && [ "$SQLITE_MINOR" -ge 8 ]; then
        log_info "SQLite 版本满足 Django 要求(≥ 3.8.3)"
    else
        log_warn "SQLite 版本过低,Django 可能无法正常运行!"
        log_warn "请检查环境变量 LD_RUN_PATH 是否正确设置"
    fi

    # 验证其他关键模块
    python3.11 -c "import lzma, uuid, bz2; print('其他核心模块验证通过')" 2>&1
    log_info "lzma、uuid、bz2 模块正常"
}

# ============================================================================
# 第八部分:配置 pip 镜像源
# ============================================================================
configure_pip_mirror() {
    log_step "第八步:配置 pip 华为云镜像源"

    pip3.11 config set global.index-url https://repo.huaweicloud.com/repository/pypi/simple
    log_info "pip 镜像源配置为华为云"

    # 显示当前配置
    pip3.11 config list 2>/dev/null || true
}

# ============================================================================
# 第九部分:清理临时文件
# ============================================================================
cleanup() {
    log_step "第九步:清理临时文件"

    rm -rf /tmp/Python-3.11.9 /tmp/Python-3.11.9.tgz
    rm -rf /tmp/sqlite-autoconf-3420000 /tmp/sqlite-autoconf-3420000.tar.gz

    log_info "临时文件清理完成"
}

# ============================================================================
# 完成提示
# ============================================================================
print_summary() {
    echo ""
    echo -e "${GREEN}============================================${NC}"
    echo -e "${GREEN}   Python 3.11.9 安装完成!${NC}"
    echo -e "${GREEN}============================================${NC}"
    echo ""
    echo -e "安装位置:${BLUE}/usr/local/python311${NC}"
    echo -e "Python 命令:${BLUE}python3.11${NC}"
    echo -e "pip 命令:${BLUE}pip3.11${NC}"
    echo -e "pip 镜像源:${BLUE}华为云${NC}"
    echo ""
    echo -e "${YELLOW}使用示例:${NC}"
    echo "  查看版本:python3.11 --version"
    echo "  安装包:pip3.11 install <package>"
    echo "  创建虚拟环境:python3.11 -m venv myenv"
    echo "  激活虚拟环境:source myenv/bin/activate"
    echo ""
    echo -e "${RED}⚠ 重要提醒:${NC}"
    echo "  1. 切勿使用 python 或 pip 命令,避免影响系统 Python 2.7"
    echo "  2. 请使用 python3.11 和 pip3.11"
    echo "  3. 建议在虚拟环境中安装第三方包"
    echo -e "${GREEN}============================================${NC}"
    echo ""
}

# ============================================================================
# 主函数
# ============================================================================
main() {
    echo ""
    echo -e "${GREEN}============================================${NC}"
    echo -e "${GREEN}   CentOS 7 自动安装 Python 3.11.9${NC}"
    echo -e "${GREEN}   版本:2026-04-25${NC}"
    echo -e "${GREEN}============================================${NC}"
    echo ""

    # 环境检查
    check_root
    check_network

    # 执行安装步骤
    replace_yum_repo
    configure_epel
    install_dependencies
    upgrade_sqlite
    compile_python
    create_links
    verify_installation
    configure_pip_mirror
    cleanup

    # 打印完成摘要
    print_summary
}

# 执行主函数
main "$@"

使用方法

  1. 保存脚本

    bash 复制代码
    vi install_python311.sh
  2. 粘贴上面的完整脚本内容,保存退出

  3. 添加执行权限并运行

    bash 复制代码
    chmod +x install_python311.sh
    sudo ./install_python311.sh

脚本二

sh 复制代码
#!/bin/bash
#===============================================================================
# CentOS 7 自动化安装 Python 3.11.9 脚本(带进度显示 + 断点续传)
# 适用环境:CentOS 7 初始系统,可连接外网
# 功能:
#   1. 更换阿里云 Yum 源和 EPEL 源
#   2. 升级系统 SQLite 到 3.42.0(国内/官网双下载源)
#   3. 编译安装 Python 3.11.9(含 SSL、SQLite、lzma、uuid 等模块)
#   4. 配置 pip 华为云镜像源
#   5. 支持中断后继续(断点续传)
# 使用方法:
#   chmod +x install_python311.sh
#   sudo ./install_python311.sh
#===============================================================================

set -e  # 遇到错误立即退出

# ============================================================================
# 全局变量
# ============================================================================
TOTAL_STEPS=9
CURRENT_STEP=0
SCRIPT_START_TIME=$(date +%s)
STATE_FILE="/tmp/python311_install_state"
# 状态值:
#   0 - 未开始
#   1 - yum_repo 完成
#   2 - epel 完成
#   3 - dependencies 完成
#   4 - sqlite 完成
#   5 - python_compile 完成
#   6 - links 完成
#   7 - verify 完成
#   8 - pip_mirror 完成
#   9 - cleanup 完成

# ============================================================================
# 颜色定义
# ============================================================================
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
MAGENTA='\033[0;35m'
NC='\033[0m' # No Color
BOLD='\033[1m'

# 如果标准输出不是终端(如被重定向),去掉颜色
if [ ! -t 1 ]; then
    RED='' GREEN='' YELLOW='' BLUE='' CYAN='' MAGENTA='' NC='' BOLD=''
fi

# ============================================================================
# 工具函数:检测 wget 版本,选择合适的参数
# ============================================================================
WGET_OPTS="-q"

detect_wget() {
    # 检测 wget 是否支持 --show-progress
    if wget --help 2>&1 | grep -q '\-\-show-progress'; then
        WGET_OPTS="-q --show-progress"
    else
        # 旧版 wget 不支持,使用 -nv 显示基本信息
        WGET_OPTS="-nv"
    fi
}

# ============================================================================
# 通用下载函数(兼容新旧 wget)
# ============================================================================
wget_download() {
    local url="$1"
    local output="$2"
    local desc="$3"
    local timeout="${4:-60}"
    local tries="${5:-3}"
    
    if [ -n "$output" ]; then
        wget ${WGET_OPTS} --timeout=${timeout} --tries=${tries} -O "$output" "$url" 2>&1
    else
        wget ${WGET_OPTS} --timeout=${timeout} --tries=${tries} "$url" 2>&1
    fi
    return $?
}

# ============================================================================
# 状态管理函数
# ============================================================================
get_state() {
    if [ -f "$STATE_FILE" ]; then
        cat "$STATE_FILE"
    else
        echo "0"
    fi
}

set_state() {
    echo "$1" > "$STATE_FILE"
    chmod 644 "$STATE_FILE" 2>/dev/null || true
}

clear_state() {
    rm -f "$STATE_FILE"
}

# ============================================================================
# 进度条和日志函数
# ============================================================================
show_progress() {
    local step_num=$1
    local total=$2
    local percent=$((step_num * 100 / total))
    local bar_width=40
    local filled=$((percent * bar_width / 100))
    local empty=$((bar_width - filled))
    
    printf "\r${CYAN}[进度]${NC} ["
    printf "${GREEN}%-${filled}s${NC}" | tr ' ' '█'
    printf "${RED}%-${empty}s${NC}" | tr ' ' '░'
    printf "] ${BOLD}%3d%%${NC} (步骤 %d/%d)" "$percent" "$step_num" "$total"
}

log_info() {
    echo -e "\n${GREEN}[INFO]${NC} $1"
}

log_warn() {
    echo -e "${YELLOW}[WARN]${NC} $1"
}

log_error() {
    echo -e "\n${RED}[ERROR]${NC} $1"
    exit 1
}

log_step_header() {
    CURRENT_STEP=$((CURRENT_STEP + 1))
    echo ""
    echo -e "${BLUE}╔══════════════════════════════════════════════════════════════╗${NC}"
    printf "${BLUE}║${NC} ${BOLD}步骤 %d/%d:%s${NC}" "$CURRENT_STEP" "$TOTAL_STEPS" "$1"
    local header_text="步骤 $CURRENT_STEP/$TOTAL_STEPS:$1"
    local text_length=${#header_text}
    local padding=$((58 - text_length))
    [ $padding -lt 1 ] && padding=1
    printf "%${padding}s${BLUE}║${NC}\n" ""
    echo -e "${BLUE}╚══════════════════════════════════════════════════════════════╝${NC}"
    if [ -t 1 ]; then
        show_progress "$CURRENT_STEP" "$TOTAL_STEPS"
    fi
}

log_substep() {
    echo -e "  ${CYAN}➤${NC} $1"
}

log_success() {
    echo -e "  ${GREEN}✔${NC} $1"
}

log_fail() {
    echo -e "  ${RED}✘${NC} $1"
}

log_skip() {
    echo -e "  ${MAGENTA}◎${NC} $1 (已完成,跳过)"
}

# 显示耗时
show_elapsed() {
    local current_time=$(date +%s)
    local elapsed=$((current_time - $1))
    local hours=$((elapsed / 3600))
    local minutes=$(((elapsed % 3600) / 60))
    local seconds=$((elapsed % 60))
    
    if [ $hours -gt 0 ]; then
        echo -e "${MAGENTA}⏱ 耗时:${hours}小时${minutes}分${seconds}秒${NC}"
    elif [ $minutes -gt 0 ]; then
        echo -e "${MAGENTA}⏱ 耗时:${minutes}分${seconds}秒${NC}"
    else
        echo -e "${MAGENTA}⏱ 耗时:${seconds}秒${NC}"
    fi
}

# 旋转指示器(用于后台任务)
spinner() {
    local pid=$1
    local delay=0.1
    local spinstr='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'
    if [ ! -t 1 ]; then
        wait $pid
        return
    fi
    while kill -0 $pid 2>/dev/null; do
        local temp=${spinstr#?}
        printf "\r  ${CYAN}[%c]${NC} 处理中..." "$spinstr"
        local spinstr=$temp${spinstr%"$temp"}
        sleep $delay
    done
    printf "\r  ${GREEN}✔${NC} 完成                    \n"
}

# ============================================================================
# 环境检查
# ============================================================================
check_root() {
    log_step_header "检查运行权限"
    
    log_substep "检查是否为 root 用户..."
    if [ "$(id -u)" != "0" ]; then
        log_fail "当前用户不是 root"
        log_error "请使用 root 用户或 sudo 执行此脚本"
    fi
    log_success "当前为 root 用户"
    set_state "0.5"
    show_elapsed "$SCRIPT_START_TIME"
}

check_network() {
    log_step_header "检查网络连接"
    
    log_substep "测试外网连通性..."
    if ping -c 2 -W 3 www.baidu.com > /dev/null 2>&1; then
        log_success "网络连接正常"
    else
        log_fail "无法连接外网"
        log_error "请检查网络配置后重试"
    fi
    show_elapsed "$SCRIPT_START_TIME"
}

# ============================================================================
# 第一部分:更换 Yum 源
# ============================================================================
replace_yum_repo() {
    if [ "$(get_state)" -ge 1 ]; then
        log_step_header "更换阿里云 Yum 源"
        log_skip "Yum 源已配置"
        show_elapsed "$SCRIPT_START_TIME"
        return
    fi
    
    log_step_header "更换阿里云 Yum 源"
    
    log_substep "备份原 CentOS-Base.repo..."
    if [ -f /etc/yum.repos.d/CentOS-Base.repo ]; then
        mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
        log_success "已备份原配置"
    else
        log_warn "原配置文件不存在,跳过备份"
    fi
    
    log_substep "下载阿里云 CentOS 7 源..."
    if curl -s -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo; then
        log_success "下载完成"
    else
        log_fail "下载失败"
        log_error "请检查网络连接"
    fi
    
    log_substep "清理 Yum 缓存..."
    yum clean all > /dev/null 2>&1
    log_success "缓存清理完成"
    
    log_substep "重建 Yum 缓存..."
    yum makecache -q > /dev/null 2>&1 &
    spinner $!
    log_success "缓存重建完成"
    
    set_state "1"
    show_elapsed "$SCRIPT_START_TIME"
}

# ============================================================================
# 第二部分:配置 EPEL 源
# ============================================================================
configure_epel() {
    if [ "$(get_state)" -ge 2 ]; then
        log_step_header "配置阿里云 EPEL 源"
        log_skip "EPEL 源已配置"
        show_elapsed "$SCRIPT_START_TIME"
        return
    fi
    
    log_step_header "配置阿里云 EPEL 源"
    
    log_substep "安装 EPEL 源..."
    yum install -y -q epel-release > /dev/null 2>&1 &
    spinner $!
    log_success "EPEL 源安装完成"
    
    log_substep "备份原 EPEL 配置..."
    [ -f /etc/yum.repos.d/epel.repo ] && mv /etc/yum.repos.d/epel.repo /etc/yum.repos.d/epel.repo.backup 2>/dev/null || true
    [ -f /etc/yum.repos.d/epel-testing.repo ] && mv /etc/yum.repos.d/epel-testing.repo /etc/yum.repos.d/epel-testing.repo.backup 2>/dev/null || true
    log_success "备份完成"
    
    log_substep "下载阿里云 EPEL 镜像源..."
    if curl -s -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo; then
        log_success "下载完成"
    else
        log_fail "下载失败"
        log_error "请检查网络连接"
    fi
    
    log_substep "修正 EPEL 配置(强制使用 baseurl)..."
    sed -i 's/mirrorlist=/#mirrorlist=/g' /etc/yum.repos.d/epel.repo
    sed -i 's|#baseurl=http://download.fedoraproject.org/pub/epel|baseurl=http://mirrors.aliyun.com/epel|g' /etc/yum.repos.d/epel.repo
    log_success "配置修正完成"
    
    log_substep "清理 EPEL 旧缓存..."
    yum clean all > /dev/null 2>&1
    rm -rf /var/cache/yum/x86_64/7/epel
    log_success "旧缓存清理完成"
    
    log_substep "重建缓存..."
    yum makecache -q > /dev/null 2>&1 &
    spinner $!
    log_success "EPEL 源配置完成"
    
    set_state "2"
    show_elapsed "$SCRIPT_START_TIME"
}

# ============================================================================
# 第三部分:安装编译依赖
# ============================================================================
install_dependencies() {
    if [ "$(get_state)" -ge 3 ]; then
        log_step_header "安装编译依赖"
        log_skip "编译依赖已安装"
        show_elapsed "$SCRIPT_START_TIME"
        return
    fi
    
    log_step_header "安装编译依赖"
    
    log_substep "准备安装 Development Tools 组..."
    yum groups mark install "Development Tools" > /dev/null 2>&1 || true
    yum groups mark convert "Development Tools" > /dev/null 2>&1 || true
    
    log_substep "安装 Development Tools(可能需要几分钟)..."
    yum groupinstall -y -q "Development Tools" > /dev/null 2>&1 &
    spinner $!
    log_success "Development Tools 安装完成"
    
    log_substep "安装基础编译依赖..."
    local pkgs="gcc zlib-devel bzip2-devel libffi-devel readline-devel"
    echo -e "  ${CYAN}  →${NC} 安装: ${pkgs}"
    yum install -y -q $pkgs > /dev/null 2>&1 &
    spinner $!
    log_success "基础依赖安装完成"
    
    log_substep "安装 SQLite 编译依赖..."
    yum install -y -q sqlite-devel > /dev/null 2>&1 &
    spinner $!
    log_success "SQLite 依赖安装完成"
    
    log_substep "安装可选模块依赖(lzma、uuid、gdbm)..."
    yum install -y -q xz-devel libuuid-devel gdbm-devel > /dev/null 2>&1 &
    spinner $!
    log_success "可选模块依赖安装完成"
    
    log_substep "安装 OpenSSL 1.1(Python 3.11 必需)..."
    yum install -y -q openssl-devel openssl11 openssl11-devel > /dev/null 2>&1 &
    spinner $!
    log_success "OpenSSL 1.1 安装完成"
    
    log_substep "安装 wget..."
    yum install -y -q wget > /dev/null 2>&1 &
    spinner $!
    log_success "wget 安装完成"
    
    # 检测 wget 版本
    detect_wget
    
    set_state "3"
    show_elapsed "$SCRIPT_START_TIME"
}

# ============================================================================
# 下载函数(支持国内备用源,兼容新旧 wget)
# ============================================================================
download_with_fallback() {
    local primary_url="$1"
    local fallback_url="$2"
    local output_file="$3"
    local description="$4"
    
    log_substep "尝试从主源下载 ${description}..."
    echo -e "  ${CYAN}  →${NC} 主源:${primary_url}"
    
    if wget_download "$primary_url" "$output_file" "$description"; then
        log_success "从主源下载成功"
        return 0
    fi
    
    log_warn "主源下载失败,切换到备用源(国内 Gitee)..."
    echo -e "  ${CYAN}  →${NC} 备用:${fallback_url}"
    
    if [ -n "$fallback_url" ]; then
        if wget_download "$fallback_url" "$output_file" "$description" 120 5; then
            log_success "从备用源下载成功"
            return 0
        fi
        log_fail "备用源下载也失败"
    fi
    
    # 如果还是失败,尝试用 curl 作为最后的备用方案
    log_warn "wget 下载失败,尝试使用 curl 下载..."
    if curl -L -o "$output_file" "$primary_url" --connect-timeout 30 --retry 3 -# 2>&1; then
        log_success "使用 curl 从主源下载成功"
        return 0
    fi
    
    if [ -n "$fallback_url" ]; then
        if curl -L -o "$output_file" "$fallback_url" --connect-timeout 30 --retry 3 -# 2>&1; then
            log_success "使用 curl 从备用源下载成功"
            return 0
        fi
    fi
    
    return 1
}

# ============================================================================
# 第四部分:升级系统 SQLite
# ============================================================================
upgrade_sqlite() {
    # 检查是否已完成(通过验证 SQLite 版本判断)
    if [ -f /usr/local/bin/sqlite3 ]; then
        INSTALLED_VERSION=$(/usr/local/bin/sqlite3 --version 2>/dev/null | awk '{print $1}')
        if [ "$INSTALLED_VERSION" = "3.42.0" ]; then
            if [ "$(get_state)" -ge 4 ]; then
                log_step_header "升级系统 SQLite 到 3.42.0"
                log_skip "SQLite 3.42.0 已安装"
                show_elapsed "$SCRIPT_START_TIME"
                return
            fi
        fi
    fi
    
    if [ "$(get_state)" -ge 4 ]; then
        log_warn "状态文件异常,重新执行 SQLite 升级"
        set_state "3"
    fi
    
    log_step_header "升级系统 SQLite 到 3.42.0"
    
    local SQLITE_VERSION="3420000"
    local SQLITE_DIR="/tmp/sqlite-autoconf-${SQLITE_VERSION}"
    local SQLITE_TAR="sqlite-autoconf-${SQLITE_VERSION}.tar.gz"
    
    # 主源和备用源
    local PRIMARY_URL="https://www.sqlite.org/2023/${SQLITE_TAR}"
    local FALLBACK_URL="https://gitee.com/suifenglzy/test/raw/master/${SQLITE_TAR}"
    
    cd /tmp
    
    # 验证已有的 tar 包是否完整
    if [ -f "$SQLITE_TAR" ]; then
        if tar -tzf "$SQLITE_TAR" > /dev/null 2>&1; then
            log_substep "SQLite 源码包已存在且有效"
            log_success "跳过下载"
        else
            log_warn "SQLite 源码包损坏,重新下载..."
            rm -f "$SQLITE_TAR"
        fi
    fi
    
    # 下载
    if [ ! -f "$SQLITE_TAR" ]; then
        if ! download_with_fallback "$PRIMARY_URL" "$FALLBACK_URL" "$SQLITE_TAR" "SQLite 3.42.0"; then
            echo ""
            echo -e "  ${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
            echo -e "  ${RED}  所有下载方式均失败!${NC}"
            echo -e "  ${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
            echo ""
            echo -e "  请手动执行以下命令:"
            echo -e "  ${YELLOW}cd /tmp${NC}"
            echo -e "  ${YELLOW}wget ${PRIMARY_URL}${NC}"
            echo -e "  或"
            echo -e "  ${YELLOW}curl -L -o ${SQLITE_TAR} ${FALLBACK_URL}${NC}"
            echo -e "  然后重新运行本脚本"
            echo ""
            log_error "SQLite 源码下载失败"
        fi
    fi
    
    # 解压
    log_substep "解压 SQLite 源码..."
    if [ -d "$SQLITE_DIR" ]; then
        rm -rf "$SQLITE_DIR"
    fi
    tar -xzf "$SQLITE_TAR"
    cd "$SQLITE_DIR"
    log_success "解压完成"
    
    # 配置
    log_substep "配置编译选项..."
    ./configure --prefix=/usr/local > /dev/null 2>&1
    log_success "配置完成"
    
    # 编译
    log_substep "编译 SQLite(使用 $(nproc) 个核心)..."
    local cpu_cores=$(nproc)
    make -j ${cpu_cores} > /dev/null 2>&1 &
    spinner $!
    log_success "编译完成"
    
    # 安装
    log_substep "安装 SQLite..."
    make install > /dev/null 2>&1
    log_success "安装完成"
    
    # 替换系统版本
    log_substep "替换系统旧版 SQLite..."
    mv /usr/bin/sqlite3 /usr/bin/sqlite3.old 2>/dev/null || true
    ln -sf /usr/local/bin/sqlite3 /usr/bin/sqlite3
    echo "/usr/local/lib" > /etc/ld.so.conf.d/sqlite3.conf
    ldconfig > /dev/null 2>&1
    log_success "替换完成"
    
    # 验证
    SQLITE_VER=$(sqlite3 --version 2>/dev/null | awk '{print $1}')
    log_substep "已安装 SQLite 版本:${BOLD}${SQLITE_VER}${NC}"
    log_success "SQLite 升级完成"
    
    set_state "4"
    show_elapsed "$SCRIPT_START_TIME"
}

# ============================================================================
# 第五部分:编译安装 Python 3.11
# ============================================================================
compile_python() {
    # 检查是否已完成
    if [ "$(get_state)" -ge 5 ]; then
        if [ -f /usr/local/python311/bin/python3.11 ]; then
            log_step_header "编译安装 Python 3.11.9"
            log_skip "Python 3.11.9 已编译安装"
            show_elapsed "$SCRIPT_START_TIME"
            return
        else
            log_warn "状态文件异常,重新执行 Python 编译"
            set_state "4"
        fi
    fi
    
    log_step_header "编译安装 Python 3.11.9"
    
    local PYTHON_VERSION="3.11.9"
    local PYTHON_PREFIX="/usr/local/python311"
    local SOURCE_DIR="/tmp/Python-${PYTHON_VERSION}"
    local PYTHON_TAR="Python-${PYTHON_VERSION}.tgz"
    local PYTHON_URL="https://www.python.org/ftp/python/${PYTHON_VERSION}/${PYTHON_TAR}"
    
    cd /tmp
    
    # 验证已有的 tar 包
    if [ -f "$PYTHON_TAR" ]; then
        if tar -tzf "$PYTHON_TAR" > /dev/null 2>&1; then
            log_substep "Python 源码包已存在且有效"
            log_success "跳过下载"
        else
            log_warn "Python 源码包损坏,重新下载..."
            rm -f "$PYTHON_TAR"
        fi
    fi
    
    # 下载
    if [ ! -f "$PYTHON_TAR" ]; then
        log_substep "下载 Python ${PYTHON_VERSION} 源码..."
        echo -e "  ${CYAN}  →${NC} URL:${PYTHON_URL}"
        
        # 尝试 wget 下载
        if wget_download "$PYTHON_URL" "$PYTHON_TAR" "Python ${PYTHON_VERSION}" 120 5; then
            log_success "下载完成"
        else
            # 尝试 curl 下载
            log_warn "wget 下载失败,尝试使用 curl..."
            if curl -L -o "$PYTHON_TAR" "$PYTHON_URL" --connect-timeout 60 --retry 5 -# 2>&1; then
                log_success "下载完成"
            else
                log_fail "下载失败"
                echo ""
                echo -e "  请手动下载并放置在 /tmp/:"
                echo -e "  ${YELLOW}cd /tmp && curl -L -o ${PYTHON_TAR} ${PYTHON_URL}${NC}"
                echo ""
                log_error "Python 源码下载失败"
            fi
        fi
    fi
    
    # 清理旧目录
    if [ -d "$SOURCE_DIR" ]; then
        log_substep "清理旧的源码目录..."
        rm -rf "$SOURCE_DIR"
        log_success "清理完成"
    fi
    
    # 解压
    log_substep "解压 Python 源码..."
    tar -xzf "$PYTHON_TAR"
    cd "$SOURCE_DIR"
    log_success "解压完成"
    
    # 设置环境变量
    log_substep "配置编译环境变量..."
    export CFLAGS="$(pkg-config --cflags openssl11) -I/usr/local/include"
    export LDFLAGS="$(pkg-config --libs openssl11) -L/usr/local/lib"
    export LD_RUN_PATH="/usr/local/lib"
    echo -e "  ${CYAN}  →${NC} CFLAGS: ${CFLAGS}"
    echo -e "  ${CYAN}  →${NC} LDFLAGS: ${LDFLAGS}"
    echo -e "  ${CYAN}  →${NC} LD_RUN_PATH: ${LD_RUN_PATH}"
    log_success "环境变量配置完成"
    
    # 配置
    log_substep "运行 ./configure(检测依赖和配置编译选项)..."
    echo -e "  ${CYAN}  →${NC} 安装路径: ${PYTHON_PREFIX}"
    echo -e "  ${CYAN}  →${NC} 启用可加载 SQLite 扩展"
    echo -e "  ${CYAN}  →${NC} 不启用 PGO 优化(兼容 GCC 4.8.5)"
    
    ./configure \
        --prefix="${PYTHON_PREFIX}" \
        --enable-loadable-sqlite-extensions > /dev/null 2>&1 &
    spinner $!
    log_success "配置完成"
    
    # 编译(这是最耗时的步骤)
    local cpu_cores=$(nproc)
    echo ""
    echo -e "  ${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
    echo -e "  ${BOLD}开始编译 Python ${PYTHON_VERSION}${NC}"
    echo -e "  ${CYAN}  →${NC} 使用 ${BOLD}${cpu_cores}${NC} 个 CPU 核心并行编译"
    echo -e "  ${CYAN}  →${NC} 预计耗时:5-15 分钟(取决于 CPU 性能)"
    echo -e "  ${CYAN}  →${NC} 可随时按 Ctrl+C 中断,下次运行将从此继续"
    echo -e "  ${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
    echo ""
    
    local compile_start=$(date +%s)
    
    # 显示实时编译输出
    if make -j ${cpu_cores} 2>&1 | while IFS= read -r line; do
        printf "\r  ${CYAN}[编译中]${NC} %-65s" "${line:0:65}"
    done; then
        local compile_end=$(date +%s)
        local compile_elapsed=$((compile_end - compile_start))
        echo ""
        log_success "编译完成"
        echo -e "  ${MAGENTA}⏱ 编译耗时:${compile_elapsed}秒${NC}"
    else
        echo ""
        log_fail "编译失败!"
        log_error "请查看上方错误信息,修复后重新运行脚本(会自动从本步骤继续)"
    fi
    
    # 安装
    log_substep "安装 Python ${PYTHON_VERSION}..."
    make altinstall > /dev/null 2>&1 &
    spinner $!
    log_success "安装完成"
    
    cd /tmp
    
    set_state "5"
    show_elapsed "$SCRIPT_START_TIME"
}

# ============================================================================
# 第六部分:创建软链接
# ============================================================================
create_links() {
    if [ "$(get_state)" -ge 6 ]; then
        log_step_header "创建软链接"
        log_skip "软链接已创建"
        show_elapsed "$SCRIPT_START_TIME"
        return
    fi
    
    log_step_header "创建软链接"
    
    log_substep "创建 python3.11 软链接..."
    ln -sf /usr/local/python311/bin/python3.11 /usr/bin/python3.11
    log_success "/usr/bin/python3.11 → /usr/local/python311/bin/python3.11"
    
    log_substep "创建 pip3.11 软链接..."
    ln -sf /usr/local/python311/bin/pip3.11 /usr/bin/pip3.11
    log_success "/usr/bin/pip3.11 → /usr/local/python311/bin/pip3.11"
    
    set_state "6"
    show_elapsed "$SCRIPT_START_TIME"
}

# ============================================================================
# 第七部分:验证安装
# ============================================================================
verify_installation() {
    if [ "$(get_state)" -ge 7 ]; then
        log_step_header "验证安装"
        log_skip "已验证"
        show_elapsed "$SCRIPT_START_TIME"
        return
    fi
    
    log_step_header "验证安装"
    
    echo ""
    echo -e "  ${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
    echo -e "  ${BOLD}  执行完整验证${NC}"
    echo -e "  ${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
    
    local all_ok=true
    
    # 验证 Python
    echo ""
    echo -e "  ${CYAN}[1/5]${NC} 检查 Python 版本..."
    PYTHON_VER=$(python3.11 --version 2>&1)
    if [ $? -eq 0 ]; then
        log_success "Python: ${BOLD}${PYTHON_VER}${NC}"
    else
        log_fail "Python 安装失败"
        all_ok=false
    fi
    
    # 验证 pip
    echo -e "  ${CYAN}[2/5]${NC} 检查 pip 版本..."
    PIP_VER=$(pip3.11 --version 2>&1)
    if [ $? -eq 0 ]; then
        log_success "pip: ${BOLD}${PIP_VER}${NC}"
    else
        log_fail "pip 安装失败"
        all_ok=false
    fi
    
    # 验证 SSL
    echo -e "  ${CYAN}[3/5]${NC} 检查 SSL 模块..."
    SSL_VER=$(python3.11 -c "import ssl; print(ssl.OPENSSL_VERSION)" 2>&1)
    if [ $? -eq 0 ]; then
        log_success "SSL: ${BOLD}${SSL_VER}${NC}"
    else
        log_fail "SSL 模块异常"
        all_ok=false
    fi
    
    # 验证 SQLite
    echo -e "  ${CYAN}[4/5]${NC} 检查 SQLite 版本..."
    SQLITE_VER=$(python3.11 -c "import sqlite3; print(sqlite3.sqlite_version)" 2>&1)
    if [ $? -eq 0 ]; then
        log_success "SQLite: ${BOLD}${SQLITE_VER}${NC}"
        
        SQLITE_MAJOR=$(echo "$SQLITE_VER" | cut -d. -f1)
        SQLITE_MINOR=$(echo "$SQLITE_VER" | cut -d. -f2)
        if [ "$SQLITE_MAJOR" -gt 3 ] || ([ "$SQLITE_MAJOR" -eq 3 ] && [ "$SQLITE_MINOR" -ge 8 ]); then
            log_success "SQLite 版本满足 Django 要求(≥ 3.8.3) ✅"
        else
            log_fail "SQLite 版本过低!当前 ${SQLITE_VER},需要 ≥ 3.8.3"
            all_ok=false
        fi
    else
        log_fail "SQLite 模块异常"
        all_ok=false
    fi
    
    # 验证其他模块
    echo -e "  ${CYAN}[5/5]${NC} 检查其他核心模块(lzma, uuid, bz2)..."
    OTHER_MODULES=$(python3.11 -c "import lzma, uuid, bz2; print('OK')" 2>&1)
    if [ $? -eq 0 ]; then
        log_success "核心模块全部正常 ✅"
    else
        log_warn "部分模块缺失:${OTHER_MODULES}"
        log_warn "不影响基本使用,但建议安装对应的 devel 包后重新编译"
    fi
    
    echo ""
    if [ "$all_ok" = true ]; then
        echo -e "  ${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
        echo -e "  ${GREEN}  ✅ 所有验证通过${NC}"
        echo -e "  ${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
        set_state "7"
    else
        echo -e "  ${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
        echo -e "  ${RED}  ❌ 验证未通过,请检查失败项${NC}"
        echo -e "  ${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
        log_error "安装验证失败,请修复后重新运行脚本"
    fi
    
    show_elapsed "$SCRIPT_START_TIME"
}

# ============================================================================
# 第八部分:配置 pip 镜像源
# ============================================================================
configure_pip_mirror() {
    if [ "$(get_state)" -ge 8 ]; then
        log_step_header "配置 pip 华为云镜像源"
        log_skip "pip 镜像源已配置"
        show_elapsed "$SCRIPT_START_TIME"
        return
    fi
    
    log_step_header "配置 pip 华为云镜像源"
    
    log_substep "设置 pip 镜像源为华为云..."
    pip3.11 config set global.index-url https://repo.huaweicloud.com/repository/pypi/simple > /dev/null 2>&1
    log_success "镜像源配置完成"
    
    echo ""
    echo -e "  ${BOLD}当前 pip 配置:${NC}"
    pip3.11 config list 2>/dev/null | while read line; do        echo -e "  ${CYAN}  →${NC} ${line}"
    done
    
    set_state "8"
    show_elapsed "$SCRIPT_START_TIME"
}

# ============================================================================
# 第九部分:清理临时文件
# ============================================================================
cleanup() {
    if [ "$(get_state)" -ge 9 ]; then
        log_step_header "清理临时文件"
        log_skip "已清理"
        show_elapsed "$SCRIPT_START_TIME"
        return
    fi
    
    log_step_header "清理临时文件"
    
    log_substep "清理 Python 源码和编译目录..."
    if [ -d "/tmp/Python-3.11.9" ]; then
        rm -rf /tmp/Python-3.11.9
        log_success "已删除 /tmp/Python-3.11.9"
    fi
    if [ -f "/tmp/Python-3.11.9.tgz" ]; then
        rm -f /tmp/Python-3.11.9.tgz
        log_success "已删除 /tmp/Python-3.11.9.tgz"
    fi
    
    log_substep "清理 SQLite 源码和编译目录..."
    if [ -d "/tmp/sqlite-autoconf-3420000" ]; then
        rm -rf /tmp/sqlite-autoconf-3420000
        log_success "已删除 /tmp/sqlite-autoconf-3420000"
    fi
    if [ -f "/tmp/sqlite-autoconf-3420000.tar.gz" ]; then
        rm -f /tmp/sqlite-autoconf-3420000.tar.gz
        log_success "已删除 /tmp/sqlite-autoconf-3420000.tar.gz"
    fi
    
    log_success "所有临时文件清理完成"
    
    set_state "9"
    show_elapsed "$SCRIPT_START_TIME"
}

# ============================================================================
# 完成提示
# ============================================================================
print_summary() {
    local total_elapsed=$(( $(date +%s) - SCRIPT_START_TIME ))
    local hours=$((total_elapsed / 3600))
    local minutes=$(((total_elapsed % 3600) / 60))
    local seconds=$((total_elapsed % 60))
    
    echo ""
    echo -e "${GREEN}╔══════════════════════════════════════════════════════════════════╗${NC}"
    echo -e "${GREEN}║                                                                  ║${NC}"
    echo -e "${GREEN}║${NC}                   ${BOLD}✅ Python 3.11.9 安装完成!${NC}                   ${GREEN}║${NC}"
    echo -e "${GREEN}║                                                                  ║${NC}"
    echo -e "${GREEN}╚══════════════════════════════════════════════════════════════════╝${NC}"
    echo ""
    
    echo -e "${BOLD}📦 安装信息:${NC}"
    echo -e "  ${CYAN}├─${NC} Python 路径:${BOLD}/usr/local/python311${NC}"
    echo -e "  ${CYAN}├─${NC} Python 命令:${BOLD}python3.11${NC}"
    echo -e "  ${CYAN}├─${NC} pip 命令:${BOLD}pip3.11${NC}"
    echo -e "  ${CYAN}├─${NC} pip 镜像源:${BOLD}华为云${NC}"
    echo -e "  ${CYAN}└─${NC} 总耗时:${BOLD}${hours}时${minutes}分${seconds}秒${NC}"
    echo ""
    
    echo -e "${BOLD}📝 使用示例:${NC}"
    echo -e "  ${CYAN}├─${NC} 查看版本:${BOLD}python3.11 --version${NC}"
    echo -e "  ${CYAN}├─${NC} 安装包:${BOLD}pip3.11 install <package>${NC}"
    echo -e "  ${CYAN}├─${NC} 创建虚拟环境:${BOLD}python3.11 -m venv myenv${NC}"
    echo -e "  ${CYAN}└─${NC} 激活虚拟环境:${BOLD}source myenv/bin/activate${NC}"
    echo ""
    
    echo -e "${YELLOW}⚠  ${BOLD}重要提醒:${NC}${YELLOW}"
    echo -e "${YELLOW}  ├─ 切勿使用 python 或 pip 命令(会调用系统 Python 2.7)${NC}"
    echo -e "${YELLOW}  ├─ 请始终使用 python3.11 和 pip3.11${NC}"
    echo -e "${YELLOW}  └─ 建议在虚拟环境中安装第三方包${NC}"
    echo ""
    
    echo -e "${GREEN}══════════════════════════════════════════════════════════════════${NC}"
    echo ""
    
    # 安装完成,清除状态文件
    clear_state
}

# ============================================================================
# 询问用户是否继续上次进度
# ============================================================================
ask_resume() {
    local state=$(get_state)
    
    if [ "$state" = "0" ] || [ "$state" = "0.5" ]; then
        return
    fi
    
    echo ""
    echo -e "${YELLOW}╔══════════════════════════════════════════════════════════════════╗${NC}"
    echo -e "${YELLOW}║                                                                  ║${NC}"
    echo -e "${YELLOW}║${NC}  ${BOLD}检测到上次安装进度${NC}                                          ${YELLOW}║${NC}"
    echo -e "${YELLOW}║                                                                  ║${NC}"
    echo -e "${YELLOW}║${NC}  上次已完成步骤:${BOLD}$state / $TOTAL_STEPS${NC}                                      ${YELLOW}║${NC}"
    echo -e "${YELLOW}║                                                                  ║${NC}"
    echo -e "${YELLOW}║${NC}  ${CYAN}[1]${NC} 从上次中断处继续(推荐)                                ${YELLOW}║${NC}"
    echo -e "${YELLOW}║${NC}  ${CYAN}[2]${NC} 重新开始(清除所有进度)                                  ${YELLOW}║${NC}"
    echo -e "${YELLOW}║${NC}  ${CYAN}[q]${NC} 退出安装                                                  ${YELLOW}║${NC}"
    echo -e "${YELLOW}║                                                                  ║${NC}"
    echo -e "${YELLOW}╚══════════════════════════════════════════════════════════════════╝${NC}"
    echo ""
    
    while true; do
        read -p "请输入选择 [1/2/q](默认 1): " choice
        choice=${choice:-1}
        case $choice in
            1)
                echo ""
                echo -e "${GREEN}从上次中断处继续安装...${NC}"
                echo ""
                case $state in
                    1) set_state "0"; log_info "将重新配置 EPEL 源";;
                    2) set_state "1"; log_info "将重新安装编译依赖";;
                    3) set_state "2"; log_info "将重新升级 SQLite";;
                    4) set_state "3"; log_info "将重新编译 Python";;
                    # 5~8 保留状态,直接跳过
                esac
                return
                ;;
            2)
                echo ""
                echo -e "${YELLOW}清除所有进度,重新开始...${NC}"
                clear_state
                echo ""
                return
                ;;
            q|Q)
                echo ""
                echo -e "${CYAN}安装已取消${NC}"
                exit 0
                ;;
            *)
                echo -e "${RED}无效输入,请输入 1、2 或 q${NC}"
                ;;
        esac
    done
}

# ============================================================================
# 主函数
# ============================================================================
main() {
    # 检测 wget 版本
    detect_wget
    
    # 清屏
    clear
    
    echo ""
    echo -e "${BLUE}╔══════════════════════════════════════════════════════════════════╗${NC}"
    echo -e "${BLUE}║                                                                  ║${NC}"
    echo -e "${BLUE}║${NC}       ${BOLD}CentOS 7 自动安装 Python 3.11.9${NC}                         ${BLUE}║${NC}"
    echo -e "${BLUE}║${NC}       ${CYAN}版本:2026-04-25${NC}                                      ${BLUE}║${NC}"
    echo -e "${BLUE}║${NC}       ${CYAN}特性:进度显示 · 断点续传 · 国内备用源 · 三重下载保障${NC}  ${BLUE}║${NC}"
    echo -e "${BLUE}║                                                                  ║${NC}"
    echo -e "${BLUE}╚══════════════════════════════════════════════════════════════════╝${NC}"
    echo ""
    
    # 询问是否继续上次进度
    ask_resume
    
    echo -e "${BOLD}本脚本将执行以下操作:${NC}"
    echo -e "  ${CYAN}[1]${NC} 检查运行权限和网络连接"
    echo -e "  ${CYAN}[2]${NC} 更换阿里云 Yum 源"
    echo -e "  ${CYAN}[3]${NC} 配置阿里云 EPEL 源"
    echo -e "  ${CYAN}[4]${NC} 安装编译依赖"
    echo -e "  ${CYAN}[5]${NC} 升级系统 SQLite 到 3.42.0"
    echo -e "  ${CYAN}[6]${NC} 编译安装 Python 3.11.9"
    echo -e "  ${CYAN}[7]${NC} 创建软链接"
    echo -e "  ${CYAN}[8]${NC} 验证安装"
    echo -e "  ${CYAN}[9]${NC} 配置 pip 镜像源并清理"
    echo ""
    
    local state=$(get_state)
    if [ "$state" != "0" ] && [ "$state" != "0.5" ]; then
        echo -e "${GREEN}将从步骤 $state 之后继续执行${NC}"
        echo ""
    else
        echo -e "${YELLOW}预计总耗时:10-30 分钟${NC}"
        echo ""
    fi
    
    read -p "按回车键开始安装,或按 Ctrl+C 取消..." 
    
    # 根据状态跳过已完成的步骤
    local current_state=$(get_state)
    
    if [ "$current_state" = "0" ] || [ "$current_state" = "0.5" ]; then
        check_root
        check_network
    fi
    
    replace_yum_repo
    configure_epel
    install_dependencies
    upgrade_sqlite
    compile_python
    create_links
    verify_installation
    configure_pip_mirror
    cleanup
    
    # 最终进度条 100%
    echo ""
    if [ -t 1 ]; then
        show_progress "$TOTAL_STEPS" "$TOTAL_STEPS"
    fi
    echo ""
    
    # 打印完成摘要
    print_summary
}

# 捕获中断信号
trap 'echo -e "\n\n${YELLOW}安装已中断,进度已保存${NC}"; echo -e "${YELLOW}下次运行脚本可选择从断点继续${NC}"; exit 1' INT

# 执行主函数
main "$@"
相关推荐
bksczm2 小时前
Linux之基础开发工具(Ubuntu)之apt 、vim
linux·ubuntu·php
java_logo2 小时前
Docker 部署 Open WebUI + Ollama 完整教程(Windows / Linux 通用)—— 打造自己的本地OpenAI
linux·docker·容器·ollama·open-webui·open-webui部署·open-webui教程
小夏子_riotous2 小时前
Docker学习路径——8、Dockerfile
linux·运维·docker·容器·系统架构·centos·运维开发
叶子上的考拉2 小时前
解决远程连接服务器反应较慢问题
linux·运维·服务器
JackSparrow4142 小时前
彻底理解Java NIO(一)C语言实现 单进程+多进程+多线程 阻塞式I/O 服务器详解
java·linux·c语言·网络·后端·tcp/ip·nio
lazybird742 小时前
vmware装的ubuntu22.04, 在vmware中将磁盘由40G调整为50G后,ubuntu中还需要进行调整
linux·运维·服务器
Beiwen_2 小时前
在 Linux 服务器上配置 LaTeX Workshop 并解决 `ifsym.sty` 和 `llncs.cls` 错误
linux·服务器
IMPYLH2 小时前
Linux 的 stat 命令
linux·运维·服务器·bash
error:(2 小时前
Linux系统Claude Code安装指南:绕过官方curl 403错误的解决方案
linux·运维·服务器