先侦察事实(232 行、71 个 Slot 级字段):**可编辑面比预想的还窄,边界落在一条可判定的规则上** —— Encrypt 19 个全是单行(可编辑),Description 24 个单行 + 1 个跨行(Edge 那条多行说明,只读),Path / Exclude 一律只读(Scoop 三条带 $(if ($env:SCOOP_GLOBAL){...}) 表达式、Edge 的 Exclude 是跨行拼接)。
规则:**只有单行的 Encrypt / Description 可编辑** —— 与 ADR-0013 的"按能不能安全往返来定"一致:"改一行"对动态表达式与跨行拼接没有明确含义。
扫描时跳过块注释:文件头部模板里有 SoftWareName = @{ ... } 示例,纯文本扫描会把它当成一个软件(实测踩到)。定位靠 (软件名, Slot, 字段名) 三元组且要求唯一命中,命中 0 个或多个都抛错。
实测五个事实:认出 70 个字段、可编辑 43 个(19+24)、往返**逐字节相同 = True**、真改一个只动一行、Edge 的跨行 Description 正确地只读。
过程里两处自己的坑都被门禁逮住:插入的测试代码带过一行手滑垃圾(Parse 层当场报),以及函数文档注释里写了块注释的闭合符号 —— 它提前结束了那段注释,后半句变成代码,而它是**合法语法**所以解析层抓不到、跑到才炸(与"左边空的赋值"同族)。
验收:test.ps1 9/9 全绿(5.1 与 7)。
61 lines
2.8 KiB
PowerShell
61 lines
2.8 KiB
PowerShell
function Get-BakNRetCatalogField {
|
||
<#
|
||
.SYNOPSIS
|
||
把名录文本拆成"Slot 级字段"的表:行号、软件名、Slot 名、字段名、原文值、是否可编辑。
|
||
|
||
.DESCRIPTION
|
||
可编辑面按**能不能安全往返**来定(ADR-0013):
|
||
|
||
* `Encrypt` / `Description`,且值在**一行之内** → 可编辑(真实名录里是 19 + 24 个);
|
||
* `Path` / `Exclude` / `Include` → 一律只读:它们常带 `$( )` 动态表达式(Scoop 那三条
|
||
的 `$(if ($env:SCOOP_GLOBAL) { ... })`)与**跨行字符串拼接**(Edge 的 Exclude),
|
||
"改一行"对它们没有明确含义;
|
||
* `Description` 也可能是跨行的(Edge 那条多行说明)→ 那种同样只读。
|
||
|
||
文件头部的注释模板里有 `SoftWareName = @{ ... }` 这样的**示例**:纯文本扫描会把它当成一个
|
||
软件,所以扫描时显式跳过块注释里的内容。注意本文档里**不能**写那对块注释符号的闭合形式:
|
||
它会提前结束上面这段注释(实测踩到:后半句变成了代码,而它是合法语法 —— 等于调用一个
|
||
同名命令,所以解析层抓不到,只有跑到那一步才炸)。
|
||
#>
|
||
param([Parameter(Mandatory = $true)][AllowEmptyString()][string]$Text)
|
||
|
||
$lines = $Text -split "`r?`n"
|
||
$inComment = $false
|
||
$app = ''
|
||
$slot = ''
|
||
$rows = @()
|
||
|
||
for ($index = 0; $index -lt $lines.Count; $index++) {
|
||
$line = $lines[$index]
|
||
$trimmed = $line.Trim()
|
||
|
||
# 块注释:整段跳过(否则模板里的示例会被当成真条目)
|
||
if ($trimmed.StartsWith('<#')) { $inComment = $true; continue }
|
||
if ($trimmed.StartsWith('#>')) { $inComment = $false; continue }
|
||
if ($inComment) { continue }
|
||
if ($trimmed -eq '' -or $trimmed.StartsWith('#')) { continue }
|
||
|
||
if ($line -match '^ ([\w''\-]+)\s*=\s*@\{\s*$') { $app = $Matches[1]; $slot = ''; continue }
|
||
if ($line -match '^ ([\w''\-]+)\s*=\s*@\{\s*$') { $slot = $Matches[1]; continue }
|
||
if ($line -match '^\s{8}\}') { $slot = ''; continue }
|
||
if ($line -match '^\s{4}\}') { $app = ''; continue }
|
||
|
||
if ($slot -and $line -match '^\s{12}([\w]+)\s*=\s*(.+)$') {
|
||
$key = $Matches[1]
|
||
$value = $Matches[2]
|
||
$singleLine = $value -match "^('.*'|(\`$true|\`$false))\s*$"
|
||
$rows += [pscustomobject]@{
|
||
Line = $index + 1
|
||
App = $app
|
||
Slot = $slot
|
||
Key = $key
|
||
Value = $value
|
||
Editable = (($key -eq 'Encrypt' -or $key -eq 'Description') -and $singleLine)
|
||
Raw = $line
|
||
}
|
||
}
|
||
}
|
||
|
||
return @($rows)
|
||
}
|