如何编写自动安装补丁脚本

wen 实用脚本 4

本文目录导读:

如何编写自动安装补丁脚本

  1. Windows PowerShell 补丁安装脚本
  2. Linux/Unix Bash 补丁安装脚本
  3. Windows批处理脚本(.bat)
  4. 使用自动化工具(推荐)
  5. 使用建议

我来介绍几种编写自动安装补丁脚本的方法,根据不同的操作系统和使用场景:

Windows PowerShell 补丁安装脚本

基础版本

# patch_installer.ps1
# 配置参数
$PatchPath = "C:\Patches"  # 补丁存放路径
$LogFile = "C:\Logs\patch_install.log"
$PatchExtension = "*.msu"   # Windows补丁文件类型
# 创建日志函数
function Write-Log {
    param($Message)
    $TimeStamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    "$TimeStamp - $Message" | Out-File -FilePath $LogFile -Append
    Write-Host "$TimeStamp - $Message"
}
# 创建日志目录
if (!(Test-Path (Split-Path $LogFile))) {
    New-Item -Path (Split-Path $LogFile) -ItemType Directory -Force
}
# 获取所有补丁文件
$patchFiles = Get-ChildItem -Path $PatchPath -Filter $PatchExtension
Write-Log "开始安装补丁,共发现 $($patchFiles.Count) 个补丁文件"
# 遍历安装补丁
foreach ($patch in $patchFiles) {
    try {
        Write-Log "正在安装: $($patch.Name)"
        # 方法1: 使用wusa.exe安装MSU补丁
        $process = Start-Process -FilePath "wusa.exe" -ArgumentList "$($patch.FullName) /quiet /norestart" -Wait -PassThru
        # 检查安装结果
        if ($process.ExitCode -eq 0 -or $process.ExitCode -eq 3010) {
            Write-Log "成功安装: $($patch.Name) (退出码: $($process.ExitCode))"
            Rename-Item $patch.FullName "$($patch.FullName).installed" -ErrorAction SilentlyContinue
        } else {
            Write-Log "安装失败: $($patch.Name) (退出码: $process.ExitCode)"
            # 记录失败补丁
            $patch.Name | Out-File -FilePath "C:\Logs\failed_patches.txt" -Append
        }
    }
    catch {
        Write-Log "错误安装 $($patch.Name) : $_"
    }
}
Write-Log "补丁安装完成"

高级版本(带重启控制和错误处理)

# advanced_patch_installer.ps1
param(
    [string]$PatchDirectory = "C:\Patches",
    [switch]$ForceReboot,
    [switch]$DryRun
)
# 全局变量
$script:LogFile = "C:\Logs\patch_install.log"
$script:InstalledPatches = @()
$script:FailedPatches = @()
# 日志函数
function Write-Log {
    param([string]$Message, [string]$Level = "INFO")
    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $logMessage = "[$timestamp] [$Level] $Message"
    Write-Host $logMessage -ForegroundColor $(if ($Level -eq "ERROR") {"Red"} else {"Green"})
    Add-Content -Path $script:LogFile -Value $logMessage
}
# 检查系统要求
function Test-SystemRequirements {
    Write-Log "检查系统要求..."
    # 检查磁盘空间
    $drive = Get-PSDrive C
    if ($drive.Free -lt 10GB) {
        Write-Log "磁盘空间不足,至少需要10GB可用空间" "ERROR"
        return $false
    }
    # 检查Windows服务是否正常运行
    if (!(Get-Service -Name wuauserv).Status -eq "Running") {
        Write-Log "Windows Update服务未运行" "WARNING"
    }
    return $true
}
# 安装单个补丁
function Install-SinglePatch {
    param([string]$PatchPath)
    $patchName = Split-Path $PatchPath -Leaf
    Write-Log "开始安装: $patchName"
    if ($DryRun) {
        Write-Log "[模拟] 将安装: $patchName"
        return "SIMULATED"
    }
    try {
        $process = Start-Process -FilePath "wusa.exe" -ArgumentList "$PatchPath /quiet /norestart" -Wait -PassThru
        switch ($process.ExitCode) {
            0 { Write-Log "成功: $patchName"; return "SUCCESS" }
            3010 { Write-Log "成功(需要重启): $patchName"; return "SUCCESS_PENDING_REBOOT" }
            2359302 { Write-Log "补丁已安装: $patchName"; return "ALREADY_INSTALLED" }
            default { 
                Write-Log "失败 ($($process.ExitCode)): $patchName" "ERROR"
                return "FAILED"
            }
        }
    }
    catch {
        Write-Log "安装异常: $_" "ERROR"
        return "FAILED"
    }
}
# 主函数
function Main {
    Write-Log "=== 开始补丁安装脚本 ==="
    # 检查补丁目录
    if (!(Test-Path $PatchDirectory)) {
        Write-Log "补丁目录不存在: $PatchDirectory" "ERROR"
        exit 1
    }
    # 检查系统要求
    if (!(Test-SystemRequirements)) {
        Write-Log "系统要求未满足,退出脚本" "ERROR"
        exit 1
    }
    # 获取补丁列表并按依赖排序
    $patches = Get-ChildItem -Path $PatchDirectory -Filter "*.msu" | Sort-Object Name
    if ($patches.Count -eq 0) {
        Write-Log "未找到补丁文件"
        exit 0
    }
    Write-Log "发现 $($patches.Count) 个补丁"
    # 安装补丁
    $rebootRequired = $false
    foreach ($patch in $patches) {
        $result = Install-SinglePatch -PatchPath $patch.FullName
        switch ($result) {
            "SUCCESS" {
                $script:InstalledPatches += $patch.Name
                Rename-Item $patch.FullName "$($patch.FullName).installed"
            }
            "SUCCESS_PENDING_REBOOT" {
                $script:InstalledPatches += $patch.Name
                $rebootRequired = $true
                Rename-Item $patch.FullName "$($patch.FullName).installed"
            }
            "ALREADY_INSTALLED" {
                Write-Log "跳过(已安装): $($patch.Name)"
                Rename-Item $patch.FullName "$($patch.FullName).installed"
            }
            "FAILED" {
                $script:FailedPatches += $patch.Name
            }
        }
    }
    # 输出摘要
    Write-Log "=== 安装摘要 ==="
    Write-Log "成功安装: $($script:InstalledPatches.Count) 个补丁"
    if ($script:FailedPatches.Count -gt 0) {
        Write-Log "失败补丁: $($script:FailedPatches.Count) 个"
        $script:FailedPatches | ForEach-Object { Write-Log "  - $_" "ERROR" }
    }
    if ($rebootRequired) {
        Write-Log "系统需要重启以完成补丁安装"
        if ($ForceReboot) {
            Write-Log "自动重启系统..."
            Restart-Computer -Force
        } else {
            Write-Log "请手动重启系统"
        }
    }
    # 发送邮件通知(可选)
    # Send-MailMessage -To "admin@company.com" -Subject "补丁安装完成" ...
}
# 执行主程序
Main

Linux/Unix Bash 补丁安装脚本

#!/bin/bash
# patch_installer.sh
# 配置
PATCH_DIR="/var/tmp/patches"
LOG_FILE="/var/log/patch_install.log"
BACKUP_DIR="/var/backups/patches/$(date +%Y%m%d)"
# 日志函数
log() {
    local level=$1
    local message=$2
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    echo "[$timestamp] [$level] $message" | tee -a "$LOG_FILE"
}
# 检查root权限
check_root() {
    if [[ $EUID -ne 0 ]]; then
        log "ERROR" "请使用root权限运行此脚本"
        exit 1
    fi
}
# 创建备份目录
create_backup() {
    log "INFO" "创建备份目录: $BACKUP_DIR"
    mkdir -p "$BACKUP_DIR"
    # 备份配置文件
    cp -r /etc/yum.repos.d "$BACKUP_DIR/repos_backup"
    log "INFO" "备份完成"
}
# 自动检测发行版
detect_distro() {
    if [[ -f /etc/redhat-release ]]; then
        echo "redhat"
    elif [[ -f /etc/debian_version ]]; then
        echo "debian"
    else
        echo "unknown"
    fi
}
# RPM系统(CentOS/RHEL/Fedora)
install_rpm_patches() {
    log "INFO" "开始安装RPM补丁"
    # 清理缓存
    yum clean all
    # 更新包列表
    yum makecache
    # 检查是否有挂起的更新
    updates=$(yum check-update 2>/dev/null | grep -c "\.\(i386\|x86_64\)")
    log "INFO" "有 $updates 个更新可用"
    if [[ "$updates" -gt 0 ]]; then
        # 安装安全更新
        if yum update -y --security; then
            log "INFO" "安全更新安装成功"
        else
            log "ERROR" "安全更新安装失败"
            return 1
        fi
    fi
    # 安装本地补丁文件
    for patch_file in "$PATCH_DIR"/*.rpm; do
        if [[ -f "$patch_file" ]]; then
            log "INFO" "安装补丁: $(basename $patch_file)"
            if rpm -Uvh "$patch_file"; then
                log "INFO" "补丁安装成功: $(basename $patch_file)"
                mv "$patch_file" "$BACKUP_DIR/"
            else
                log "ERROR" "补丁安装失败: $(basename $patch_file)"
            fi
        fi
    done
}
# Debian系统(Ubuntu/Debian)
install_debian_patches() {
    log "INFO" "开始安装Debian补丁"
    # 更新软件包列表
    apt-get update
    # 计算可用更新
    updates=$(apt-get -s upgrade 2>/dev/null | grep -c "^Inst")
    log "INFO" "有 $updates 个更新可用"
    if [[ "$updates" -gt 0 ]]; then
        # 安装安全更新
        if DEBIAN_FRONTEND=noninteractive apt-get upgrade -y --no-install-recommends; then
            log "INFO" "安全更新安装成功"
        else
            log "ERROR" "安全更新安装失败"
            return 1
        fi
    fi
    # 安装本地补丁
    for patch_file in "$PATCH_DIR"/*.deb; do
        if [[ -f "$patch_file" ]]; then
            log "INFO" "安装补丁: $(basename $patch_file)"
            if dpkg -i "$patch_file"; then
                log "INFO" "补丁安装成功: $(basename $patch_file)"
                mv "$patch_file" "$BACKUP_DIR/"
            else
                log "ERROR" "补丁安装失败: $(basename $patch_file)"
                # 尝试修复依赖
                apt-get install -f -y
            fi
        fi
    done
}
# 检查重启需求
check_reboot_needed() {
    if [[ -f /var/run/reboot-required ]]; then
        log "WARNING" "系统需要重启"
        return 0
    else
        log "INFO" "系统无需重启"
        return 1
    fi
}
# 发送通知
send_notification() {
    local subject=$1
    local message=$2
    # 邮件通知(需要配置mail命令)
    # echo "$message" | mail -s "$subject" admin@example.com
    # 或使用curl发送到API服务
    # curl -H "Content-Type: application/json" -d "{\"text\":\"$subject: $message\"}" https://api.example.com/notify
}
# 主函数
main() {
    log "INFO" "=== 开始自动补丁安装脚本 ==="
    # 检查root权限
    check_root
    # 检查补丁目录是否存在
    if [[ ! -d "$PATCH_DIR" ]]; then
        mkdir -p "$PATCH_DIR"
        log "INFO" "创建补丁目录: $PATCH_DIR"
    fi
    # 创建备份
    create_backup
    # 检测系统类型并安装
    distro=$(detect_distro)
    log "INFO" "检测到操作系统: $distro"
    case $distro in
        redhat)
            install_rpm_patches
            ;;
        debian)
            install_debian_patches
            ;;
        *)
            log "ERROR" "不支持的操作系统"
            exit 1
            ;;
    esac
    # 检查是否需要重启
    if check_reboot_needed; then
        read -p "系统需要重启,是否立即重启? (y/n): " answer
        if [[ "$answer" == "y" ]]; then
            log "INFO" "正在重启系统..."
            send_notification "补丁安装完成" "系统即将重启"
            reboot
        fi
    fi
    log "INFO" "=== 补丁安装脚本完成 ==="
    send_notification "补丁安装完成" "补丁安装脚本执行完毕"
}
# 执行主函数
main "$@"

Windows批处理脚本(.bat)

@echo off
REM ====================================
REM 自动补丁安装脚本
REM ====================================
setlocal enabledelayedexpansion
REM 配置参数
set PATCH_DIR=C:\Patches
set LOG_FILE=C:\Logs\patch_install.log
set BACKUP_DIR=C:\Backup\patches
REM 检查管理员权限
net session >nul 2>&1
if %errorlevel% neq 0 (
    echo 请以管理员身份运行此脚本
    pause
    exit /b 1
)
REM 创建目录
if not exist "%PATCH_DIR%" mkdir "%PATCH_DIR%"
if not exist "%LOG_FILE%" (
    if not exist "C:\Logs\" mkdir "C:\Logs"
    echo Patch Installer Script Started at %date% %time% > "%LOG_FILE%"
)
REM 日志函数
echo. >> "%LOG_FILE%"
echo ======================================== >> "%LOG_FILE%"
echo Starting patch installation at %date% %time% >> "%LOG_FILE%"
echo ======================================== >> "%LOG_FILE%"
REM 备份系统状态
echo Creating system restore point...
wmic /namespace:\\root\default path SystemRestore call create "Before patch installation" >> "%LOG_FILE%"
REM 遍历安装MSU补丁
echo Scanning for patches in %PATCH_DIR%...
set PATCH_COUNT=0
set SUCCESS_COUNT=0
set FAIL_COUNT=0
for %%f in ("%PATCH_DIR%\*.msu") do (
    set /a PATCH_COUNT+=1
    echo. >> "%LOG_FILE%"
    echo Installing patch: %%~nxf >> "%LOG_FILE%"
    REM 用wusa安装补丁
    wusa "%%f" /quiet /norestart
    if %errorlevel% equ 0 (
        echo Successfully installed: %%~nxf >> "%LOG_FILE%"
        set /a SUCCESS_COUNT+=1
        REM 重命名已安装的文件
        ren "%%f" "%%~nxf.installed"
    ) else (
        echo Failed to install: %%~nxf (Error: %errorlevel%) >> "%LOG_FILE%"
        set /a FAIL_COUNT+=1
    )
)
REM 输出统计信息
echo. >> "%LOG_FILE%"
echo ======================================== >> "%LOG_FILE%"
echo Installation Summary at %date% %time% >> "%LOG_FILE%"
echo Total Patches: %PATCH_COUNT% >> "%LOG_FILE%"
echo Successfully Installed: %SUCCESS_COUNT% >> "%LOG_FILE%"
echo Failed: %FAIL_COUNT% >> "%LOG_FILE%"
echo ======================================== >> "%LOG_FILE%"
REM 检查是否需要重启
if %SUCCESS_COUNT% gtr 0 (
    echo.
    echo Some patches were installed successfully.
    echo Please reboot your system to complete the installation.
    choice /m "Do you want to reboot now?"
    if %errorlevel% equ 1 (
        shutdown /r /t 30 /c "Rebooting to complete patch installation"
    )
)
echo.
echo Patch installation process completed. Check %LOG_FILE% for details.
pause

使用自动化工具(推荐)

Ansible Playbook 示例

---
- name: Automatic Patch Installation
  hosts: all
  become: yes
  vars:
    patch_dir: /var/tmp/patches
    log_file: /var/log/patch_install.log
  tasks:
    - name: Create patch directory
      file:
        path: "{{ patch_dir }}"
        state: directory
    - name: Copy patches to target
      copy:
        src: "{{ item }}"
        dest: "{{ patch_dir }}/"
      loop:
        - patch1.rpm
        - patch2.rpm
    - name: Apply patches (RHEL/CentOS)
      yum:
        name: "{{ patch_dir }}/*.rpm"
        state: present
      when: ansible_os_family == "RedHat"
    - name: Apply patches (Debian/Ubuntu)
      apt:
        deb: "{{ item }}"
      loop: "{{ lookup('fileglob', '{{ patch_dir }}/*.deb') }}"
      when: ansible_os_family == "Debian"
    - name: Check if reboot needed
      stat:
        path: /var/run/reboot-required
      register: reboot_required
    - name: Reboot if needed
      reboot:
        msg: "Rebooting to apply patches"
        reboot_timeout: 300
      when: reboot_required.stat.exists

使用建议

  1. 测试环境先行:在生产环境使用前,务必备份并测试
  2. 日志记录:保留完整日志便于问题追溯
  3. 错误处理:处理常见错误情况(磁盘空间、权限、依赖等)
  4. 通知机制:设置邮件或通知,了解安装结果
  5. 回滚准备:制定回滚计划,以防补丁导致问题
  6. 分阶段部署:分批安装,先测试组,再逐步推广

选择哪种方案取决于你的具体需求、操作系统环境和管理工具,建议优先使用专业的补丁管理工具。

抱歉,评论功能暂时关闭!