上周在 Termux 里跑一个三年前写的数据清洗脚本第一次意识到版本差距不是小打小闹。脚本里那个老旧的gensim模型在两年前的 Python 3.8 上跑得稳稳当当换到 Python 3.11 直接抛AttributeError再换 3.12 干脆连import都过不去。翻遍 issue 最后结论出奇一致这库没人维护了只认 3.8。于是我被逼着在 Termux 里执行降级安装 python3.8——不是装个新包然后pkg install python3.8能完事而是要把旧解释器完整地落到 Android 的终端环境里。这篇文章专门聊这件事降级的两条可行路线、每条路线要跨的坎以及折腾过程中容易被忽略的编译和依赖问题。如果你手头也有项目必须跑在 python3.8 上或者正在被其他 Python 新版本兼容性问题折磨按下面的流程走一遍大概率能少交两小时学费。很多人以为 Termux 是“Android 上的 Ubuntu”包管理操作应该和桌面版差不多实际差距很大。Termux 的仓库策略、编译环境、链接库路径都有独特的一套直接套用 Linux 教程的降级方案多数时候不仅装不上还会把现有 Python 环境搅成一锅粥。所以我不打算只给一条命令而是把背后的原因和实操取舍都讲清楚。1. 为什么 Termux 降级 Python 这么费劲包管理器的“单向车道”1.1 Termux 的 apt 仓库里只有一个 Python 大版本Termux 基于 apt 管理软件包但它是一个 Android 应用沙箱里的 Linux 环境所有软件都安装到$PREFIX通常也就是/data/data/com.termux/files/usr。这套设计决定了它对「多版本共存」的支持非常弱官方仓库出于维护成本考虑对同一门语言往往只保留一个活跃的大版本。你可以自己在终端里验证一下pkg install python python --version目前新装的 Termux 默认拉到的基本是 Python 3.11 或 3.12取决于仓库快照时间。如果你想直接执行apt install python3.8大概率会得到Unable to locate package python3.8。这不是你包名拼错了而是仓库里压根不存在这个版本的安装包。Ubuntu 上能apt install python3.8是因为有较老的系统仓库或 PPA 支撑Termux 官方没有这套历史存档机制眼睁睁给你一个“最新版”之外的选项都不给。更要命的是Termux 的 Python 是经过 Bionic libc 适配的它自己带了一堆针对 Android 的编译补丁。直接从 Python 官网拉一个 Linux 的二进制或源码包扔进去大概率跑不起来因为依赖的动态库和系统路径都被重新定义了。所以“降级安装”在这里不是一句简单的 apt 语法而是一次对环境底层的重构。1.2 降级的三个真实可行思路源码、旧 deb、版本管理器网上关于“Termux 降级 python3.8”的教程不少我实操和对比下来能落地的基本是三条线方案优点缺点适合场景源码编译安装版本完全可控最「干净」耗时长、内存吃紧、要自己排编译错长期使用、需要跑多种 C 扩展旧 .deb 包硬装看起来最快几条命令搞定依赖极容易冲突装完环境可能崩临时跑一个短脚本能快速验证pyenv 版本管理多版本切换方便结构清晰在 Termux 上底层还是要编译且路径适配坑更多需要频繁切版本且设备内存充裕我最后采用的是源码编译这条主线先整体把 Python 3.8 装好再用虚拟环境固定项目依赖并在第 3 章补充旧 deb 路线的适配场景和止损策略。下面先把最核心的源码编译讲透。2. 源码编译 Python 3.8依赖准备和 configure 抉择2.1 先别急着 configure把编译链和依赖一次性装齐在 Termux 里编译 Python不是你有 clang 就行。Python 标准库在编译过程中会检测一堆底层组件比如 SSL、ffi、zlib、sqlite任何一个缺失都可能导致最终的二进制功能残缺——最典型的是编译完import ssl报错或者_ctypes模块直接消失。我建议在开始前先把依赖一次性装齐pkg update pkg upgrade pkg install clang make pkg-config openssl bison sqlite libffi zlib ncurses这里每个包的解释都值得看一眼clangTermux 的默认编译器是 Clang 而不是 GCC记住这一点很多 Linux 教程会让你apt install gcc在 Termux 里方向就错了。后续编译依赖时CC环境变量也默认指向 clang。makePython 源代码的构建系统没有它一切免谈。pkg-configconfigure 脚本需要用它来探测openssl、libffi等库的头文件和链接路径。openssl提供 SSL/TLS。老教程会写openssl-dev但新版 Termux 仓库里openssl包已经自带头文件openssl-dev早就不单独存在了。装完可以检查/data/data/com.termux/files/usr/include/openssl/ssl.h是否存在。bisonPython 语法分析器的生成器缺失时编译会在Parser/pgen阶段卡住。libffi、sqlite、zlib、ncurses分别对应_ctypes、_sqlite3、zlib压缩支持、终端库等核心模块。如果不装全之后你会不断遇到某个标准库import失败。这些包加起来体积不小但在 Android 上其实还好优先保证一次到位。2.2 下载源码版本configure 参数逐个拆解Python 3.8 的源码建议选择 3.8 分支的最后一个小版本我装的是 3.8.20包含后续的安全修复避免一上来就带着一堆已知漏洞。下载解压wget https://www.python.org/ftp/python/3.8.20/Python-3.8.20.tgz tar -xzf Python-3.8.20.tgz cd Python-3.8.20有人问为什么不用git cloneCPython 的 Git 仓库带了一堆子模块和分支体积大不说在 Termux 的受限网络下还容易 clone 到一半断掉。直接拉官方 tar 包最干净。接下来是重头戏 configure ./configure --prefix$PREFIX --enable-optimizations --with-system-ffi --with-system-expat逐条解释--prefix$PREFIX让 Python 安装在 Termux 的 usr 目录下和系统里其他 apt 包装在一起。不要试着指定自定义路径到/data/localAndroid 的沙箱权限会拒绝写入。--enable-optimizations启用 PGO 优化让解释器运行更快。代价是编译时间几乎翻倍占据的内存也更高。如果你只是跑纯 Python 数据处理可以不加但我首次降级时加了它后续跑模型推理时明显更稳。--with-system-ffi让 Python 使用系统 libffi 而不是内部捆绑版本。Termux 的编译环境有时无法加载内部 libffi 的符号所以这个参数一定要带上否则_ctypes大概率编译失败。--with-system-expat同理让 Python 使用系统的 expat XML 库避免捆绑版本和 Android 的 libc 不兼容。有不少教程会额外建议--disable-shared让 Python 以静态方式把运行库编进可执行文件这样python3.8命令不会在运行时找不到libpython3.8.so。但我实测下来并不推荐原因是如果你后续要装一些需要加载.so扩展的第三方库静态编译的 Python 支持度很差。我宁可让动态库正常生成再去解决链接路径问题后面第 5.3 节会专门讲。2.3 make 的并发数、OOM 规避和 make install 的副作用configure 完成后就是编译。这里有个针对 Android 设备的特殊建议不要无脑make -j$(nproc)。手机的内存和桌面机截然不同2GB 内存的机器跑-j4编译到一半进程就被系统杀掉你只能从头再来。我自己的实测经验是2GB 内存make -j2极限能跑完耗时约 15~20 分钟。4GB 及以上make -j4可以尝试但建议先free -h看一眼剩余内存。命令make -j2 make installmake install这一步的副作用是你必须提前想清楚的。如果 Termux 里已经通过pkg install python装过官方 Python这次安装会在/data/data/com.termux/files/usr/bin/下生成一堆同名或相近的命令——比如python3.8、pip3.8。它不会主动覆盖python3软链但会把python3.8这个新的可执行文件放进去和你现有的官方 Python 共存。这一点既是好事也是坏事好的是你不必忍痛pkg remove python坏的是如果你后续手动把python3指向 3.8系统里其他依赖官方 Python 的命令行工具就会遭殃。我的建议是编译安装完先不要动用独立的python3.8命令去运行项目保持共存。如果你真的要完全替换默认 Python等确认 3.8 可用后再执行pkg remove python然后手动维护软链这样回滚还能有退路。3. 用旧 deb 包硬降级省力路线但依赖冲突是老大难3.1 哪里能翻出 python3.8 的旧 .deb如果你只是想临时跑一个旧脚本不想等十几分钟编译另一条路线是从历史仓库里翻出 python3.8 的.deb文件直接dpkg -i。Termux 官方仓库的历史包其实并没有被删除你可以在包索引的 pool 目录下按名字找例如https://packages.termux.dev/apt/termux-main/pool/main/python/文件名大致长这样python_3.8.x_arm64.deb。要说明的是Termux 官方仓库的包名是python而不是python3.8所以下载前最好先dpkg -I xxx.deb确认包名、版本和依赖关系否则你可能误把一个 3.8 的 python 包当成 python3.8。除了官方历史文件还有社区维护的 tur 仓库Termux User Repository里面的python3.8包会更完整但第三方仓库的依赖名可能与官方不完全一致混用后pkg update时有概率报冲突。我建议把第三方来源仅当作“应急补丁”优先找官方路径。3.2 dpkg 强制安装的完整步骤与回滚策略假如你已经拿到了一个版本匹配的python_3.8.x_arm64.deb执行wget https://.../python_3.8.x_arm64.deb dpkg -i python_3.8.x_arm64.deb如果依赖不满足会出现典型的dpkg: dependency problems报错。很多人第一时间想到的是--force-dependsdpkg -i --force-depends python_3.8.x_arm64.deb这样能强行把包装上但风险是 Termux 的 dpkg 数据库会被“污染”——它会认为 python3.8 已就位而实际上某些依赖库版本不对。之后你再装任何与 Python 相关的包都会触发连锁依赖检查甚至其他命令行工具崩掉。所以如果你决定走硬装请先做好三件止损准备执行前备份$PREFIX/bin/python*和$PREFIX/lib/python3.*到~/python_backup。执行前记录当前与 Python 相关的包列表dpkg -l | grep -i python。装砸了之后的回滚命令是apt reinstall python再手动恢复备份文件。我在一台旧机上试过贪图省事直接硬装了一个老 deb然后pip命令就找不到python3.8的位置连python -V都开始反复横跳。最后还是回到源码编译才把环境救回来。3.3 为什么我不推荐直接卸载系统 python有些教程会说“既然要降级先pkg remove python再装 3.8”。这句话听着干净利落实际上在 Termux 里极容易引发连环事故。因为系统里很多基础工具的管理脚本、pkg自身的后处理脚本、甚至部分插件都隐式依赖 python3 的某些运行环境。你把官方 Python 卸载那些工具不一定立刻崩但等它们需要调用 python 来执行一段辅助脚本时就会因为找不到解释器而报错。所以请记住降级安装 python3.8不代表非要把默认 python 干掉。最好的“降级”是共存隔离让旧版解释器在独立路径下稳定运行而不是把整个 Termux 环境改造成只有一个 Python 的“干净状态”。4. 降级后的版本隔离别让 python3.8 把系统环境搅乱4.1 prefix 编译后生成哪些命令和系统 python 如何共存完成源码安装后$PREFIX/bin下通常会出现python3.8、pip3.8、idle3.8等以版本号命名的命令。这些命令和官方包生成的python3、pip3是两个独立实体互不干扰。如果你在终端里直接输入python3.8 -V应该能看到Python 3.8.20。此时python3 -V大概率还是系统自带的 3.11/3.12两个命令井水不犯河水。如果你希望叫得更顺口可以直接做软链ln -s $PREFIX/bin/python3.8 $PREFIX/bin/python38注意不要轻易把$PREFIX/bin/python3的软链改成指向 python3.8。因为python3这个文件由官方包管理你手动改它之后任何一次pkg upgrade都可能因为文件哈希不匹配而报错还会影响其他脚本的 shebang很多工具的脚本第一行是#!/usr/bin/env python3。4.2 建虚拟环境的最佳时机venv 创建和 pip 配置降级成功不等于项目能跑真正让项目跑起来的是虚拟环境。即使你有多个 python3.8、python3.12如果直接在全局 site-packages 里装依赖迟早会因为版本错乱把自己绕晕。项目目录里执行python3.8 -m venv venv38 source venv38/bin/activate pip install --upgrade pip setuptools这里有个细节Python 3.8 在创建 venv 时需要能找到一个可用的初始化基础环境。如果你的 Termux 里没有任何 python3或者官方 python 也没装venv创建有可能报错。所以我建议始终保留系统的官方 python即使你在项目里只用 3.8。Pip 安装第三方包时如果网络慢可以在项目目录下配置pip.conf指到国内 PyPI 镜像[global] index-url https://pypi.tuna.tsinghua.edu.cn/simple trusted-host pypi.tuna.tsinghua.edu.cn这只是换源不影响 Python 版本选择但能让那些动辄几百 MB 的依赖下载时间从半小时压缩到几分钟。4.3 alias 与 PATH 调整的稳妥做法如果你嫌每次都打python3.8太啰嗦可以在~/.bashrc里加 aliasalias python3.8$PREFIX/bin/python3.8 alias pip3.8$PREFIX/bin/pip3.8为什么要用 alias 而不是软链因为 alias 只影响你交互终端的输入不会改变其他脚本解析 shebang 时的行为。你发出去的脚本第一行如果写了#!/usr/bin/env python3它依然会去找系统 python3不会因为你 alias 了python3.8而意外跑错解释器。这样既享受了旧打字手感又不污染全局执行环境。5. 编译实战中的四个经典报错和排查思路5.1configure提示C compiler cannot create executables这个报错几乎每次都能在 Termux 编译贴里看到。最常见原因是 clang 没装对位置或者环境变量里残留了不合适的CFLAGS。很多老教程会建议export CFLAGS-m64这在旧的 Android 环境上也许有用但现在 Termux 完全靠 configure 自动识别架构你手动设置反而会破坏它。检查方式which clang clang --version env | grep CFLAGS如果发现 CFLAGS 或 LDFLAGS 有残留值直接unset CFLAGS LDFLAGS然后重新 configure。同时记得把之前未完成的编译目录清理干净重新解压源码再走一遍避免config.cache里的脏数据干扰判断。5.2make时_ssl模块构建失败的根因编译完成并安装后如果你进入python3.8执行import ssl然后报ModuleNotFoundError: No module named _ssl这就是编译阶段 OpenSSL 检测失败的典型表现。原因通常是 Termux 的 OpenSSL 版本升到了 3.x而 Python 3.8 的源码里某些调用接口不够兼容或者 configure 根本没找到头文件。排查思路grep -i ssl config.log | head -50看 configure 是否输出了checking for X509_VERIFY_PARAM_set1_host... yes。如果 no就说明 OpenSSL 头文件没被识别。解决办法是回到依赖安装那一步执行pkg reinstall openssl然后删除源码目录里的 build 缓存重新 configure、make。千万不要只装一个libssl-dev之类的包Termux 仓库里根本没有这种包名。5.3make install后python3.8依然找不到libpython3.8.so如果你 configure 时保留了--enable-shared默认安装完成后的python3.8是一个动态链接的可执行文件运行时需要找到$PREFIX/lib/libpython3.8.so.1.0。Termux 不像桌面 Linux 那样有权限执行ldconfig所以系统默认不会主动加载这个新库。现象就是$ python3.8 error while loading shared libraries: libpython3.8.so.1.0: cannot open shared object file解决办法是在~/.bashrc中加上export LD_LIBRARY_PATH$PREFIX/lib然后重新打开一个终端python3.8就能正常启动。这一步几乎每个人都会遇到因为 Termux 的链接器搜索路径并不包含$PREFIX/lib的全部动态库。如果你非常不想用动态库可以回到 configure 阶段加--disable-shared但前面说过后续第三方扩展的 .so 加载会受限我建议还是用动态库加环境变量的组合。5.4pip install编译扩展时编译器报错有了 Python 3.8 之后用pip install安装带 C 扩展的包时你可能会看到类似gcc: command not found或g: command not found的错误。Termux 里面根本没有 gcc只有 clang。解决办法在 configure 阶段就做好铺垫在用./configure之前先告诉编译系统我们后续要用 clangexport CCclang export CXXclang这样 configure 和后续 setuptools 构建扩展时自动继承这个编译器选择。如果环境变量已经设晚可以强制重装一个具体扩展pip install --force-reinstall --no-cache-dir 包名同时确认pkg install binutils make已经装好。大多数纯接口的 C 扩展在这套组合下都能顺利编译通过。最后再分享一点我自己的体会如果你只是临时想在一台已经升级到 Python 3.12 的 Termux 上“降级”跑个旧脚本最省心的方法不是立刻开始编译而是先想想那个脚本能不能用 Docker 容器或者云上的一台 3.8 环境先跑通。因为 Termux 的 Python 3.8 源码编译虽然能成功但它毕竟是 Android 适配环境库版本和桌面 Linux 还存在细微差别。要是脚本里调用了太多系统层面的绝对路径即使你在 Termux 里降级到 3.8也未必能直接跑。真正愿意折腾 Termux 降级的人多半是需要在手机上离线处理数据或者和我一样对旧库有一种“改代码不如改环境”的执念。只要把版本隔离做好编译流程走顺这套环境其实是相当稳定的——我目前的主力机就用python3.8挂着两三个数据分析任务运行了整整一个月没有崩过。