当前位置:网站首页 > 更多 > 玩电脑 > 正文

[玩转系统] 创建 PowerShell 备份系统 - 第 4 部分

作者:精品下载站 日期:2024-12-14 07:55:57 浏览:14 分类:玩电脑

创建 PowerShell 备份系统 - 第 4 部分


我的 PowerShell 备份系统即将结束。上次我向您展示了如何处理每日增量备份。今天我想我应该回过头来回顾一下我如何处理每周的完整备份。请记住,我只关心备份少数关键文件夹。我已将该列表保存到一个文件中,如果我需要开始备份其他内容,我可以随时更新该文件。

PowerShell 计划作业

让我们首先看一下我用来设置每周五晚上 10:00 运行的 PowerShell 计划作业的代码。我将代码保存在脚本文件中,以便在需要时可以重新创建作业。

#create weekly full backups

$trigger = New-JobTrigger -At 10:00PM -DaysOfWeek Friday -WeeksInterval 1 -Weekly
$jobOpt = New-ScheduledJobOption -RunElevated -RequireNetwork -WakeToRun

$params = @{
 FilePath = "C:\scripts\WeeklyFullBackup.ps1"
 Name = "WeeklyFullBackup" 
 Trigger = $trigger 
 ScheduledJobOption = $jobOpt 
 MaxResultCount = 5 
 Credential = "$env:computername\jeff"
}
Register-ScheduledJob @params

我想指出的一件事是,我不是定义脚本块,而是指定文件路径。这样我就可以根据需要调整脚本,而无需重新创建或修改计划的作业。当然,因为我在脚本文件中包含了这个,所以这并不困难,但我不喜欢在不必要的情况下接触正在工作的东西。我添加了凭据,因为我的部分任务包括将备份存档文件复制到我的 Synology NAS 设备。

备份控制脚本

计划作业调用的脚本非常简单。它必须处理目录列表并将每个文件夹备份到 RAR 文件。我使用 WinRar 作为我的存档解决方案。您可以替换您自己的备份或存档代码。我将在以后的文章中保存一些变体。然后,每个存档文件都会移动到我的 NAS 设备。现在我想起来了,我可能应该添加一些代码来将副本发送到我的一个云驱动器(例如 OneDrive)。

这是完整的脚本。

#requires -version 5.1

[cmdletbinding(SupportsShouldProcess)]
Param()

If (Test-Path -Path c:\scripts\mybackupPaths.txt) {

    #filter out blanks and commented lines
    $paths = Get-Content c:\scripts\mybackupPaths.txt | Where-Object { $_ -match "(^[^#]\S*)" -and $_ -notmatch "^\s+$" }
   
    #import my custom module
    Import-Module C:\scripts\PSRAR\Dev-PSRar.psm1 -force
   
    $paths | ForEach-Object {

        if ($pscmdlet.ShouldProcess($_)) {
            Try {
                #invoke a control script using my custom module
                C:\scripts\RarBackup.ps1 -Path $_ -Verbose -ErrorAction Stop
                $ok = $True
            }
            Catch {
                $ok = $False
                Write-Warning $_.exception.message
            }
        }

        #clear corresponding incremental log files
        $name = ((Split-Path $_ -Leaf).replace(' ', ''))
        #specify the directory for the CSV log files
        $log = "D:\Backup\{0}-log.csv" -f $name

        if ($OK -AND (Test-Path $log) -AND ($pscmdlet.ShouldProcess($log, "Clear Log"))) {
            Remove-Item -path $log
        }
    }
}
else {
    Write-Warning "Failed to find c:\scripts\mybackupPaths.txt"
}

RarBackup.ps1 脚本负责实际归档和复制到 NAS。

备份脚本

该文件调用我的 RAR 命令来创建存档并将其复制到 NAS。我有使用 Get-FileHash 验证文件复制是否成功的代码。

#requires -version 5.1

[cmdletbinding(SupportsShouldProcess)]
Param(
    [Parameter(Mandatory)]
    [ValidateScript( { Test-Path $_ })]
    [string]$Path,
    [ValidateScript( { Test-Path $_ })]
    #my temporary work area with plenty of disk space
    [string]$TempPath = "D:\Temp",
    [ValidateSet("FULL", "INCREMENTAL")]
    [string]$Type = "FULL"
)

Write-Verbose "[$(Get-Date)] Starting $($myinvocation.MyCommand)"
if (-Not (Get-Module Dev-PSRar)) {
    Import-Module C:\scripts\PSRAR\Dev-PSRar.psm1 -force
}

#replace spaces in path names
$name = "{0}_{1}-{2}.rar" -f (Get-Date -format "yyyyMMdd"), (Split-Path -Path $Path -Leaf).replace(' ', ''), $Type
$target = Join-Path -Path $TempPath -ChildPath $name

#I have hard coded my NAS backup. Would be better as a parameter with a default value.
$nasPath = Join-Path -Path \DS416\backup -ChildPath $name
Write-Verbose "[$(Get-Date)] Archiving $path to $target"

if ($pscmdlet.ShouldProcess($Path)) {
    #Create the RAR archive -you can use any archiving technique you want
    Add-RARContent -path $Path -Archive $target -CompressionLevel 5 -Comment "$Type backup of $(($Path).ToUpper()) from $env:Computername"

    Write-Verbose "[$(Get-Date)] Copying $target to $nasPath"
    Try {
        #copy the RAR file to the NAS for offline storage
        Copy-Item -Path $target -Destination $NASPath -ErrorAction Stop
    }
    Catch {
        Write-Warning "Failed to copy $target. $($_.exception.message)"
        Throw $_
    }
    #verify the file was copied successfully
    Write-Verbose "[$(Get-Date)] Validating file hash"

    $here = Get-FileHash $Target
    $there = Get-FileHash $nasPath
    if ($here.hash -eq $there.hash) {
        #delete the file if the hashes match
        Write-Verbose "[$(Get-Date)] Deleting $target"
        Remove-Item $target
    }
    else {
        Write-Warning "File hash difference detected."
        Throw "File hash difference detected"
    }
}

Write-Verbose "[$(Get-Date)] Ending $($myinvocation.MyCommand)"

#end of script file

从结构上讲,我可以将所有这些代码放入一个 PowerShell 脚本中。但我喜欢更加模块化的方法,这样我就可以重复使用脚本或命令。或者,如果我需要修改某些内容,我不必担心会破坏整体脚本。

报告

我想分享的最后一步是报告。我不太担心检查预定作业的结果。我需要做的就是检查我的备份文件夹中的存档文件。由于我的备份存档遵循标准命名约定,因此我可以使用正则表达式来匹配文件名。我使用这个控制脚本来查看我的备份档案。

# requires -version 5.1

[cmdletbinding()]
Param(
    [Parameter(Position = 0, HelpMessage = "Enter the path with extension of the backup files.")]
    [ValidateNotNullOrEmpty()]
    [ValidateScript( { Test-Path $_ })]
    [string]$Path = "\ds416\backup",
    [Parameter(HelpMessage = "Get backup files only with no formatted output.")]
    [Switch]$Raw
)

<#
 A regular expression pattern to match on backup file name with named captures
 to be used in adding some custom properties. My backup names are like:

  20191101_Scripts-FULL.rar
  20191107_Scripts-INCREMENTAL.rar

  #>
[regex]$rx = "^20\d{6}_(?<set>\w+)-(?<type>\w+)\.rar$"

<#
I am doing so 'pre-filtering' on the file extension and then using the regular
expression filter to fine tune the results
#>
$files = Get-ChildItem -path $Path -filter *.rar | Where-Object { $rx.IsMatch($_.name) }

#add some custom properties to be used with formatted results based on named captures
foreach ($item in $files) {
    $setpath = $rx.matches($item.name).groups[1].value
    $settype = $rx.matches($item.name).groups[2].value

    $item | Add-Member -MemberType NoteProperty -Name SetPath -Value $setpath
    $item | Add-Member -MemberType NoteProperty -Name SetType -Value $setType
}

if ($raw) {
    $Files
}
else {
    $files | Sort-Object SetPath, SetType, LastWriteTime | Format-Table -GroupBy SetPath -Property LastWriteTime, Length, Name
}

默认行为是显示格式化输出。

[玩转系统] 创建 PowerShell 备份系统 - 第 4 部分

通常,您永远不会希望在代码中包含 Format cmdlet。但在本例中,我使用脚本来给出我想要的结果,而无需进行大量输入。这就是为什么这是一个脚本而不是一个函数。考虑到这一点,控制脚本还有一个 -Raw 参数,它将文件对象写入管道。

[玩转系统] 创建 PowerShell 备份系统 - 第 4 部分

我的每周备份脚本会删除增量文件。现在,我将手动删除旧的完整备份。虽然我可以添加代码来只保留最后 5 个完整备份。

这个系统仍然是新的,我在不断调整和添加新工具。鉴于我已经为我和我的网络编写了代码,我觉得我无法将其变成一个完整的、独立的 PowerShell 模块。但是我的代码应该足够您可以用来创建您自己的解决方案。我还有一些其他的备份想法和技术,下次我会与大家分享。与此同时,我很想听听您对这一切的看法。

您需要 登录账户 后才能发表评论

取消回复欢迎 发表评论:

关灯