From 2446c7c5c76a654615ec8bd56493b81651f48ead Mon Sep 17 00:00:00 2001 From: Shuery <2463253700@qq.com> Date: Sun, 27 Sep 2026 11:34:34 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=8A=8A"=E5=89=8D=E7=BC=80=E8=A1=A5?= =?UTF-8?q?=E5=85=A8=E5=8F=AA=E6=90=9C=E4=B8=80=E5=B1=82=E3=80=81=E4=B8=94?= =?UTF-8?q?=E4=B8=8D=E6=8F=90=E4=BE=9B=E6=B7=B1=E5=BA=A6=E5=BC=80=E5=85=B3?= =?UTF-8?q?"=E5=86=99=E6=88=90=E5=86=B3=E7=AD=96=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 深度递归不是"补上一个没实现的功能",而是引入一个具体的错。写成 ADR-0009 连同实测数据:26 条真实 Path 里 19 条直接命中、3 条补全救不了(软件没装)、1 条(%UserProfile%\fnm)在 5 层内会命中 AppData\Local\fnm_multishells 这个临时目录 —— 静默备份错的东西还报成功。 同时在 Find-BakNRetChildDirectoryByName 的注释里指回 ADR,免得下一个人把它当"漏了的功能"补回去。 顺带修掉 README 里一句与实现不符的话:原先写"前缀补全命中多个候选(同名目录分散在多处)",而只搜一层时多个候选只可能来自同一个父目录(既有 X 又有 X_后缀)。 全仓复查:MaxDepth / CatalogMaxDepth / 最大深度 / 向下找几层 除 ADR-0009 的历史叙述外 0 处。 验收:test.ps1 9/9 全绿(7 与 5.1)。 --- .../Find-BakNRetChildDirectoryByName.ps1 | 5 +++ README.md | 4 +- .../0009-prefix-completion-is-single-level.md | 40 +++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 docs/adr/0009-prefix-completion-is-single-level.md diff --git a/BakNRet/Public/Find-BakNRetChildDirectoryByName.ps1 b/BakNRet/Public/Find-BakNRetChildDirectoryByName.ps1 index 4e642fa..2d37027 100644 --- a/BakNRet/Public/Find-BakNRetChildDirectoryByName.ps1 +++ b/BakNRet/Public/Find-BakNRetChildDirectoryByName.ps1 @@ -6,6 +6,11 @@ .DESCRIPTION 只做保守的前缀补全:必须以下一个字符是 _ 或 - 为界, 避免把 Legendary 匹配成 LegendarySomething。 + + 为什么只搜一层、而且**没有**"向下找几层"的参数:见 + docs/adr/0009-prefix-completion-is-single-level.md。实测那件事值得记住 —— + 允许向下递归时,"fnm 那条会命中 AppData\Local\fnm_multishells 这个临时目录", + 等于静默备份错的东西还报成功;而只搜一层时它是明确报"源不存在"。 #> param( [Parameter(Mandatory = $true)][string]$Parent, diff --git a/README.md b/README.md index 7d65f91..2e0d13e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# BakNRet +# BakNRet 把 `BackupList.txt` 里列出的软件 / 目录用 **7-Zip** 打包进 `Backups/`,并且能用 `Restore.ps1` 原样恢复的 Windows 备份工具。 @@ -339,7 +339,7 @@ powershell 内置 ZIP 分支也不支持排除规则(`Compress-Archive` 没有对应开关),只保证内容完整。 - **暂存改名需要能建目录连接点(junction)。** 暂存目录在 `%TEMP%`(NTFS 即可),目标源目录跨盘也没问题; 建不出连接点时该条目会明确失败,而不会静默换成别的布局。恢复时的 junction 建不出来会自动退回"临时目录 + 合并"。 -- **一个 Slot 只能对应一个目录。** 前缀补全命中多个候选(同名目录分散在多处)时会报错并让你拆成多个 Slot, +- **一个 Slot 只能对应一个目录。** 前缀补全命中多个候选(同一个父目录下既有 `X` 又有 `X_后缀`)时会报错并让你拆成多个 Slot, 而不是任选一个。 - **`!re:` 有量级上限。** 正则命中的路径超过 300 条、或排除参数超过命令行安全长度时会明确失败; 这种场景应改用更粗的通配模式。 diff --git a/docs/adr/0009-prefix-completion-is-single-level.md b/docs/adr/0009-prefix-completion-is-single-level.md new file mode 100644 index 0000000..80358b1 --- /dev/null +++ b/docs/adr/0009-prefix-completion-is-single-level.md @@ -0,0 +1,40 @@ +# 前缀补全只搜一层,而且**不提供**"向下找几层"的开关 + +名录里的 `Path` 按字面不存在时,会做一次前缀补全:在同一父目录下找 `<名>_<后缀>` 或 +`<名>-<后缀>` 形式的目录。这个过程**只看直接子目录**,并且刻意没有"最多向下几层"的参数。 + +历史上确实有过这样一个参数:`CatalogMaxDepth`(出厂值 5),从配置一路传到 +`Resolve-BakNRetEntry` → `Get-BakNRetItemArchiveName` → `Get-BakNRetSoftwareCatalog` → +`Find-BakNRetChildDirectoryByName`。但它**从未被使用** —— 最后一个函数接下了参数, +实现里却只有 `Get-ChildItem -Directory`。也就是说那个配置项写多少都不影响行为。 + +2026-09 的改造把整条链路删掉了(配置项、三个函数的参数、70 处调用点实参)。理由不只是 +"文档不该承诺代码不做的事",还有一条拿真实名录量出来的发现。 + +## 实测:让深度生效会让一个条目备份错目录 + +拿真实的 `SoftwareCatalog.psd1`(26 条 Path)逐条试"按字面存在吗 / 1 层能救吗 / 5 层能救吗": + +| 情况 | 条数 | 说明 | +| --- | --- | --- | +| 直接命中 | 19 | —— | +| 补全救不了,无论几层 | 3 | `AutoDarkMode` / `dsh-desktop` / `twinkle-tray`:软件没装,报"源不存在"是对的 | +| **深度生效后会命中错的东西** | 1 | `%UserProfile%\fnm`:1 层内 0 个,**5 层内命中 `AppData\Local\fnm_multishells`** | + +`fnm_multishells` 是 fnm 做 shell 集成用的**临时目录**,不是它的数据目录。所以如果那个参数 +真的生效,`FastNodeManager` 会**静默备份错的东西并报告成功**;而今天它的行为是明确报 +"源不存在" —— 一个可见的失败,胜过一个不可见的成功。 + +## 根因:那条判据只在"同一个父目录下"才成立 + +补全的判据是"下一个字符是 `_` 或 `-`"(`^<名>(_|-)`)。原作者在注释里写明了它的用意 —— +"避免把 Legendary 匹配成 LegendarySomething"。这个边界在**同一层**是保守的,但一旦允许 +向下递归,它就会命中任意深度的"同名亲戚":`fnm` 与 `AppData\Local\fnm_multishells` 之间 +隔着两层目录,判据对此毫无抵抗力。 + +## 所以 + +**不要重新加回深度参数。** 如果将来确实需要"路径变了也能找到",正确的顺序是先把判据收紧 +(例如要求后缀像版本号:`^<名>[-_]\d`),并且把每次补全的事实打印出来 +("我把 X 解析成了 Y"),让人看见它猜了什么 —— 而不是扩大搜索范围,因为搜索范围越大, +这种启发式越容易命中看起来对、其实不对的东西。