前言

Python “自带电池”——标准库覆盖了运维脚本 80% 的需求。这篇盘点我使用频率最高的那些,每个都给实战片段。

一、os / sys / pathlib:系统交互

import os, sys
from pathlib import Path

# 环境与退出
os.environ.get("HOME", "/root")     # 读环境变量(带默认值)
os.getpid(), os.getcwd()
sys.exit(1)                          # 带状态码退出(脚本规范)

# pathlib 三板斧(上篇讲过, 这里补充)
for f in Path("/var/log").rglob("*.log"):     # 递归找
    print(f, f.stat().st_size)

# 执行外部命令的正确姿势见 subprocess(别用 os.system)

二、collections:数据结构增强

from collections import Counter, defaultdict, deque, OrderedDict

# Counter: 计数器之王
c = Counter(["go", "py", "go", "sh", "go"])
c.most_common(2)                  # [('go', 3), ('py', 1)]
c["go"]                           # 3(不存在的 key 返回 0, 不报错)

# defaultdict: 消灭 if key not in
groups = defaultdict(list)
for name, dept in [("a", "ops"), ("b", "dev"), ("c", "ops")]:
    groups[dept].append(name)
# {'ops': ['a', 'c'], 'dev': ['b']}

logs = defaultdict(int)           # 默认 0: 直接 += 1 不会 KeyError
logs["error"] += 1

# deque: 双端队列(队列/栈/滑动窗口)
q = deque(maxlen=5)
for i in range(10):
    q.append(i)                   # 自动挤掉最老的
# deque([5, 6, 7, 8, 9])

q.appendleft(0)                   # O(1) 头部插入, list 是 O(n)

三、itertools:迭代器魔法

from itertools import chain, islice, groupby, product, combinations

# chain: 拼接多个可迭代对象
list(chain([1, 2], [3], range(4, 6)))      # [1, 2, 3, 4, 5]

# islice: 切迭代器(不能下标的对象)
list(islice(range(100), 5, 10))            # [5, 6, 7, 8, 9]

# product: 笛卡尔积(替代多层 for)
for env, svc in product(["prod", "staging"], ["web", "api"]):
    print(f"{env}-{svc}")

# groupby: 分组(注意: 先排序再分组!)
data = sorted([("a", 1), ("b", 2), ("a", 3)], key=lambda x: x[0])
for k, g in groupby(data, key=lambda x: x[0]):
    print(k, list(g))            # a [('a',1),('a',3)] / b [('b',2)]

四、datetime:时间处理

from datetime import datetime, timedelta, timezone

now = datetime.now()
utc = datetime.now(timezone.utc)             # 时区 aware(推荐!)

# 格式化与解析
now.strftime("%Y-%m-%d %H:%M:%S")            # '2024-06-15 10:30:00'
datetime.strptime("2024-06-15", "%Y-%m-%d")  # 字符串 -> datetime
datetime.fromisoformat("2024-06-15T10:00")   # ISO 格式直解

# 运算
yesterday = now - timedelta(days=1)
(now - yesterday).days                       # 1
now.timestamp()                              # -> Unix 时间戳
datetime.fromtimestamp(1718419200)           # <- Unix 时间戳

# 时区转换
tz_sh = timezone(timedelta(hours=8))
utc.astimezone(tz_sh)                        # UTC -> 北京时间

五、subprocess:执行命令(重点)

import subprocess

# ✅ 推荐写法: run + capture(3.5+)
r = subprocess.run(
    ["df", "-h"],                         # 列表形式(不经 shell, 更安全)
    capture_output=True, text=True, timeout=10,
)
print(r.returncode)        # 0
print(r.stdout)

# 带 shell 管道时才用 shell=True(注意注入风险!)
r = subprocess.run("df -h | grep data", shell=True,
                   capture_output=True, text=True)

# 失败抛异常(写脚本报错即停)
r = subprocess.run(["ls", "/nope"], check=True, capture_output=True, text=True)

# 实战: 检查服务是否存活
def service_active(name: str) -> bool:
    r = subprocess.run(["systemctl", "is-active", name],
                       capture_output=True, text=True)
    return r.stdout.strip() == "active"

六、argparse:像样的命令行参数

import argparse

p = argparse.ArgumentParser(description="主机巡检工具")
p.add_argument("hosts", nargs="+", help="目标主机列表")
p.add_argument("-p", "--port", type=int, default=22)
p.add_argument("-v", "--verbose", action="store_true")
args = p.parse_args()

# python check.py web01 web02 -p 2222 -v
# args.hosts=['web01','web02'] args.port=2222 args.verbose=True

七、json / re / hashlib / secrets:日常刚需

import json, re, hashlib, secrets

# json(上篇详述)
json.dumps({"k": "值"}, ensure_ascii=False, indent=2)

# re: 三个够用的函数
re.search(r"(\d+\.\d+\.\d+\.\d+)", text)        # 找第一处(返回 match)
re.findall(r"ERROR.*", log)                       # 找全部
re.sub(r"\s+", " ", s)                            # 替换
# 预编译(循环里高频使用时)
IP = re.compile(r"^(?:\d{1,3}\.){3}\d{1,3}$")
IP.match("10.0.0.1")

# hashlib: 校验文件
h = hashlib.sha256()
for chunk in iter(lambda: open("app.tar.gz","rb").read(8192), b""):
    h.update(chunk)
print(h.hexdigest())

# secrets: 生成安全随机串(密码/Token, 别用 random!)
secrets.token_urlsafe(32)

八、logging:别再 print 了

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
)
log = logging.getLogger(__name__)

log.info("开始巡检 %s", "web01")       # 惰性格式化, 占位符写法
try:
    1 / 0
except ZeroDivisionError:
    log.exception("计算失败")          # 自动带堆栈

九、综合实战:一行式巡检脚本

#!/usr/bin/env python3
"""快速体检: 磁盘/内存/负载"""
import subprocess, shutil, os

def sh(cmd: str) -> str:
    r = subprocess.run(cmd, shell=True, capture_output=True, text=True)
    return r.stdout.strip()

def check_disk(threshold=80):
    for line in sh("df -h --output=pcent,target").splitlines()[1:]:
        pct, mount = line.split()
        if int(pct.rstrip("%")) > threshold:
            print(f"[告警] {mount} 使用率 {pct}")

def check_load():
    cores = os.cpu_count()
    load = float(sh("cat /proc/loadavg").split()[0])
    status = "告警" if load > cores else "正常"
    print(f"[{status}] load {load} / {cores} cores")

def check_mem():
    total, _, free, _, _, avail = sh("free -b").splitlines()[1].split()[1:]
    print(f"[信息] 可用内存 {int(avail)/2**30:.1f}G")

if __name__ == "__main__":
    check_disk(); check_load(); check_mem()

零第三方依赖,拷到任何有 Python3 的机器就能跑。

小结

库 一句话
pathlib / os 文件与系统
collections Counter 计数、defaultdict 分组
itertools chain/product/islice
subprocess run + 列表参数 + timeout
argparse 脚本参数
logging 替代 print

本文是「Python」系列第 6 篇。