运维常用内置模块
上一篇过了语法和工具链,这篇进入正题:标准库内置模块。不用 pip 装任何东西,import 就能用,覆盖了运维脚本的绝大部分需求。每个模块按「用途一句话 + 可跑示例 + 注意点」来记。环境 Python 3.12+,macOS / Linux,示例均可直接运行。
os 与 pathlib
用途:os 管环境变量和系统交互,pathlib 管路径——拼路径、判断存在、按模式找文件。
import os
from pathlib import Path
# 环境变量:get 带默认值,不存在不报错
home = os.environ.get('HOME')
env = os.environ.get('APP_ENV', 'prod') # 没设 APP_ENV 时用 'prod'
# 路径拼接:/ 运算符,不用操心分隔符
log_dir = Path('/var/log') / 'nginx'
print(log_dir) # /var/log/nginx
print(log_dir.exists()) # 是否存在
# glob 按模式找文件
for f in Path('/var/log').glob('*.log'):
print(f.name, f.stat().st_size)
注意点:
- 老代码里的
os.path.join()、os.path.exists()功能 pathlib 全都有,新写的脚本统一用 pathlib,面向对象的路径对象好读好传。os.path知道存在就行,维护老脚本时会遇到。 - 环境变量读出来都是字符串,要数字记得
int(os.environ.get('PORT', '22'))。
subprocess
用途:在 Python 里调外部命令——systemctl、df、ssh、docker,shell 能干活的命令这里都能接住。
正确姿势是 subprocess.run 加列表传参:
import subprocess
result = subprocess.run(
['df', '-h', '/'], # 命令和参数拆成列表,一个元素一项
capture_output=True, # 接住 stdout/stderr
text=True, # 输出按字符串解码,不给 bytes
timeout=10, # 超时保护,防止命令挂死脚本
)
print('返回码:', result.returncode)
print('输出:', result.stdout)
想让它执行失败时直接抛异常,加 check=True:
subprocess.run(['systemctl', 'is-active', 'nginx'],
capture_output=True, text=True, check=True)
注意点:
shell=True有注入风险。subprocess.run(f'ls {user_input}', shell=True)这种写法,用户输入里塞个; rm -rf ...就完了。列表传参让参数原样交给程序、不经 shell 解析,能不用shell=True就不用;必须用管道/重定向时才开,且绝不拼接外部输入。- 忘了
text=True的话result.stdout是 bytes,正则匹配、字符串判断都会报错。 - 返回码不为 0 不代表抛异常——不加
check时要自己看returncode,巡检脚本里经常是「非 0 就告警」的判断依据。
shutil
用途:文件和目录的「高级操作」——拷贝、移动、删目录树,外加查磁盘、找命令这两个巡检常客。
import shutil
shutil.copy('/etc/hosts', '/tmp/hosts.bak') # 拷文件
shutil.move('/tmp/hosts.bak', '/tmp/hosts.old') # 移动 / 重命名
# shutil.rmtree('/tmp/old_dir') # 递归删目录
# 巡检常用:看磁盘,返回总容量/已用/剩余(字节)
total, used, free = shutil.disk_usage('/')
print(f'根分区剩余 {free / 1024**3:.1f} GiB')
# 找命令在哪,找不到返回 None
print(shutil.which('nginx'))
注意点:
rmtree是递归删除、不可恢复,路径一定要确认再执行,脚本里删目录前先打印出来人工核对一次。disk_usage在挂载点上查就传挂载点路径,比如shutil.disk_usage('/data')。- 判断某台机器装没装某个工具,
which返回 None 比 try 执行命令优雅。
json 与 yaml
用途:读写结构化数据。json 是内置的;yaml 靠第三方库 PyYAML,运维配置文件大多是这两种格式。
import json
# 读
with open('servers.json', encoding='utf-8') as f:
servers = json.load(f)
# 写:ensure_ascii=False 让中文正常落盘,indent 排版好看
with open('out.json', 'w', encoding='utf-8') as f:
json.dump(servers, f, ensure_ascii=False, indent=2)
PyYAML 要先装(记得在虚拟环境里):
pip install pyyaml
读配置文件的六行模板,抄走即用:
import yaml
with open('config.yaml', encoding='utf-8') as f:
config = yaml.safe_load(f)
print(config['database']['host'])
注意点:
- 必须用
safe_load,别用load。老的load会执行 YAML 里构造的任意 Python 对象,读不可信来源的 YAML 等于远程执行代码。 json.load/dump操作文件对象,json.loads/dumps操作字符串,多一个 s,别混。- YAML 里
on/off、yes/no会被解析成布尔值,写配置时想保留字符串就加引号。
datetime 与 time
用途:时间戳互转、格式化输出、算时间差——日志时间处理、清理过期文件的判断全靠它。
from datetime import datetime, timedelta
now = datetime.now()
ts = now.timestamp() # datetime -> 时间戳
dt = datetime.fromtimestamp(ts) # 时间戳 -> datetime
print(now.strftime('%Y-%m-%d %H:%M:%S')) # 格式化:2026-10-25 09:30:00
# 算时间差:7 天前的分界线
deadline = now - timedelta(days=7)
典型场景:清理 7 天前的旧日志,拿文件的修改时间和分界线比:
import os
from datetime import datetime, timedelta
deadline = datetime.now() - timedelta(days=7)
mtime = datetime.fromtimestamp(os.path.getmtime('/var/log/app/old.log'))
if mtime < deadline:
print('超过 7 天,可以清理')
注意点:
strptime是「字符串解析成时间」,strftime是「时间格式化成字符串」,p 是 parse、f 是 format。- 跨时区的对时用带时区的
datetime.now(tz=...),单机运维脚本用本地时间一般够了。
logging
用途:脚本别再用 print 输出运行信息了,logging 带级别、带时间戳,能同时写控制台和文件。
级别从低到高:DEBUG → INFO → WARNING → ERROR → CRITICAL。巡检脚本平时开 INFO,排障时开 DEBUG。
同时输出到控制台和文件的最小完整配置:
import logging
logger = logging.getLogger('ops')
logger.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
# 控制台只看 INFO 及以上
console = logging.StreamHandler()
console.setLevel(logging.INFO)
console.setFormatter(formatter)
# 文件里记全部,包括 DEBUG
file_handler = logging.FileHandler('ops.log', encoding='utf-8')
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(formatter)
logger.addHandler(console)
logger.addHandler(file_handler)
logger.debug('排查细节')
logger.info('巡检完成')
logger.error('磁盘超阈值')
注意点:
- 级别是「门槛」:handler 设 INFO,DEBUG 消息到不了这个 handler;logger 本身的级别先过滤一轮,所以 logger 开 DEBUG、各 handler 再各管各的。
- 日志文件会无限长大,生产脚本把
FileHandler换成logging.handlers.RotatingFileHandler,按大小自动轮转留几个备份,一句话:RotatingFileHandler('ops.log', maxBytes=10*1024*1024, backupCount=5)。
re 正则
用途:从日志、命令输出里提取想要的东西——IP、状态码、错误行,运维文本处理的瑞士刀。
从一行 nginx 日志里提取 IP 和状态码:
import re
log = '192.168.1.10 - - [25/Oct/2026:09:30:00 +0800] "GET /api/status HTTP/1.1" 200 1234'
# search:找第一个匹配
m = re.search(r'(\d+\.\d+\.\d+\.\d+)', log)
print(m.group()) # 192.168.1.10
# 分组提取:引号结束后的三位数字是状态码
m = re.search(r'"\s(\d{3})\s', log)
if m:
print(m.group(1)) # 200
# findall:找全部,返回列表
ips = re.findall(r'\d+\.\d+\.\d+\.\d+', log)
常用模式小表:
| 模式 | 匹配什么 |
|---|---|
\d+\.\d+\.\d+\.\d+ | IPv4 地址(粗匹配,不校验 0-255) |
\d{3} | 三位数字,比如 HTTP 状态码 |
\w+ | 字母数字下划线,匹配单词 |
\s+ | 一个以上空白字符 |
^ERROR | 行首的 ERROR(配合 re.M 按行匹配) |
[0-9a-f]{12} | MAC 地址这类固定长度十六进制串 |
注意点:
- 模式串前面加
r(原始字符串),反斜杠不用写两遍。 search找不到返回 None,直接.group()会报 AttributeError,先判 if。- 模式里带
|表示「或」,比如'ERROR|WARN',匹配两类日志级别。
argparse
用途:给脚本做命令行参数——位置参数、可选参数、默认值、类型、自动生成的帮助,脚本交给别人用时全靠它撑门面。
带 -v verbose 开关的脚本骨架,抄走改成自己的:
#!/usr/bin/env python3
"""巡检脚本骨架"""
import argparse
def main():
parser = argparse.ArgumentParser(description='服务器巡检脚本')
parser.add_argument('host', help='目标主机名或 IP')
parser.add_argument('-p', '--port', type=int, default=22,
help='SSH 端口,默认 22')
parser.add_argument('-t', '--timeout', type=int, default=10,
help='超时秒数,默认 10')
parser.add_argument('-v', '--verbose', action='store_true',
help='输出调试信息')
args = parser.parse_args()
if args.verbose:
print(f'[debug] 目标 {args.host}:{args.port},超时 {args.timeout}s')
# 真正干活的代码写在这里
if __name__ == '__main__':
main()
跑起来看看:
python3 check.py web01 --port 2222 -v
[debug] 目标 web01:2222,超时 10s
python3 check.py -h
usage: check.py [-h] [-p PORT] [-t TIMEOUT] [-v] host
服务器巡检脚本
positional arguments:
host 目标主机名或 IP
options:
-h, --help show this help message and exit
-p PORT, --port PORT SSH 端口,默认 22
-t TIMEOUT, --timeout TIMEOUT
超时秒数,默认 10
-v, --verbose 输出调试信息
注意点:
- 位置参数(
host)必填,-开头的可选参数都有default;type=int会自动做转换,传了非数字 argparse 自己报错退出。 action='store_true'是开关写法:不加-v时args.verbose是 False,加了就是 True,不需要跟值。-h帮助是免费的,用户拿到脚本第一条命令就是-h,把每个参数的help写清楚比写文档实在。
小结
- 路径用
pathlib,环境变量用os.environ.get,老代码里的os.path认识即可。 - 调外部命令就
subprocess.run+ 列表传参 +capture_output+text+timeout,shell=True能躲就躲。 shutil管文件搬运和disk_usage磁盘巡检;rmtree删目录前再三确认。- 配置读写:json 内置,yaml 装 PyYAML 且只认
safe_load。 - 时间用
datetime+timedelta算差值;日志用logging双 handler 同时落控制台和文件;文本提取用re;参数解析用argparse。
这八个模块组合起来,就是一台机器上巡检、清理、采集、上报的完整工具箱。下一篇开始用第三方库:requests 调 API、paramiko 走 SSH,把脚本从单机延伸到网络。