docs: README 拆出四篇主题文档,本文件留总览与操作

拆出软件名录(67 行)、清单语法(114 行)、归档布局与迁移(47 行)、安全描述符(69 行),共 297 行;README 从 653 降到 360 行,每节原位留一句摘要加链接。

为什么拆:这四块是「要查的资料」,README 剩下的是「要读的流程」。混在一起时,想查清单语法的人得先滚过一百多行总览;拆开后每篇也能被单独引用与单独评审。

搬运按标题抽取原文、不重打,并在写之前断言正文长度、写之后再读回断言一次(第一次尝试就是栽在没有断言上:@(a, b, $arr) 不会展开数组,而是把 $arr 拼成一行,结果四篇各只有 5 行、正文却已从 README 删除。那次已回滚)。
This commit is contained in:
Shuery committed 2026-09-27 10:20:08 +08:00
1 parent fb0d93a25f
commit 60ecbc2933
5 files changed
+322 -301

No files matched your search

+118
View File
@@ -0,0 +1,118 @@
# BackupList.txt 语法
> 本文从 [README.md](../README.md) 拆出,单独成篇是为了让它能被单独引用与单独评审。
```text
[+|-] <软件名 或 绝对路径> [ :: <绝对路径> ] [ :- <模式>[,<模式>...] ] [ :+ <追加项>[,<追加项>...] ]
[ :encrypt | :!encrypt ] [ @ <Key>='<值>' ] [ # 说明 ]
```
修饰符必须是**独立的、前后带空白的记号**,所以路径里出现的 `:-`、`C:\a#b` 之类不会被误切。
### 目标(二选一)
| 写法 | 说明 |
| --- | --- |
| `Edge` | **软件名**:去 `SoftwareCatalog.psd1` 查 Slot 组,**归档名 = 软件名** |
| `C:\Programs\MiFlash` | **手写路径**:含 `\` `/` 或 `%` 就按路径处理,归档名 = `<末级名>_from_<上级路径用 + 连接>` |
| `Edge @pathname` | 软件名 + 强制用路径命名(想换到名录体系但暂时不想改归档名时用) |
### 行首方向标记
| 标记 | 作用 |
| --- | --- |
| `+` | **仅备份,不恢复**(`Restore.ps1` 会跳过它;归档名照旧算"有主"的,不会被报成孤儿) |
| `-` | **仅恢复,不备份**(`Backup.ps1` 会跳过它;适合放在别处、必要时才还原的目录) |
| 无 | 既能备份也能恢复(默认) |
### 修饰符
| 修饰符 | 等价写法 | 作用 |
| --- | --- | --- |
| `:: <绝对路径>` | `@ Path='<绝对路径>'` | 覆盖 Path(软件名条目只有一个 Slot 时可用) |
| `:- <模式>[,...]` | `@ Exclude='<模式>'` | 排除模式(`,` `;` 都当分隔符),**覆盖**名录里各 Slot 的 Exclude |
| `:+ <追加项>[,...]` | `@ Include='<追加项>'` | 追加项,语法 `<归档内相对路径>:<宿主机绝对路径>`,**覆盖**名录里的 Include |
| `:encrypt` | `@ Encrypt='$true'` | 该条目加密 |
| `:!encrypt` | `@ Encrypt='$false'` | 该条目不加密 |
| `@ <Key>='<值>'` | — | 覆盖名录里的默认字段(目前支持 Path / Exclude / Include / Encrypt) |
兼容的历史写法仍然认:`@encrypt` / `@!encrypt` / `@pathname` / `@root=<名>`(`root=` 已废弃,只会打印告警)。
```text
# 软件名:用名录里的 Slot 与排除;再把额外目录放进包内 Modules\ 位置
Scoop :- GlobalPersist\steam\steamapps
# 手写目录 + 排除
C:\Programs\MiFlash :- MiFlash\logs\
# 追加映射:把宿主机的 D:\extra\ps-modules 放到包内 Modules\ 下
PowerShell :+ Modules:D:\extra\ps-modules
# 覆盖加密(名录里默认加密时特别有用)
PowerShell @ Encrypt='$false'
WindowsPowerShell :!encrypt
```
### 模式(排除 / 追加)怎么写
模式匹配的是**归档内的相对路径**,而且**相对本 Slot 的根**(也就是 `<Slot>\` 里面那一层):
| 形态 | 展开成 | 说明 |
| --- | --- | --- |
| `<相对路径>` | `-x!<Slot>\<相对路径>` | 锚定在归档根下这一份 |
| `!<通配>` | `-xr!<通配>` | **任意层级**按组件名匹配,`*` `?` 是 7z 通配符(不是正则) |
| `!re:<正则>` | 若干 `-x!<完整路径>` | **正则**:脚本自己遍历源目录把命中的路径展开成精确排除项 |
| `GlobalPersist\steam` | `-x!GlobalPersist\steam` | 第一段是 Slot 名时,只作用在那一个 Slot 上 |
- `!*Cache` 一次覆盖 `Cache` / `Code Cache` / `GPUCache` / `DaemonCache` 等一批以 Cache 结尾的组件名。
- 模式里**不要自己写引号**;模式里的空格会被自动转成 `?`(7z 的 `-x!` 不接受带空格的模式)。
- 想把 `.log` 之类按正则排除就写 `!re:.*\.log$`;命中的目录会整棵剪掉,命中数超过 300 条会明确报错
(命令行长度有限),这时应该改用更粗的通配模式。
- 不带 `!` 的模式是**锚定**的:Edge 的 `OneAuth\WebView2\EBWebView\` 里还有一整套自己的
`Crashpad` / `BrowserMetrics` / `ProvenanceData` / `optimization_guide`,锚定模式碰不到它们,
这些可再生的东西一律用 `!<组件名>` 才会在任意层级命中。
实测效果(本机真实 Edge 配置,源 4619.9 MB):
| Edge 归档 | 大小 | 条目数 |
| --- | --- | --- |
| 排除规则生效前 | 1781 MB | 27961 |
| 排除规则生效后 | 72 MB | 2294 |
书签、密码(`Login Data`)、`Cookies`、偏好、历史、`IndexedDB`、`Local Storage` 全部保留;
缓存、组件缓存、Service Worker、扩展本体、遥测与优化数据全部排除。
### 行尾可以写"为什么"
行尾的 ` # 说明` 会被解析出来,运行时和 Slot 介绍一起打印:
```text
Edge :- !*Cache,!Crashpad # 缓存与崩溃转储都可再生,不进归档
```
`#` 必须前面有空白才算注释,所以路径里的 `C:\a#b` 不受影响。
### 运行时会把每个条目的归档项逐条介绍出来
说明来自 `SoftwareCatalog.psd1` 的 Slot,追加/排除的**来源**来自清单:
```text
[INFO] 条目:Scoop
[INFO] 归档:Scoop.7z;方向:备份 + 恢复;加密:是
[INFO] 归档项 1/3:DefaultConfig <- C:\Users\Shuery\.config\scoop
[INFO] 来源:软件名录;存在,会打包;目录
[INFO] 介绍:Scoop 配置。
[INFO] 归档项 2/3:GlobalPersist <- C:\ProgramData\scoop\persist
[INFO] 来源:软件名录;存在,会打包;目录
[INFO] 排除 1 条(来自清单的 :- / @ Exclude):GlobalPersist\steam\steamapps
[INFO] 排除 2 条(来自 BackupConfig.psd1 的 DefaultExcludes):!Thumbs.db、!desktop.ini
```
`Restore.ps1` 也会打印"哪一项还原到哪个目录、是文件还是目录、会新建还是覆盖"。
### 几个必须知道的约束
- **归档名重复会直接报错。** 归档名 = 软件名字,所以同一个软件写两遍会让两个条目互相覆盖。
- **同一软件里的 Slot 名不能重复**,追加项的归档内路径也不能和 Slot 撞;脚本会在打包前明确报错。
- **一个条目挂多个归档项时,每一项只还原自己那棵子树**,不会把兄弟项也复制到别的父目录下。
- **`::` 现在是"覆盖 Path"**,不再是 `:-` 的历史别名;排除一律写 `:-`。