PAYATHON 2026

Windows 11 中 Jupyter Notebook 的 bash kernel 崩溃如何排查?

排障赵师傅

先处理 pexpect.spawn 异常

AttributeError: module 'pexpect' has no attribute 'spawn' 与 Bash 内核崩溃直接相关,应先排查这个错误。日志中的 KernelRestarter: restarting kernel 表明内核进程已经退出,Jupyter 正在尝试重启它。

从 kernel.json 看,内核运行的是 Windows 原生的 python.exe。常见的 bash_kernel 实现通过 Pexpect 控制交互式 Bash,所用的 pexpect.spawn 依赖 POSIX 伪终端。Windows 原生 Python 下的 Pexpect 通常不提供这个接口,安装 Git Bash 也无法补上这项能力。

建议在 WSL 中同时运行 Linux Python、bash_kernel 和 Jupyter。仅修改 bash.exe 路径或清理 cookies,无法解决这类平台兼容问题。

浏览器显示的“Connection failed”还可能涉及独立的服务器连接故障。现有日志能确认内核启动失败,但不能排除网络或 WebSocket 问题。

为什么安装了 Git Bash 仍然失败?

运行 Bash 内核需要满足两个条件:

  • 系统能找到并运行 Bash。
  • 运行 bash_kernel 的 Python 具备所需的交互式进程控制能力。

where bash 能找到可执行文件,只能验证第一个条件。即使选中了 Git Bash,Windows Python 仍可能在调用 pexpect.spawn 时退出,此时还没有执行到启动 Bash 的步骤。从 Git Bash 终端启动 Windows python.exe,也不会让它变成 Linux Python。

题目中的 kernel.json 使用以下启动方式,结构上是合理的:

python.exe -m bash_kernel -f {connection_file}

这里的 Python 模块实现 Jupyter 内核协议,并管理 Bash 子进程。普通的 bash.exe 不实现 Jupyter 内核协议,因此不能直接替换 argv 中的 python.exe。

确认环境并收集完整报错

1. 显式激活 bash_env

创建 Conda 环境后,还需要激活它。题目给出的操作流程缺少这一步,后续安装命令可能在其他环境中执行了。

在能够使用 Conda 的 Windows 命令提示符中执行:

conda activate bash_env

where.exe python
where.exe jupyter
where.exe bash

python -c "import sys; print(sys.executable); print(sys.platform)"
python -m pip show bash_kernel pexpect

检查 sys.executable 是否指向:

C:\Users\myusername\AppData\Local\ESRI\conda\envs\bash_env\python.exe

where.exe 可能列出多个结果,实际使用的解释器以 sys.executable 为准。

还需要确认 Python、bash_kernel 和 pexpect 的实际版本。仅凭 jupyter_server、ipykernel 和 ipython 的版本,无法判断整套环境是否兼容。

2. 检查 Pexpect 的来源和接口

继续在同一环境中执行:

python -c "import pexpect; print(pexpect.__file__); print('has spawn:', hasattr(pexpect, 'spawn'))"

如果使用的是 Windows 原生 Python,且结果为:

has spawn: False

这个结果符合上述平台限制,不能直接认定 Pexpect 安装损坏。

同时检查 pexpect.__file__:

  • 路径位于环境的 site-packages,说明加载的是环境中的包。
  • 路径指向项目里的 pexpect.py 或同名目录,可能存在模块遮蔽。先重命名本地文件或目录,再重启内核。

如果在 Linux 或 WSL 中正常安装的 Pexpect 仍然缺少 spawn,应继续检查包安装和导入过程,不能一概归为 Windows 平台问题。

3. 查看服务器实际使用的内核配置

在启动 Jupyter 的终端中执行:

python -m jupyter kernelspec list --json

检查 bash 对应的 resource_dir 和 argv。用户目录中的 kernelspec 指向 Conda 环境里的 Python 是正常用法,配置文件不必与 Python 安装在同一目录。

删除用户目录中的 bash 配置后,系统不会自动生成环境级配置。Jupyter 只能发现其他搜索路径下已经存在的 Bash kernelspec。

随后停止原来的 Jupyter 服务,再从该环境启动 Jupyter 并输出调试日志:

python -m jupyter notebook --debug

选择 Bash 内核,保留从启动到退出的完整 traceback,尤其是调用 pexpect.spawn 前的文件名和行号。如果该环境没有安装 Notebook,就使用原来启动 Jupyter 的环境和入口收集日志。

frozen modules 和调试器相关内容属于警告。调整 -Xfrozen_modules=off 或 PYDEVD_DISABLE_FILE_VALIDATION,无法补上缺失的 pexpect.spawn 接口。

推荐方案:在 WSL 中运行整套 Bash Notebook 环境

在 WSL 中,Bash 和 Python 都运行在 Linux 下,可以使用 bash_kernel 依赖的 POSIX 终端机制。

1. 安装并进入 WSL

如果尚未安装 WSL,可在管理员 PowerShell 中执行:

wsl --install -d Ubuntu

按提示完成安装、必要的重启和 Linux 用户初始化。如果已经安装 Ubuntu,直接打开其终端即可。

2. 在 Ubuntu 中创建独立环境

在 Ubuntu 终端执行:

sudo apt update
sudo apt install -y python3 python3-venv python3-pip bash

python3 -m venv ~/.venvs/jupyter-bash
source ~/.venvs/jupyter-bash/bin/activate

python -m pip install --upgrade pip
python -m pip install notebook bash_kernel
python -m bash_kernel.install

检查运行环境:

python -c "import sys, pexpect; print(sys.executable); print(sys.platform); print(hasattr(pexpect, 'spawn'))"
python -m jupyter kernelspec list --json

确认以下结果:

  • Python 路径是 Linux 路径,而非 Windows 的 python.exe。
  • sys.platform 为 linux。
  • Pexpect 提供 spawn。
  • Bash kernelspec 的 argv 指向刚创建的 Linux Python。

3. 从 WSL 启动 Jupyter

仍在已激活的虚拟环境中执行:

python -m jupyter notebook --no-browser

用 Windows 浏览器打开终端给出的访问地址,保留地址中的 token。选择 Bash 内核后运行:

printf 'Bash version: %s\n' "$BASH_VERSION"
uname -s
pwd

命令能返回结果,且内核不再反复重启,才说明 Bash 内核能够正常启动并执行命令。

WSL 中注册的 kernelspec 通常供 WSL 中的 Jupyter 使用。如果只在 WSL 安装内核,仍使用原来的 Windows Jupyter 服务,跨系统内核配置不会自动完成。

Windows 的 bash_env 也不能直接复制为 Linux Python 环境。原环境中的其他依赖需要按 Linux 平台重新确认和安装。

如果只需要在 Notebook 中执行几条 Bash 命令

可以保留 Windows 的 Python 内核,通过 subprocess 显式调用 Git Bash,绕开独立 Bash 内核的 Pexpect 启动流程。

在 Python 单元格中执行:

import subprocess

result = subprocess.run(
    [
        r"C:\Program Files\Git\usr\bin\bash.exe",
        "-lc",
        "printf 'hello from bash\\n'",
    ],
    check=True,
    capture_output=True,
    text=True,
)

print(result.stdout, end="")

路径以本机实际安装位置为准。题目中还存在 C:\Windows\System32\bash.exe,显式指定 Git Bash 路径可以避免选错入口。

这种方式适合执行独立命令。每次调用都会创建新的 Bash 进程,前一次调用中的变量、工作目录变更和函数定义不会自动保留到下一次调用。

内核修复后仍提示连接失败怎么办?

先新建一个 Python Notebook,运行:

print("kernel connection ok")

如果 Python 内核正常,只有 Bash 内核失败,就继续检查 Bash 内核的启动日志、解释器路径和依赖。

如果所有内核都无法连接,或 Notebook 页面持续断开,再检查 Jupyter 服务是否仍在运行、访问地址和 token 是否正确,以及代理、反向代理或 WebSocket 连接是否异常。使用 WSL 时,还要确认浏览器访问的端口与 WSL 中实际启动的服务一致。

备注:内容仅供参考。