当前位置:首页>python>Python教程 - 自动化运维与脚本实战

Python教程 - 自动化运维与脚本实战

  • 2026-09-10 04:28:28
Python教程 - 自动化运维与脚本实战

欢迎来到 Python 自动化运维专题!学到这里你已经掌握了变量、循环、函数、类、爬虫、数据分析、异步编程等核心技能。现在我们要换个口味——让 Python 成为你的"万能瑞士军刀",帮你搞定电脑上那些繁琐、重复的日常任务。

这一期不讲究什么高大上的架构,我们就聚焦一个问题:**怎么用 Python 写脚本自动干实事?**

---

## 一、文件系统操作:让文件自己管自己

### 1.1 遍历目录与批量重命名

假设你在下载了一个月的电影资源,名字乱七八糟,你想把它们统一规范一下。用 `os` 和 `pathlib` 就能轻松搞定。

```python

from pathlib import Path

import shutil

from datetime import datetime

defbatch_rename_movie(folder: str) -> None:

"""把文件夹里所有 mp4 文件重命名为 '电影001.mp4' 这种格式"""

    dir_path = Path(folder)

    files = sorted(dir_path.glob("*.mp4"))  # 拿到所有 mp4 文件并排序

for idx, file inenumerate(files, start=1):

        new_name = f"电影{idx:03d}{file.suffix}"

        new_path = file.with_name(new_name)

if file != new_path:

            file.rename(new_path)

print(f"{file.name} → {new_name}")

# 使用示例:

# batch_rename_movie(r"D:\\Downloads\\movies")

```

### 1.2 智能分类整理下载文件夹

更实用的是,根据文件类型自动归类到一个文件夹里的各个子文件夹中。

```python

from pathlib import Path

FOLDER_MAP = {

".jpg": "图片", ".jpeg": "图片", ".png": "图片", ".gif": "图片", ".webp": "图片",

".mp4": "视频", ".avi": "视频", ".mkv": "视频", ".mov": "视频",

".pdf": "文档", ".docx": "文档", ".txt": "文档", ".xlsx": "文档",

".mp3": "音乐", ".flac": "音乐", ".wav": "音乐",

".zip": "压缩包", ".rar": "压缩包", ".7z": "压缩包", ".tar.gz": "压缩包",

".exe": "安装包", ".dmg": "安装包",

}

defsmart_sort(download_dir: str) -> None:

"""把下载目录里的文件分类到对应子文件夹"""

    root = Path(download_dir)

for file in root.iterdir():

ifnot file.is_file():

continue

        ext = file.suffix.lower()

        category = FOLDER_MAP.get(ext, "其他")

        dest = root / category / file.name

        dest.parent.mkdir(exist_ok=True)

ifnot dest.exists():

            shutil.move(str(file), str(dest))

print(f"[{category}] {file.name}")

else:

print(f"[跳过] {file.name} 已存在")

# 使用示例:

# smart_sort(r"D:\\Downloads")

```

运行一次,你的下载文件夹立刻变清爽。

### 1.3 练习

1. 写一个函数,找出某个文件夹下超过 1GB 的大文件,并列出它们的完整路径和大小(以 MB 为单位)。

2. 编写脚本,将指定目录下所有 `.py` 文件的注释行(以 `#` 开头的行)统计出来,输出每份文件的注释行数。

3. 实现一个"重复文件查找器"——扫描一个目录树,打印出 MD5 相同的文件组。提示:使用 `hashlib.md5()` 计算文件摘要。

---

## 二、命令行工具:让终端成为你的第二工作台

### 2.1 用 argparse 编写专业命令行的脚本

Python 内置的 `argparse` 模块让你可以写出像 `ls`、`git` 那样有 `-h` 帮助的命令行程序。

```python

import argparse

import os

from pathlib import Path

defmain():

    parser = argparse.ArgumentParser(

prog="fsearch",

description="在指定目录及其子目录中搜索文件名匹配模式的文件",

    )

    parser.add_argument("pattern", help="搜索模式(支持 * 通配符)")

    parser.add_argument("-d", "--directory", default=".", help="搜索根目录(默认当前目录)")

    parser.add_argument("-e", "--extension", default=None, help="仅匹配扩展名,如 .py / .txt")

    parser.add_argument("--size-gt", type=int, default=None,

help="只显示大于指定字节数的文件")

    parser.add_argument("--count-only", action="store_true",

help="只输出结果数量,不显示详细信息")

    args = parser.parse_args()

    root = Path(args.directory)

ifnot root.is_dir():

print(f"错误:{args.directory} 不是一个有效的目录", flush=True)

return

    matches = []

for file in root.rglob(args.pattern):

ifnot file.is_file():

continue

if args.extension and file.suffix != args.extension:

continue

if args.size_gt and file.stat().st_size <= args.size_gt:

continue

        matches.append(file)

if args.count_only:

print(f"共找到 {len(matches)} 个匹配文件", flush=True)

else:

for m in matches:

            size_mb = m.stat().st_size / (1024 * 1024)

print(f"{m} ({size_mb:.2f} MB)")

print(f"\n总计 {len(matches)} 个文件", flush=True)

if__name__ == "__main__":

    main()

```

你可以这样使用:

```bash

# 搜索所有 txt 文件

pythonfsearch"*.txt"-dD:/Documents

# 只看超过 1MB 的 Python 文件

pythonfsearch"*.py"-dC:/Projects--size-gt1048576

# 只返回数量

pythonfsearch"*report*"--count-only

```

### 2.2 subprocess:在 Python 里调用系统命令

有时候光靠 Python 标准库不够用,我们需要调用系统自带的工具(比如 `ping`、`du`、`top`)。

```python

import subprocess

# ===== 用法 1:简单执行 =====

result = subprocess.run(["echo", "Hello from Python"],

capture_output=True, text=True)

print("输出:", result.stdout.strip())

# ===== 用法 2:调用 ping 检查网络 =====

defcheck_host(host: str, timeout: int = 5) -> bool:

"""检查目标主机是否在线"""

    param = "-n"if os.name == "nt"else"-c"

    cmd = ["ping", param, "1", "-W", str(timeout), host]

try:

        proc = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout+2)

return proc.returncode == 0

except subprocess.TimeoutExpired:

returnFalse

# print(check_host("127.0.0.1"))  # True

# ===== 用法 3:读取 df/du 命令的输出做磁盘分析 =====

import os

defdisk_usage_summary(top_dir: str = "/") -> dict:

"""获取磁盘使用概要"""

    result = subprocess.run(["du", "-sh", top_dir],

capture_output=True, text=True)

    lines = result.stdout.strip().split("\n")

    info = {}

for line in lines:

        parts = line.split()

iflen(parts) >= 2:

            info[parts[1]] = parts[0]

return info

# print(disk_usage_summary("."))

```

### 2.3 练习

4. 用 `subprocess` 写一个脚本,监控指定进程(如 `chrome.exe`)的 CPU 和内存占用,每隔 5 秒输出一行报告。

5. 编写一个"定时清理脚本"——每天凌晨 2 点用 `at` 或 `schtasks`(Windows)执行一次,删除临时目录中超过 30 天的文件。

---

## 三、计划任务与邮件通知

### 3.1 用 sched 模块实现轻量级定时任务

不需要装 Celery 或者 cron,标准库就有 `sched` 可以用。

```python

import sched

import time

import os

from pathlib import Path

scheduler = sched.scheduler(time.time, time.sleep)

defclean_temp_files(sc: sched.scheduler):

"""删除 temp 目录下超过 7 天的文件"""

    temp_dir = Path.home() / "AppData" / "Local" / "Temp"

    cutoff = time.time() - 7 * 86400# 7 天前

    cleaned = 0

if temp_dir.exists():

for f in temp_dir.glob("*"):

if f.is_file() and f.stat().st_mtime < cutoff:

                f.unlink()

                cleaned += 1

print(f"[{time.strftime('%Y-%m-%d %H:%M')}] 已清理 {cleaned} 个过期临时文件")

# 继续安排下一次执行(每天 3 点)

    sc.enter(86400, 1, clean_temp_files, (sc,))

# 启动任务

scheduler.enter(0, 1, clean_temp_files, (scheduler,))

try:

    scheduler.run()

exceptKeyboardInterrupt:

print("\n任务已停止")

```

### 3.2 发送通知邮件

脚本跑完了,怎么告诉你结果?发邮件最方便。

```python

import smtplib

from email.mime.text import MIMEText

from email.mime.multipart import MIMEMultipart

defsend_email(

smtp_server: str,

smtp_port: int,

sender: str,

password: str,

recipient: str,

subject: str,

body: str,

html: bool = False,

) -> bool:

"""通过 SMTP 发送邮件(支持 SSL)"""

    msg = MIMEMultipart()

    msg["From"] = sender

    msg["To"] = recipient

    msg["Subject"] = subject

    content_type = "html"if html else"plain"

    msg.attach(MIMEText(body, content_type, "utf-8"))

try:

with smtplib.SMTP_SSL(smtp_server, smtp_port) as server:

            server.login(sender, password)

            server.sendmail(sender, recipient, msg.as_string())

print(f"✅ 邮件已发送至 {recipient}")

returnTrue

exceptExceptionas e:

print(f"❌ 邮件发送失败: {e}")

returnFalse

# 使用示例(以 QQ 邮箱为例):

# send_email(

#     smtp_server="smtp.qq.com",

#     smtp_port=465,

#     sender="your@qq.com",

#     password="your_smtp_authorization_code",  # QQ邮箱需要在设置里获取"授权码"

#     recipient="admin@company.com",

#     subject="服务器监控报告",

#     body="CPU 正常,内存 72%,磁盘 45%",

# )

```

### 3.3 练习

6. 将上面的 `clean_temp_files` 和 `send_email` 组合起来:每天清理完临时文件后,自动给你发一封邮件报告清理了多少文件、剩余多少空间。

7. 编写一个"服务器健康检查"脚本:每隔 10 分钟用 `ping` 检查三个站点(127.0.0.1、8.8.8.8、114.114.114.114),如果任何目标不可达就发邮件告警。

---

## 四、注册表与系统信息:Windows 特供

### 4.1 读取系统配置

在 Windows 上我们可以直接读写注册表。下面用一个简单的例子来读取系统信息。

```python

import winreg

import platform

import socket

defget_windows_info() -> dict:

"""收集基础 Windows 系统信息"""

    info = {

"hostname": socket.gethostname(),

"platform": platform.platform(),

"python_version": platform.python_version(),

"cpu_count": platform.cpu_count(),

"username": os.environ.get("USERNAME", "unknown"),

    }

# 从注册表读取安装日期

try:

        key = winreg.OpenKey(

            winreg.HKEY_LOCAL_MACHINE,

r"SOFTWARE\Microsoft\Windows NT\CurrentVersion",

        )

        install_date, _ = winreg.QueryValueEx(key, "InstallDate")

        info["install_date_timestamp"] = int(install_date)

        winreg.CloseKey(key)

exceptFileNotFoundError:

        info["install_date"] = "无法读取"

exceptExceptionas e:

        info["install_date_error"] = str(e)

return info

for k, v in get_windows_info().items():

print(f"  {k}: {v}")

```

>**说明**:`winreg` 只在 Windows 平台可用。如果你在 macOS/Linux 上,可以改用 `psutil` 库来获取跨平台的系统信息。

### 4.2 用 psutil 做系统监控(第三方库)

```bash

pipinstallpsutil

```

```python

import psutil

import time

defsystem_monitor(interval: int = 5, rounds: int = 3) -> None:

"""周期性输出系统资源使用情况"""

print(f"{'时间':>19}{'CPU%':>7}{'内存%':>7}{'磁盘%':>7}")

print("-" * 48)

for _ inrange(rounds):

        ts = time.strftime("%Y-%m-%d %H:%M:%S")

        cpu = psutil.cpu_percent(interval=None)

        mem = psutil.virtual_memory().percent

        disk = psutil.disk_usage("/").percent

print(f"{ts:>19}{cpu:>6.1f}%  {mem:>6.1f}%  {disk:>6.1f}%")

        time.sleep(interval)

# system_monitor(interval=5, rounds=5)

```

运行效果大致如下:

```

                    时间        CPU%      内存%      磁盘%

------------------------------------------------

2026-07-14 09:50:10     12.5%     64.3%     45.2%

2026-07-14 09:50:15     15.8%     64.5%     45.2%

2026-07-14 09:50:20     8.3%      64.4%     45.2%

2026-07-14 09:50:25     22.1%     64.6%     45.2%

2026-07-14 09:50:30     6.7%      64.5%     45.2%

```

---

## 五、综合实战:一键搭建"个人运维仪表盘"

现在我们把前面学的所有东西串在一起,写一个完整的脚本,它可以:

1. 扫描指定目录下的所有文件,统计容量分布

2. 检查系统资源使用情况

3. 把结果保存到一个汇总报告文件里

4. (可选)通过邮件发送给你自己

```python

#!/usr/bin/env python3

"""

daily_health.py — 每日系统健康检查脚本

用法:python daily_health.py [--email recipient@example.com]

"""

import argparse

import json

import os

import platform

import shutil

import subprocess

import time

from datetime import datetime

from pathlib import Path

try:

import psutil

HAS_PSUTIL = True

exceptImportError:

HAS_PSUTIL = False

defscan_directories(dirs: list[str]) -> dict:

"""扫描指定目录的大小分布"""

    results = {}

for d in dirs:

        p = Path(d)

ifnot p.exists():

            results[d] = {"error": "目录不存在"}

continue

        total = 0

        by_ext: dict[str, int] = {}

        file_count = 0

for f in p.rglob("*"):

if f.is_file():

                size = f.stat().st_size

                total += size

                file_count += 1

                ext = f.suffix.lower() or"(无扩展名)"

                by_ext[ext] = by_ext.get(ext, 0) + size

        results[d] = {

"total_bytes": total,

"file_count": file_count,

"by_extension": dict(sorted(by_ext.items(), key=lambdax: -x[1]))[:10],

        }

return results

defget_system_info() -> dict:

"""获取系统资源状态"""

    info = {"host": platform.node(), "platform": platform.platform()}

ifHAS_PSUTIL:

        info.update({

"cpu_percent": psutil.cpu_percent(interval=1),

"memory_total_gb": round(psutil.virtual_memory().total / (1024 ** 3), 1),

"memory_used_percent": psutil.virtual_memory().percent,

"disk_usage_percent": psutil.disk_usage("/").percent,

        })

else:

# 回退方案:调用系统命令

try:

            out = subprocess.check_output(

                ["systeminfo", " | findstr /C:\"总物理内存\""],

text=True, shell=True,

            )

            info["systeminfo_stdout"] = out.strip()

exceptException:

            info["psutil_not_available"] = "请安装 pip install psutil"

return info

defbuild_report(scan_dirs: list[str], email: str | None = None) -> str:

"""组装并输出报告"""

    now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    scan_data = scan_directories(scan_dirs)

    sys_data = get_system_info()

    report_lines = [

f"===== 每日系统健康报告 {now} =====",

"",

"--- 系统状态 ---",

        json.dumps(sys_data, ensure_ascii=False, indent=2),

"",

"--- 目录扫描 ---",

    ]

for d, data in scan_data.items():

        mb = data.get("total_bytes", 0) / (1024 * 1024)

        report_lines.append(f"  {d}: {mb:.1f} MB, {data.get('file_count', '?')} 个文件")

for ext, size in data.get("by_extension", {}).items():

            kb = size / 1024

if kb > 1024:

                report_lines.append(f"    .{ext.lstrip('.')}: {kb/1024:.1f} MB")

else:

                report_lines.append(f"    .{ext.lstrip('.')}: {kb:.0f} KB")

    report_text = "\n".join(report_lines) + "\n"

# 保存到文件

    out_dir = Path.home() / "AppData" / "Local" / "health_reports"

    out_dir.mkdir(parents=True, exist_ok=True)

    report_file = out_dir / f"health_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"

    report_file.write_text(report_text, encoding="utf-8")

print(f"📄 报告已保存到: {report_file}")

# 如果有 psutil 且数据异常,可以触发邮件告警逻辑

# send_email(...)  # 参见前面 send_email 函数定义

return report_text

if__name__ == "__main__":

    parser = argparse.ArgumentParser(description="每日系统健康检查")

    parser.add_argument(

"dirs", nargs="+", help="要扫描的目录列表",

    )

    parser.add_argument("--email", help="接收报告的邮箱地址(可选)")

    args = parser.parse_args()

    report = build_report(args.dirs, args.email)

print("\n" + report)

```

运行:

```bash

pythondaily_health.pyD:\DownloadsD:\DocumentsD:\Projects

pythondaily_health.pyD:\ --emailyour@email.com

```

---

## 六、总结与进阶路线

这一期我们学会了:

| 模块 | 用途 | 一句话概括 |

|------|------|-----------|

| `pathlib` / `os` | 文件系统管理 | 像文件管理器一样操控文件夹 |

| `argparse` | 命令行接口 | 让你的脚本像专业工具一样好用 |

| `subprocess` | 调用外部命令 | Python 的"外挂"工具箱 |

| `sched` | 轻量调度器 | 不需要 cron 的定时方案 |

| `smtplib` | 邮件通知 | 脚本执行完,结果到手了 |

| `psutil` | 系统监控 | 实时看 CPU、内存、磁盘 |

| `winreg` | Windows 注册表 | 读取系统安装信息等配置 |

### 下一步怎么走?

自动化运维是通向"工程师思维"的桥梁——不再只是写代码,而是让代码替你工作。接下来的进阶方向:

-**Ansible / SaltStack**:大规模服务器批量运维

-**Django Admin 自定义**:把脚本封装成 Web 管理面板

-**CI/CD 流水线**:用 GitHub Actions 或 Jenkins 自动化测试和部署

---

## 七、练习题参考

以下是前面练习题的参考答案思路:

**练习 1**(查找大文件):

```python

from pathlib import Path

deffind_large_files(folder: str, threshold_mb: float = 100) -> list[tuple[str, float]]:

    results = []

for f in Path(folder).rglob("*"):

if f.is_file() and f.stat().st_size > threshold_mb * 1024 * 1024:

            results.append((str(f), f.stat().st_size / (1024 * 1024)))

returnsorted(results, key=lambdax: -x[1])

```

**练习 2**(统计注释行数):

```python

from pathlib import Path

defcount_comments(folder: str) -> dict[str, int]:

    results = {}

for f in Path(folder).rglob("*.py"):

        comments = sum(1for line in f.read_text(encoding="utf-8", errors="ignore")

if line.strip().startswith("#"))

        results[str(f)] = comments

return results

```

**练习 3**(重复文件检测):

```python

import hashlib

from pathlib import Path

deffind_duplicates(folder: str) -> dict[str, list[str]]:

defmd5sum(filepath: str) -> str:

        h = hashlib.md5()

withopen(filepath, "rb") as f:

            h.update(f.read())

return h.hexdigest()

    hash_map: dict[str, list[str]] = {}

for f in Path(folder).rglob("*"):

if f.is_file():

            h = md5sum(str(f))

            hash_map.setdefault(h, []).append(str(f))

# 只保留有多份的文件组

return {k: v for k, v in hash_map.items() iflen(v) > 1}

```

---

**下期预告(Episode 16)**:**Python 与云服务交互**—— 用 `requests` / `boto3` 对接云 API,自动化上传下载 S3/OSS 文件、管理云函数触发器、调用阿里云/腾讯云 OCR 和语音接口,打造真正的"云端自动化"。敬请期待!

最新文章

随机文章