From bbfece10b6e469f2ce4e5a25d4740e4b75aab3d2 Mon Sep 17 00:00:00 2001 From: Shuery <2463253700@qq.com> Date: Sun, 27 Sep 2026 10:25:05 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E5=85=A5=E5=8F=A3=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E4=B8=8B=E6=B2=89=E7=AC=AC=E4=B8=80=E5=9D=97=EF=BC=88?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E8=A7=A3=E6=9E=90=E4=B8=8E=E6=9D=A1=E7=9B=AE?= =?UTF-8?q?=E7=AD=9B=E9=80=89=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 把两个内联函数从入口脚本搬进模块,它们正好覆盖下沉时最容易出错的三类依赖: * Resolve-BakNRetRootedPath(原 Backup.ps1 与 Restore.ps1 各一份同名副本)靠 $PSScriptRoot 找仓库根 —— 搬进模块之后 $PSScriptRoot 会变成**模块目录**,语义就悄悄变了。所以根目录改成显式参数,由调用方传 $PSScriptRoot。 * Test-BakNRetItemSelected(原在 Backup.ps1 里)直接读脚本级的 $Only / $Skip。那种闭包依赖让它没法单独测:要测它就得先构造一个入口脚本。现在两个清单是显式参数。 * 第三个函数 Get-7zExecutable 我**没有**动:它和模块里已有的 7z 定位功能重复,但两者返回的东西不同(一个路径字符串、一个带 Name/Command/Extension 的对象),合并要先确认语义,不能顺手删。记在下面待办里。 过程里翻车一次并当场修好:给调用点补参数时我是按"行尾追加"做的,结果 3 处把参数甩到了 `)` 或 `}` 外面(例如 `if (...) { continue } -Only $Only`)。**解析是零错的** —— 又一次"绿着但是错的"。修法不是继续追加,而是按内容精确定位那 4 行、把参数插到括号里面;修完先用 5 个即时探针确认两个函数真的能调起来(含 -Only 命中、-Skip 否决、绝对路径不被拼根),再跑全套。 验收:test.ps1 9/9 全绿(7 与 5.1)、102 个文件两版解析零错、构建工具仍能合回单文件。 --- Backup.ps1 | 32 +++----------------- BakNRet/BakNRet.psd1 | 2 ++ BakNRet/BakNRet.psm1 | 5 +++ BakNRet/Public/Resolve-BakNRetRootedPath.ps1 | 23 ++++++++++++++ BakNRet/Public/Test-BakNRetItemSelected.ps1 | 29 ++++++++++++++++++ Restore.ps1 | 12 ++------ 6 files changed, 66 insertions(+), 37 deletions(-) create mode 100644 BakNRet/Public/Resolve-BakNRetRootedPath.ps1 create mode 100644 BakNRet/Public/Test-BakNRetItemSelected.ps1 diff --git a/Backup.ps1 b/Backup.ps1 index 9f90877..46b09b6 100644 --- a/Backup.ps1 +++ b/Backup.ps1 @@ -95,18 +95,10 @@ if ($PSBoundParameters.ContainsKey('Verbose')) { Set-BakNRetDebug } $script:Config = Get-BakNRetConfig -Path $ConfigPath -function Resolve-ConfigPath { - param([string]$Path, [string]$Default) - $value = if ($Path) { $Path } else { $Default } - if (-not [System.IO.Path]::IsPathRooted($value)) { - $value = Join-Path $PSScriptRoot $value - } - return $value -} -if (-not $BackupDir) { $BackupDir = Resolve-ConfigPath -Path $null -Default $script:Config.BackupDir } -$logDir = Resolve-ConfigPath -Path $null -Default $script:Config.LogDir -$snapshotDir = Resolve-ConfigPath -Path $null -Default $script:Config.SnapshotDir +if (-not $BackupDir) { $BackupDir = Resolve-BakNRetRootedPath -Path $null -Default $script:Config.BackupDir -Root $PSScriptRoot } +$logDir = Resolve-BakNRetRootedPath -Path $null -Default $script:Config.LogDir -Root $PSScriptRoot +$snapshotDir = Resolve-BakNRetRootedPath -Path $null -Default $script:Config.SnapshotDir -Root $PSScriptRoot $catalogPath = Resolve-BakNRetCatalogPath -Configured $script:Config.SoftwareCatalog -Root $PSScriptRoot $manifestPath = Join-Path $BackupDir 'manifest.json' @@ -189,20 +181,6 @@ if ($freeSpaceGB -ge 0) { } } -function Test-ItemSelected { - param([string]$DisplayPath, [string]$BaseName) - if ($Only.Count -gt 0) { - $matched = $false - foreach ($pattern in $Only) { - if ($DisplayPath -like $pattern -or $BaseName -like $pattern) { $matched = $true; break } - } - if (-not $matched) { return $false } - } - foreach ($pattern in $Skip) { - if ($DisplayPath -like $pattern -or $BaseName -like $pattern) { return $false } - } - return $true -} function New-ItemRecord { param([string]$BaseName, [string]$Source, [string]$ResolvedSource, [string]$Phase) @@ -439,7 +417,7 @@ foreach ($planLine in $lines) { $planDisplayPath = $planItem.Path $planResolved = Resolve-BakNRetBackupEntry -Entry $planItem -CatalogPath $catalogPath if (-not $planResolved.BaseName) { continue } - if (-not (Test-ItemSelected -DisplayPath $planDisplayPath -BaseName $planResolved.BaseName)) { continue } + if (-not (Test-BakNRetItemSelected -DisplayPath $planDisplayPath -BaseName $planResolved.BaseName -Only $Only -Skip $Skip)) { continue } if ($planResolved.Direction -eq 'restore') { continue } if ($planResolved.Blocking) { continue } @@ -554,7 +532,7 @@ foreach ($line in $lines) { $baseName = $resolved.BaseName $sourcePath = [Environment]::ExpandEnvironmentVariables($displayPath) - if (-not (Test-ItemSelected -DisplayPath $displayPath -BaseName $baseName)) { + if (-not (Test-BakNRetItemSelected -DisplayPath $displayPath -BaseName $baseName -Only $Only -Skip $Skip)) { Write-BakNRetLog "跳过(未选中): $displayPath" -Level DEBUG continue } diff --git a/BakNRet/BakNRet.psd1 b/BakNRet/BakNRet.psd1 index dfa935e..c846c11 100644 --- a/BakNRet/BakNRet.psd1 +++ b/BakNRet/BakNRet.psd1 @@ -19,6 +19,8 @@ CompatiblePSEditions = @('Desktop', 'Core') FunctionsToExport = @( + 'Resolve-BakNRetRootedPath', + 'Test-BakNRetItemSelected', 'Enter-BakNRetRunLock', 'Exit-BakNRetRunLock', 'Get-BakNRetRunLockPath', diff --git a/BakNRet/BakNRet.psm1 b/BakNRet/BakNRet.psm1 index d23be5f..93193f0 100644 --- a/BakNRet/BakNRet.psm1 +++ b/BakNRet/BakNRet.psm1 @@ -133,7 +133,12 @@ . (Join-Path $PSScriptRoot 'Public\Get-BakNRetConfig.ps1') . (Join-Path $PSScriptRoot 'Public\Get-BakNRetPassword.ps1') +. (Join-Path $PSScriptRoot 'Public\Resolve-BakNRetRootedPath.ps1') +. (Join-Path $PSScriptRoot 'Public\Test-BakNRetItemSelected.ps1') + Export-ModuleMember -Function @( + 'Resolve-BakNRetRootedPath', + 'Test-BakNRetItemSelected', 'Enter-BakNRetRunLock', 'Exit-BakNRetRunLock', 'Get-BakNRetRunLockPath', diff --git a/BakNRet/Public/Resolve-BakNRetRootedPath.ps1 b/BakNRet/Public/Resolve-BakNRetRootedPath.ps1 new file mode 100644 index 0000000..be3378a --- /dev/null +++ b/BakNRet/Public/Resolve-BakNRetRootedPath.ps1 @@ -0,0 +1,23 @@ +function Resolve-BakNRetRootedPath { + <# + .SYNOPSIS + 把可能是相对路径的配置值解析成绝对路径(相对的部分按 $Root 拼)。 + + .DESCRIPTION + 为什么 $Root 是参数、而不是函数里直接用 $PSScriptRoot:它原先在两个入口脚本里各有一份 + 同名副本(Backup.ps1 与 Restore.ps1 各 8 行),靠 $PSScriptRoot 找到仓库根。搬进模块之后 + $PSScriptRoot 会变成**模块目录**,语义就悄悄变了 —— 所以根目录必须由调用方显式传进来。 + 这是"入口逻辑下沉"里最容易出错的一类:闭包捕获的变量在模块作用域里指向别的东西。 + #> + param( + [string]$Path, + [string]$Default, + [Parameter(Mandatory = $true)][string]$Root + ) + + $value = if ($Path) { $Path } else { $Default } + if (-not [System.IO.Path]::IsPathRooted($value)) { + $value = Join-Path $Root $value + } + return $value +} diff --git a/BakNRet/Public/Test-BakNRetItemSelected.ps1 b/BakNRet/Public/Test-BakNRetItemSelected.ps1 new file mode 100644 index 0000000..9b9deef --- /dev/null +++ b/BakNRet/Public/Test-BakNRetItemSelected.ps1 @@ -0,0 +1,29 @@ +function Test-BakNRetItemSelected { + <# + .SYNOPSIS + 条目是否被 -Only / -Skip 选中(-Only 要先命中,-Skip 一票否决)。 + + .DESCRIPTION + 它原先躺在 Backup.ps1 里直接读脚本级的 $Only / $Skip —— 那种"闭包依赖"让它没法单独测: + 要测就得先构造一个入口脚本。搬进模块时把两个清单做成显式参数,于是它既能被单元测试直接 + 调用,也让"到底拿什么在筛选"在调用点就看得见。 + #> + param( + [string]$DisplayPath, + [string]$BaseName, + [string[]]$Only = @(), + [string[]]$Skip = @() + ) + + if ($Only.Count -gt 0) { + $matched = $false + foreach ($pattern in $Only) { + if ($DisplayPath -like $pattern -or $BaseName -like $pattern) { $matched = $true; break } + } + if (-not $matched) { return $false } + } + foreach ($pattern in $Skip) { + if ($DisplayPath -like $pattern -or $BaseName -like $pattern) { return $false } + } + return $true +} diff --git a/Restore.ps1 b/Restore.ps1 index 879eed6..660cd90 100644 --- a/Restore.ps1 +++ b/Restore.ps1 @@ -86,17 +86,9 @@ if ($PSBoundParameters.ContainsKey('Verbose')) { Set-BakNRetDebug } $script:Config = Get-BakNRetConfig -Path $ConfigPath $SupportedFormats = @('.7z', '.rar', '.zip', '.tar') -function Resolve-ConfigPath { - param([string]$Path, [string]$Default) - $value = if ($Path) { $Path } else { $Default } - if (-not [System.IO.Path]::IsPathRooted($value)) { - $value = Join-Path $PSScriptRoot $value - } - return $value -} -if (-not $BackupDir) { $BackupDir = Resolve-ConfigPath -Path $null -Default $script:Config.BackupDir } -$logDir = Resolve-ConfigPath -Path $null -Default $script:Config.LogDir +if (-not $BackupDir) { $BackupDir = Resolve-BakNRetRootedPath -Path $null -Default $script:Config.BackupDir -Root $PSScriptRoot } +$logDir = Resolve-BakNRetRootedPath -Path $null -Default $script:Config.LogDir -Root $PSScriptRoot $catalogPath = Resolve-BakNRetCatalogPath -Configured $script:Config.SoftwareCatalog -Root $PSScriptRoot $manifestPath = Join-Path $BackupDir 'manifest.json'