python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python自动化脚本

Linux下Python自动化脚本编写到运维工具开发详解

作者:lbb 小魔仙

Python 凭借其简洁的语法、丰富的内置库与第三方生态,成为 Linux 自动化运维的首选语言,下面我们就来看看Linux下Python自动化脚本编写到运维工具开发的相关知识吧

在 Linux 运维场景中,重复性工作(如日志清理、服务监控、批量部署)占据了运维人员大量时间,而 Python 凭借其简洁的语法、丰富的内置库与第三方生态,成为 Linux 自动化运维的首选语言。相较于 Shell 脚本,Python 具备更强的逻辑处理能力、跨平台兼容性和可维护性,能轻松应对复杂的自动化需求;同时,Python 与 Linux 系统底层交互顺畅,可直接调用系统命令、操作文件目录、管理进程服务,大幅提升运维效率。

一、引言

Python 在 Linux 自动化运维中的典型应用场景包括:日志收集与分析(快速提取关键信息、定位问题)、服务状态监控(实时检测服务可用性,自动告警)、批量任务执行(批量部署软件、配置服务器)、配置文件管理(统一修改、备份配置)以及系统资源统计(CPU、内存、磁盘使用率采集)等。本文将从基础脚本编写入手,逐步讲解如何封装进阶工具,并通过实际案例演示完整开发流程,帮助读者快速掌握 Linux 下 Python 自动化的核心技能。

二、基础脚本编写:实用示例与核心模块

基础脚本是自动化运维的基石,通常聚焦于单一简单任务。本节以“自动清理旧日志文件”为例,编写可直接运行的 Python 脚本,同时讲解 os、subprocess、argparse 等核心模块的使用方法。

2.1 需求说明

清理指定目录下指定后缀(如 .log)、超过指定天数的旧日志文件,支持显示清理过程与跳过空目录,避免误删重要文件。

2.2 完整脚本代码

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
自动清理 Linux 下的旧日志文件
功能:删除指定目录下超过指定天数的指定后缀文件
依赖:Python 3.6+(无需额外第三方库)
"""
import os
import argparse
import time
from datetime import datetime, timedelta

def parse_args():
    """解析命令行参数"""
    parser = argparse.ArgumentParser(description="Linux 旧日志文件清理工具")
    # 必选参数:日志目录
    parser.add_argument("-d", "--dir", required=True, help="日志文件所在目录(必填)")
    # 可选参数:文件后缀,默认 .log
    parser.add_argument("-s", "--suffix", default=".log", help="需要清理的文件后缀(默认:.log)")
    # 可选参数:保留天数,默认 7 天
    parser.add_argument("-t", "--days", type=int, default=7, help="保留文件的天数(默认:7 天,超过则删除)")
    return parser.parse_args()

def clean_old_files(log_dir, file_suffix, keep_days):
    """
    清理旧文件核心逻辑
    :param log_dir: 日志目录路径
    :param file_suffix: 目标文件后缀
    :param keep_days: 保留天数
    """
    # 校验目录是否存在
    if not os.path.isdir(log_dir):
        print(f"错误:目录 {log_dir} 不存在,请检查路径是否正确")
        return
    # 计算过期时间阈值(当前时间 - 保留天数)
    expire_time = datetime.now() - timedelta(days=keep_days)
    # 遍历目录下所有文件
    for root, dirs, files in os.walk(log_dir):
        for file in files:
            # 筛选指定后缀的文件
            if file.endswith(file_suffix):
                file_path = os.path.join(root, file)
                # 获取文件最后修改时间(Linux 时间戳转 datetime)
                mtime = os.path.getmtime(file_path)
                mtime_datetime = datetime.fromtimestamp(mtime)
                # 判断文件是否过期
                if mtime_datetime < expire_time:
                    try:
                        # 删除文件
                        os.remove(file_path)
                        print(f"已删除过期文件:{file_path}")
                    except Exception as e:
                        print(f"删除文件 {file_path} 失败:{str(e)}")
    print("清理任务执行完成!")

if __name__ == "__main__":
    # 解析参数
    args = parse_args()
    # 执行清理逻辑
    clean_old_files(args.dir, args.suffix, args.days)

2.3 核心模块解析

os 模块:与 Linux 系统交互的核心模块,本文中用于判断目录是否存在(os.path.isdir)、拼接文件路径(os.path.join)、获取文件修改时间(os.path.getmtime)、删除文件(os.remove)以及遍历目录(os.walk)。该模块无需额外安装,是 Python 内置核心库,能直接操作系统文件与目录。

argparse 模块:命令行参数解析模块,用于接收用户输入的目录、后缀、保留天数等参数,支持必选/可选参数配置、参数说明提示,让脚本更灵活通用。通过 argparse.ArgumentParser 创建解析器,add_argument 定义参数规则,最终通过 parse_args() 解析参数。

datetime/timedelta 模块:用于时间计算,本文中通过 datetime.now() 获取当前时间,减去 timedelta(days=keep_days) 得到文件保留的时间阈值,再与文件最后修改时间对比,判断文件是否过期。

2.4 脚本使用方法

保存脚本为 clean_old_logs.py,赋予执行权限:chmod +x clean_old_logs.py

基本使用(清理 /var/log 目录下超过 7 天的 .log 文件):./clean_old_logs.py -d /var/log

自定义参数(清理 /data/logs 目录下超过 3 天的 .log.1 后缀文件):./clean_old_logs.py -d /data/logs -s .log.1 -t 3

查看帮助:./clean_old_logs.py -h

三、进阶工具开发:脚本封装与功能强化

基础脚本仅能满足简单需求,在实际运维中,工具需具备更强的健壮性、可维护性和可追溯性。本节将基础日志清理脚本封装为专业命令行工具,新增错误处理、日志记录、日志级别控制等功能,提升工具的生产可用性。

3.1 强化功能说明

完善错误处理:捕获目录权限不足、文件被占用等异常,避免程序崩溃;

日志记录:将清理过程、错误信息写入日志文件,便于后续追溯;

日志级别控制:支持 DEBUG/INFO/WARNING/ERROR 级别,灵活控制日志输出详细程度;

dry-run 模式:模拟清理操作,不实际删除文件,用于测试验证。

3.2 进阶工具代码片段

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Linux 旧日志文件清理工具(进阶版)
功能:支持错误处理、日志记录、dry-run 模拟、日志级别控制
依赖:Python 3.6+
"""
import os
import argparse
import time
import logging
from datetime import datetime, timedelta

def setup_logging(log_level, log_file=None):
    """配置日志系统"""
    # 日志级别映射
    level_map = {
        "DEBUG": logging.DEBUG,
        "INFO": logging.INFO,
        "WARNING": logging.WARNING,
        "ERROR": logging.ERROR
    }
    level = level_map.get(log_level.upper(), logging.INFO)
    
    # 日志格式
    formatter = logging.Formatter(
        "%(asctime)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s"
    )
    
    # 日志处理器(控制台 + 文件)
    handlers = [logging.StreamHandler()]
    if log_file:
        file_handler = logging.FileHandler(log_file, encoding="utf-8")
        file_handler.setFormatter(formatter)
        handlers.append(file_handler)
    
    # 配置根日志
    logging.basicConfig(level=level, handlers=handlers)
    return logging.getLogger(__name__)

def parse_args():
    """解析命令行参数(强化版)"""
    parser = argparse.ArgumentParser(description="Linux 旧日志文件清理工具(进阶版)")
    parser.add_argument("-d", "--dir", required=True, help="日志文件所在目录(必填)")
    parser.add_argument("-s", "--suffix", default=".log", help="需要清理的文件后缀(默认:.log)")
    parser.add_argument("-t", "--days", type=int, default=7, help="保留文件的天数(默认:7 天)")
    parser.add_argument("--dry-run", action="store_true", help="模拟清理,不实际删除文件")
    parser.add_argument("--log-level", default="INFO", choices=["DEBUG", "INFO", "WARNING", "ERROR"], help="日志级别(默认:INFO)")
    parser.add_argument("--log-file", help="日志文件路径(默认仅输出到控制台)")
    return parser.parse_args()

def clean_old_files(log_dir, file_suffix, keep_days, dry_run, logger):
    """核心清理逻辑(带日志与错误处理)"""
    try:
        if not os.path.isdir(log_dir):
            logger.error(f"目录 {log_dir} 不存在,清理任务终止")
            return
        # 校验目录权限(是否可读取)
        if not os.access(log_dir, os.R_OK):
            logger.error(f"无权限访问目录 {log_dir},请检查权限配置")
            return
        
        expire_time = datetime.now() - timedelta(days=keep_days)
        logger.info(f"开始执行清理任务:目录={log_dir},后缀={file_suffix},保留天数={keep_days}")
        logger.debug(f"过期时间阈值:{expire_time.strftime('%Y-%m-%d %H:%M:%S')}")
        
        deleted_count = 0
        for root, dirs, files in os.walk(log_dir):
            logger.debug(f"遍历目录:{root},包含文件数:{len(files)}")
            for file in files:
                if file.endswith(file_suffix):
                    file_path = os.path.join(root, file)
                    try:
                        mtime = os.path.getmtime(file_path)
                        mtime_datetime = datetime.fromtimestamp(mtime)
                        if mtime_datetime < expire_time:
                            if dry_run:
                                logger.info(f"[Dry-Run] 拟删除过期文件:{file_path}(最后修改时间:{mtime_datetime.strftime('%Y-%m-%d %H:%M:%S')})")
                            else:
                                os.remove(file_path)
                                logger.info(f"已删除过期文件:{file_path}(最后修改时间:{mtime_datetime.strftime('%Y-%m-%d %H:%M:%S')})")
                                deleted_count += 1
                    except PermissionError:
                        logger.warning(f"无权限操作文件:{file_path},跳过该文件")
                    except Exception as e:
                        logger.error(f"处理文件 {file_path} 时出错:{str(e)}", exc_info=True)
        
        logger.info(f"清理任务执行完成!共处理过期文件 {deleted_count} 个({'模拟模式,未实际删除' if dry_run else '已实际删除'})")
    except Exception as e:
        logger.critical(f"清理任务意外终止:{str(e)}", exc_info=True)

if __name__ == "__main__":
    args = parse_args()
    # 初始化日志
    logger = setup_logging(args.log_level, args.log_file)
    # 执行清理
    clean_old_files(args.dir, args.suffix, args.days, args.dry_run, logger)

3.3 核心强化点解析

日志系统(logging 模块):Python 内置日志模块,支持多级别日志输出、多处理器(控制台 + 文件)、自定义日志格式。通过 setup_logging 函数统一配置,既能在控制台实时查看结果,又能将日志写入文件便于追溯,错误信息还会记录堆栈轨迹(exc_info=True),快速定位问题。

完善错误处理:新增目录权限校验(os.access),捕获 PermissionError、文件操作异常等特定异常,避免单一文件处理失败导致整个脚本终止;同时通过 logger 记录不同级别错误,而非简单 print,更符合生产工具规范。

dry-run 模式:通过 --dry-run 参数启用模拟清理,仅打印拟删除文件信息,不实际执行删除操作,可在正式清理前验证参数是否正确,避免误删风险,这是运维工具的常用核心功能。

四、实际案例:自动检测并重启宕机服务

本节以“自动检测并重启宕机服务”为例,完整演示从需求分析、设计思路到代码实现、测试验证的全流程,帮助读者将前文所学知识落地应用。

4.1 需求分析

针对 Linux 系统中的指定服务(如 nginx、mysql),实现以下功能:

4.2 设计思路

服务状态检测:通过 Linux 系统命令 systemctl is-active 服务名ps aux | grep 服务名 判断服务是否运行,本文采用 systemctl(适用于 systemd 系统,主流 Linux 发行版均支持);

命令执行:使用 subprocess 模块调用系统命令,捕获输出结果与返回码,判断命令执行成功与否;

循环检测:通过 while 循环实现定期检测,间隔时间由用户指定,支持手动终止(Ctrl+C);

异常处理:捕获命令执行失败、服务重启失败等异常,记录日志;

日志与参数:集成 logging 模块记录全程日志,通过 argparse 支持自定义参数。

4.3 实现过程

完整代码

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
服务状态监控与自动重启工具
功能:定期检测服务状态,宕机时自动重启,记录全程日志
依赖:Python 3.6+,适用于 systemd 系统(CentOS 7+、Ubuntu 16.04+ 等)
"""
import os
import sys
import time
import logging
import argparse
import subprocess
from signal import signal, SIGINT

def setup_logging(log_level, log_file):
    """配置日志系统"""
    level_map = {"DEBUG": logging.DEBUG, "INFO": logging.INFO, "WARNING": logging.WARNING, "ERROR": logging.ERROR}
    level = level_map.get(log_level.upper(), logging.INFO)
    formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
    handlers = [logging.StreamHandler()]
    if log_file:
        file_handler = logging.FileHandler(log_file, encoding="utf-8")
        file_handler.setFormatter(formatter)
        handlers.append(file_handler)
    logging.basicConfig(level=level, handlers=handlers)
    return logging.getLogger(__name__)

def parse_args():
    """解析命令行参数"""
    parser = argparse.ArgumentParser(description="服务状态监控与自动重启工具")
    parser.add_argument("-s", "--service", required=True, help="需要监控的服务名称(必填,如 nginx、mysql)")
    parser.add_argument("-i", "--interval", type=int, default=60, help="检测间隔(秒,默认:60 秒)")
    parser.add_argument("--log-level", default="INFO", choices=["DEBUG", "INFO", "WARNING", "ERROR"], help="日志级别(默认:INFO)")
    parser.add_argument("--log-file", help="日志文件路径(默认仅输出到控制台)")
    return parser.parse_args()

def execute_command(cmd):
    """
    执行系统命令并返回结果
    :param cmd: 命令字符串
    :return: (return_code, stdout, stderr) 命令返回码、标准输出、标准错误
    """
    try:
        result = subprocess.run(
            cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="utf-8", timeout=10
        )
        return (result.returncode, result.stdout.strip(), result.stderr.strip())
    except subprocess.TimeoutExpired:
        return (-1, "", "命令执行超时")
    except Exception as e:
        return (-2, "", f"命令执行异常:{str(e)}")

def check_service_status(service_name, logger):
    """检查服务运行状态"""
    cmd = f"systemctl is-active {service_name}"
    ret_code, stdout, stderr = execute_command(cmd)
    if ret_code == 0:
        if stdout == "active":
            logger.debug(f"服务 {service_name} 运行正常(状态:{stdout})")
            return True
        else:
            logger.warning(f"服务 {service_name} 状态异常(状态:{stdout})")
            return False
    else:
        logger.error(f"检测服务 {service_name} 状态失败:{stderr}(返回码:{ret_code})")
        return False

def restart_service(service_name, logger):
    """重启服务并返回结果"""
    logger.warning(f"开始尝试重启服务 {service_name}")
    cmd = f"systemctl restart {service_name}"
    # 重启命令需要 root 权限,检测当前用户是否为 root
    if os.geteuid() != 0:
        logger.error("重启服务需要 root 权限,请使用 sudo 运行脚本")
        return False
    ret_code, stdout, stderr = execute_command(cmd)
    if ret_code == 0:
        logger.info(f"服务 {service_name} 重启命令执行成功,等待 5 秒后验证状态")
        time.sleep(5)
        # 验证重启后状态
        if check_service_status(service_name, logger):
            logger.info(f"服务 {service_name} 重启成功并正常运行")
            return True
        else:
            logger.error(f"服务 {service_name} 重启命令执行成功,但状态仍异常")
            return False
    else:
        logger.error(f"服务 {service_name} 重启失败:{stderr}(返回码:{ret_code})")
        return False

def signal_handler(signum, frame, logger):
    """处理 Ctrl+C 终止信号"""
    logger.info("收到终止信号,正在退出监控程序...")
    sys.exit(0)

def start_monitor(service_name, interval, logger):
    """启动服务监控循环"""
    logger.info(f"服务监控程序启动:监控服务={service_name},检测间隔={interval}秒")
    # 注册 Ctrl+C 信号处理
    signal(SIGINT, lambda signum, frame: signal_handler(signum, frame, logger))
    while True:
        try:
            if not check_service_status(service_name, logger):
                # 服务异常,尝试重启
                if not restart_service(service_name, logger):
                    logger.critical(f"服务 {service_name} 重启失败,监控程序退出")
                    break
            # 等待下一次检测
            time.sleep(interval)
        except Exception as e:
            logger.error(f"监控循环异常:{str(e)}", exc_info=True)
            time.sleep(interval)

if __name__ == "__main__":
    args = parse_args()
    logger = setup_logging(args.log_level, args.log_file)
    # 启动监控
    start_monitor(args.service, args.interval, logger)

关键模块补充解析

subprocess 模块:用于调用 Linux 系统命令,相较于 os.system 更强大,能捕获命令返回码、标准输出和标准错误。本文中通过 subprocess.run 执行 systemctl 命令,设置 shell=True 支持命令字符串格式,timeout 避免命令卡死,encoding 指定输出编码为 utf-8,便于中文处理。

信号处理(signal 模块):注册 SIGINT 信号(对应 Ctrl+C)的处理函数,使程序在被手动终止时能优雅退出,记录终止日志,避免强制终止导致的资源泄露。

权限校验(os.geteuid):重启服务需要 root 权限,通过 os.geteuid() == 0 判断当前用户是否为 root,若不是则提示并退出,避免因权限不足导致重启失败。

4.4 测试方法

环境准备

以 CentOS 8 系统、nginx 服务为例,确保系统已安装 nginx 并启用 systemd 管理:yum install -y nginx && systemctl enable --now nginx

功能测试

五、案例流程图

以下使用 Mermaid 语法绘制“自动检测并重启宕机服务”案例的逻辑流程图,清晰展示程序执行流程:

六、总结与扩展建议

6.1 总结

Python 在 Linux 自动化运维中具备“轻量、高效、易扩展”的核心优势,从简单的日志清理脚本,到功能完善的服务监控工具,仅需依托内置模块即可快速实现。本文通过“基础脚本-进阶封装-实际案例”的递进式讲解,展示了 Python 自动化的开发流程:先通过核心模块实现核心功能,再通过错误处理、日志记录、参数解析强化工具健壮性,最后结合实际需求落地案例,形成可直接应用于生产环境的运维工具。

相较于传统手动运维或 Shell 脚本,Python 自动化工具更易维护、可扩展性更强,能有效减少重复性工作,降低人为操作失误,提升运维效率与系统稳定性。

6.2 扩展建议

通过持续学习与实践,结合 Python 丰富的生态与 Linux 系统特性,可开发出更贴合实际运维需求的自动化工具,逐步实现运维工作的“自动化、智能化、可视化”。

以上就是Linux下Python自动化脚本编写到运维工具开发详解的详细内容,更多关于Python自动化脚本的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文