在jupyter notebook中,!前缀可直接执行linux命令,但每条命令运行于独立shell进程,cd等状态变更不跨单元格生效;需用os.chdir()或%%bash多行脚本维持路径,命令输出默认不返回变量,捕获结果应使用subprocess.run()。

在 Jupyter Notebook 单元格里直接运行 Linux 命令
只要内核是 Python(默认),你就能在代码单元格里用 ! 前缀执行任意 Linux 终端命令,比如 !ls -la、!pwd、!pip list。这是最常用也最轻量的方式,不需要写 Python 调用逻辑。
注意:命令在当前 notebook 的工作目录下执行,该目录由 jupyter notebook 启动时的路径或配置中的 c.NotebookApp.notebook_dir 决定,不是你 SSH 登录时的家目录。
-
!后面跟的是 shell 命令,不是 Python 语法;写错会报CommandNotFoundError或直接输出错误信息 - 命令输出实时显示在单元格下方,但**不返回给 Python 变量**;如果需要捕获结果,得用
%%bash魔法命令或subprocess - 环境变量继承自启动 Jupyter 的 shell,所以虚拟环境里的
pip、python路径通常可用,但 PATH 没被重置——这点容易误判命令是否真在当前环境里运行
用 %%bash 魔法命令批量执行多行 shell 脚本
当你要跑一段带变量、循环或条件判断的 shell 脚本时,! 就不够用了。这时候用 %%bash 魔法命令,它会把整个单元格当作一个独立 shell 脚本执行。
示例:
%%bash for f in *.py; do echo "Found: $f" done echo "Total $(ls *.py | wc -l) Python files"
-
%%bash必须独占第一行,且不能和!混用 - 变量展开、管道、重定向都按标准 bash 行为工作,但注意 notebook 内核本身仍是 Python,所以这些只是“借壳执行”,不改变 Python 进程状态
- 如果脚本里调用了需要交互的命令(如
ssh、sudo),会卡住——Jupyter 不提供 TTY,得提前配好免密或改用 key-based 认证
在 Python 代码里安全调用并捕获命令结果
当你需要把命令输出当数据处理(比如解析 df -h 结果、检查某个进程是否存在),就得用 Python 的 subprocess 模块,而不是依赖魔法命令。
Linux系统管理专家,覆盖12大模块:用户权限、SSH、存储、网络、systemd、防火墙、日志监控、备份恢复、TLS证书、Ansible、容器、IaC。提供配置、验证、加固、监控、备份、自动化、故障排查、回滚闭环。关键词:useradd、sudo、sshd_config、chmod、SEL...
推荐用 subprocess.run(),明确指定 capture_output=True 和 text=True:
import subprocess
result = subprocess.run(['ls', '-l', '/tmp'], capture_output=True, text=True)
if result.returncode == 0:
print(result.stdout[:200]) # 只看前 200 字符
else:
print("Error:", result.stderr)
- 别用
os.system()或os.popen():它们难控制、易受注入攻击,且无法可靠获取退出码 - 参数必须是列表形式(如
['ls', '-l']),避免 shell 注入;如果真要走 shell 解析(比如用|管道),显式加shell=True,但输入必须可信 - 远程服务器上若启用了
--allow-root或以 root 运行 notebook,subprocess也会继承权限——这点常被忽略,可能造成意外的高权限操作
为什么 cd 在 ! 里不生效
每次 ! 命令都启动一个新 shell 进程,执行完就退出。所以 !cd /data 看似运行了,但下一单元格的 !ls 仍在原目录——cd 没法跨进程持久化。
正确做法只有两个:
- 用 Python 切换:
import os; os.chdir('/data'),之后所有!和 Python 文件操作都在新目录 - 在单个
%%bash里连写:%%bash\ncd /data\nls -l,靠同一 shell 生命周期维持路径
这个限制很基础,但新手几乎必踩一次;它背后是进程隔离的本质,不是 Jupyter 的 bug,也改不了。










