文章目录
sh:在 Python 里直接调系统命令
如果写过 Python,大概率用过 subprocess 模块。每次想调一个系统命令,得 import subprocess,再 Popen 或者 run,参数拼成列表,处理返回值,捕获异常。一套流程下来,代码量翻了几倍。这种体验说不上差,但总觉得可以更直接。
sh 这个库的思路就是:把系统命令变成 Python 函数调用。装了之后,你操作的就是一个可调用对象:
python
from sh import git
git("status", "--short")
git 就是一个 Python 函数,不用拼字符串、不用管 shell 参数解析。sh 支持 Python 3.8 到 3.14 以及 PyPy,pip 安装就能用。项目在 GitHub 上已有 7,200+ Star,作者 Andrew Moffat 维护了超过十年,稳定性经得起考验。

解决了什么问题
Python 的 subprocess 抽象层级低。你要执行一条命令,写的却是进程管理代码:创建进程、等待结束、读取输出、检查返回值。sh 把底层细节藏起来,让你关注「做什么」,不用操心「怎么启动进程」。
sh 的映射规则很简单:每个系统命令封装成一个可调用对象,命令参数直接当函数参数传,关键字参数对应命令行选项。ls -l 写成 ls("-l"),git status --short 写成 git("status", "--short")。这种对应关系不需要查文档就能猜到。
举几个实际用法。逐行读取日志文件:
python
from sh import tail
for line in tail("-f", "/var/log/syslog", _iter=True):
print(line)
带超时的文件查找:
python
from sh import find
try:
find(".", "-name", "*.py", _timeout=10)
except sh.TimeoutException:
print("查找超时")
sh 还提供 _out、_err、_done、_bg 等参数控制输出、错误处理、回调和异步执行。这些参数用下划线前缀标记,和普通命令参数区分开。需要后台执行时加个 _bg=True 就行,sh 会自动处理进程生命周期。
实际用起来怎样
日常就是一行函数调用。不用记 subprocess 的 Popen 参数列表,不经过 shell 所以没有命令注入风险。出错时抛的异常自带 stdout 和 stderr,不用手动捕获和拼接,排查方便。

局限也有。sh 依赖 Unix 系统调用,Windows 完全不能用,在纯 Windows 环境工作的需要留意。命令名中带特殊字符(如连字符)时要用 sh.Command() 来调用。每次调用都启动新进程,高频循环场景下性能需要评估。
文档覆盖了从安装到高级用法的所有内容,还提供了一整页的完整版方便丢给 LLM 做上下文参考。
适用场景
如果你经常在 Python 里调系统命令,或者做自动化运维、CI/CD 相关工作,sh 值得一试。它把一个高频操作简化到极致,不做多余的事。和其他同类方案不同,sh 没有自己去实现系统命令的功能,只是做了一层薄封装。这种克制是它能维护十年的原因之一。
sh 的底层机制是高阶函数封装,通过重载 __call__ 把子进程调用伪装成普通函数调用。代码写起来顺手,读起来也自然。如果你觉得 subprocess 用着太啰嗦,从 sh 开始可能就回不去了。
调用。代码写起来顺手,读起来也自然。如果你觉得 subprocess 用着太啰嗦,从 sh 开始可能就回不去了。