subprocess 是不装第三方库时执行系统命令并捕获指标的唯一可靠接口,需用 run() 显式指定 text=True 和 encoding,配合 threading.Timer 定时、logging 记录、sys.exit() 返回规范退出码。

用 subprocess 执行系统命令并捕获关键指标
不装任何第三方库时,subprocess 是你唯一可靠的“外挂接口”。它能调用 psutil 做不到的事——比如读取 /proc/meminfo(Linux)或 wmic(Windows),且无依赖、跨 Python 版本稳定。
常见错误是直接用 os.popen 或 os.system:前者难控制编码和超时,后者根本拿不到输出。必须用 subprocess.run() 并显式指定 text=True 和 encoding='utf-8'(尤其 Windows 下 cmd 输出可能是 gbk)。
- Linux 示例:监控内存使用率 →
subprocess.run(['grep', 'MemAvailable', '/proc/meminfo'], capture_output=True, text=True) - Windows 示例:查 CPU 占用前 3 进程 →
subprocess.run(['wmic', 'process', 'get', 'name,percentprocessorusage', '/order', 'percentprocessorusage DESC', '/limit=3'], capture_output=True, text=True) - 超时必须加:
timeout=5,否则卡死进程无法回收
用 threading.Timer 实现低开销周期轮询
别用 time.sleep() 配 while True——它阻塞主线程,一次异常就停摆;也别自己写信号量或锁,纯监控场景不需要并发安全。
threading.Timer 是标准库里最轻的“定时触发器”:每次检查完,立刻启动下一轮,错峰执行,失败也不影响后续调度。
立即学习“Python免费学习笔记(深入)”;
- 每 30 秒查一次磁盘空间:
threading.Timer(30.0, check_disk_usage).start() - 务必在回调函数末尾再启动下一轮,避免因异常跳过:
try: ... finally: threading.Timer(30.0, check_disk_usage).start() - 不要重复创建 Timer 对象存变量——没意义,Python 的 Timer 一次性执行后自动销毁
用 logging 写入本地日志而非 print 或文件 open
print() 会混入 stdout/stderr,难以区分监控日志和调试输出;手写 open(..., 'a') 容易因多线程写入导致乱码或丢行。
logging 模块自带线程安全、自动轮转(配合 RotatingFileHandler)、格式统一,且零依赖。
- 基础配置只需三行:
logging.basicConfig(filename='monitor.log', level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s') - 写日志统一用:
logging.info('CPU usage: %s%%', cpu_pct),避免字符串拼接 - 若需按天切日志,改用
TimedRotatingFileHandler,但注意它的when='midnight'在非 UTC 时区可能漂移
用 sys.exit() + 退出码做外部集成信号
监控工具常被 systemd、cron 或 shell 脚本调用。仅靠日志不够,必须通过进程退出码告诉调用方“是否异常”。
标准做法:正常完成返回 0;关键指标越界(如内存 >95% 持续 2 分钟)返回 1;命令执行失败(如 wmic 不可用)返回 2。
- 不要用
exit()或抛未捕获异常——它们可能被上层 try 包住,导致退出码为 0 - 最终主逻辑结尾写:
sys.exit(exit_code),确保 exit_code 是整数且范围在 0–127(POSIX 合规) - shell 调用示例:
python monitor.py && echo "OK" || echo "ALERT"
真正的难点不在代码长短,而在于每个子命令的兼容性边界:比如 df -h 在 Alpine Linux 上不支持 --output,wmic 在 Win10 1809+ 才默认启用。写之前先在目标环境手动跑通命令,再封装进 subprocess。


















