跳到主要内容

运维常用内置模块

上一篇过了语法和工具链,这篇进入正题:标准库内置模块。不用 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,把脚本从单机延伸到网络。