Cache Migration

将 Windows 系统上任意开发工具(如 VSCode、Yarn、npm、pip、Gradle、Docker 等)的缓存、插件、数据目录迁移到指定目录(如 E/D 盘),通过 NTFS Junction 实现对应用完全透明的重定向。当用户请求"迁移应用的缓存到 X 盘"、"将 XX 的数据目录移到 D/E 盘"、"释放 C 盘空间"、"清理并迁移 XX 缓存"或类似意图时触发。 (user)

Audits

Warn

Install

openclaw skills install cache-migration

App Cache Migration

Overview

将 Windows 任意应用的缓存/插件/数据目录从当前盘(通常为 C 盘)迁移到用户指定的目标目录,通过 NTFS Junction 对应用完全透明地重定向——应用无感知,路径不变,数据在目标盘。

核心原则(不可跳过)先完整复制 → 再删除原目录 → 最后创建 Junction,确保迁移中断时数据不丢失。

适用系统:Windows 10/11(需管理员权限运行 PowerShell)
不适用:macOS / Linux(用 symlink 替代 Junction)


Step 0: 收集信息

0-1 确认目标路径

从用户对话中提取:

参数说明示例
应用名"迁移 XXX 缓存"VSCodeGradlepip
目标目录"移到 E:/AppData"E:\AppData\Local

若用户未指定目标目录,询问:"请问希望迁移到哪个目录?(如 E:\AppData\Local)"

0-2 扫描当前占用(推荐先运行)

# 运行扫描脚本,显示 AppData 中 >= 20 MB 的目录及迁移建议
powershell -File scan-usage.ps1 -MinSizeMB 20 -TopN 30

输出示例:

大小(MB)  状态          路径
   820.0  [推荐迁移]    C:\Users\admin\AppData\Local\Yarn\Cache\v6
   612.3  [已迁移]      C:\Users\admin\AppData\Roaming\Code\Cache
   410.0  [推荐迁移]    C:\Users\admin\AppData\Local\pip\Cache
   ...

0-3 查找应用缓存路径

已知应用速查表

应用源路径迁移方式
VSCode%APPDATA%\Code\{Cache, CachedData, GPUCache, Code Cache, ...}Junction
VSCode Extensions%USERPROFILE%\.vscode\extensionsJunction + --extensions-dir 启动参数
Cursor%APPDATA%\Cursor\{Cache, CachedData, ...}Junction
Cursor Extensions%USERPROFILE%\.cursor\extensionsJunction
Yarn 1.x%LOCALAPPDATA%\Yarn\Cache\v6Junction + yarn config set cache-folder <新路径>
npm%APPDATA%\npm-cacheJunction + npm config set cache <新路径>
pnpm%LOCALAPPDATA%\pnpm\cachepnpm\storeJunction + pnpm config set cache-dir <新路径>
pip%LOCALAPPDATA%\pip\CacheJunction(或 pip config set global.cache-dir <新路径>
uv (Python)%LOCALAPPDATA%\uv\cacheJunction(或 UV_CACHE_DIR 环境变量)
Poetry%LOCALAPPDATA%\pypoetry\CacheJunction
conda/Miniconda%USERPROFILE%\anaconda3\pkgsJunction(或 conda config --set pkgs_dirs <新路径>
Gradle%USERPROFILE%\.gradle\caches.gradle\wrapperJunction(或 GRADLE_USER_HOME 环境变量)
Maven%USERPROFILE%\.m2\repository.m2\wrapperJunction(或 settings.xml localRepository 节点)
NuGet%LOCALAPPDATA%\NuGet\CacheNuGet\v3-cacheJunction(或 nuget config -set globalPackagesFolder=<新路径>
IntelliJ IDEA%LOCALAPPDATA%\JetBrains\IntelliJIdea*Junction(或 IDE 内 Help → Edit Custom Properties 配置 idea.system.path
PyCharm%LOCALAPPDATA%\JetBrains\PyCharm*同上,pycharm.system.path
WebStorm%LOCALAPPDATA%\JetBrains\WebStorm*同上,webstorm.system.path
其他 JetBrains%LOCALAPPDATA%\JetBrains\<IDE名>*同上模式
Docker Desktop%LOCALAPPDATA%\DockerJunction(建议同时修改 Docker Desktop → Settings → Resources → Disk image location)
Rust Cargo%USERPROFILE%\.cargo\registry.cargo\gitJunction(或 CARGO_HOME 环境变量)
Go Modules%LOCALAPPDATA%\go\pkg\modJunction(或 GOPATH / GOMODCACHE 环境变量)
Ruby Gems%USERPROFILE%\.gemJunction(或 GEM_HOME 环境变量)
Chrome 缓存%LOCALAPPDATA%\Google\Chrome\User Data\Default\CacheJunction
Edge 缓存%LOCALAPPDATA%\Microsoft\Edge\User Data\Default\CacheJunction
Electron 应用%APPDATA%\<应用名>\{Cache,Code Cache,GPUCache}Junction(Electron 系列路径规律相同)

未知应用:如何找到缓存路径

若应用不在速查表中,按以下顺序定位缓存目录:

# 1. 搜索应用名(模糊匹配)
$appName = "MyApp"  # 替换为实际应用名

# 在 APPDATA\Roaming 中搜索
Get-ChildItem "$env:APPDATA" -Directory | Where-Object Name -like "*$appName*"

# 在 APPDATA\Local 中搜索
Get-ChildItem "$env:LOCALAPPDATA" -Directory | Where-Object Name -like "*$appName*"

# 在用户根目录下搜索隐藏目录(.xxx 格式)
Get-ChildItem "$env:USERPROFILE" -Directory -Force | Where-Object Name -like "*$appName*"

# 2. 查看进程打开的文件(Process Monitor / Handle 工具)
# 也可使用 Sysinternals Process Monitor 运行应用后筛选 "WriteFile" 事件

Step 1: 迁移执行

通用迁移(适用任意应用,首选)

# 语法
powershell -File migrate-any.ps1 -SourcePath "<原路径>" -DstPath "<目标路径>"

# 示例:迁移 pip 缓存
powershell -File migrate-any.ps1 `
  -SourcePath "$env:LOCALAPPDATA\pip\Cache" `
  -DstPath "E:\AppData\Local\pip\Cache"

# 示例:迁移 Gradle 缓存
powershell -File migrate-any.ps1 `
  -SourcePath "$env:USERPROFILE\.gradle\caches" `
  -DstPath "E:\AppData\gradle\caches"

# 示例:迁移 Docker Desktop 数据
powershell -File migrate-any.ps1 `
  -SourcePath "$env:LOCALAPPDATA\Docker" `
  -DstPath "E:\AppData\Docker"

# 示例:迁移 IntelliJ IDEA 本地缓存
powershell -File migrate-any.ps1 `
  -SourcePath "$env:LOCALAPPDATA\JetBrains\IntelliJIdea2024.1" `
  -DstPath "E:\AppData\JetBrains\IntelliJIdea2024.1"

migrate-any.ps1 会自动完成:复制数据 → 删除原目录 → 创建 Junction → 验证。

批量迁移(一次迁移多个目录)

function Invoke-JunctionMigrate {
    param([string]$Src, [string]$Dst)
    if (-not (Test-Path $Src)) { Write-Host "跳过(不存在): $Src" -ForegroundColor Gray; return }
    $item = Get-Item $Src -Force
    if ($item.Attributes -band [IO.FileAttributes]::ReparsePoint) {
        Write-Host "跳过(已是Junction): $Src" -ForegroundColor DarkCyan; return
    }
    New-Item -ItemType Directory -Path $Dst -Force | Out-Null
    $sz = (Get-ChildItem $Src -Recurse -Force -ErrorAction SilentlyContinue | Measure-Object Length -Sum).Sum
    Write-Host ("迁移 $Src ({0:N1} MB)..." -f ($sz/1MB)) -ForegroundColor Yellow
    Copy-Item "$Src\*" $Dst -Recurse -Force -ErrorAction SilentlyContinue
    Remove-Item $Src -Recurse -Force
    cmd /c "mklink /J `"$Src`" `"$Dst`"" 2>&1 | Out-Null
    $ok = (Get-Item $Src -Force).Attributes -band [IO.FileAttributes]::ReparsePoint
    Write-Host ("  $(if($ok){'✓ Junction OK'}else{'✗ FAIL'}): $Src") -ForegroundColor $(if($ok){"Green"}else{"Red"})
}

# 示例:批量迁移 VSCode + Yarn + pip
$dst = "E:\AppData"
Invoke-JunctionMigrate "$env:APPDATA\Code\Cache"            "$dst\VSCode\Cache"
Invoke-JunctionMigrate "$env:APPDATA\Code\CachedData"       "$dst\VSCode\CachedData"
Invoke-JunctionMigrate "$env:APPDATA\Code\GPUCache"         "$dst\VSCode\GPUCache"
Invoke-JunctionMigrate "$env:LOCALAPPDATA\Yarn\Cache\v6"    "$dst\yarn\v6"
Invoke-JunctionMigrate "$env:LOCALAPPDATA\pip\Cache"        "$dst\pip\Cache"

应用专用脚本(进阶,含配置同步)

脚本适用场景核心参数
migrate-vscode.ps1VSCode 完整迁移(缓存+插件+修改启动脚本)-DstBase <目标根目录>
migrate-yarn.ps1Yarn 缓存迁移 + yarn config 配置更新-DstPath <目标路径>
migrate-npm.ps1npm 缓存迁移 + npm config 配置更新-DstPath <目标路径>

Step 2: 应用配置同步

部分应用需要在 Junction 之外额外同步配置,否则可能绕过 Junction 写入新路径:

包管理器类

yarn config set cache-folder "E:\AppData\yarn"
npm config set cache "E:\AppData\npm-cache"
pnpm config set cache-dir "E:\AppData\pnpm\cache"
pnpm config set store-dir "E:\AppData\pnpm\store"
pip config set global.cache-dir "E:\AppData\pip\Cache"

JVM 工具类(环境变量方式)

# 设置用户级别环境变量(持久生效)
[Environment]::SetEnvironmentVariable("GRADLE_USER_HOME", "E:\AppData\gradle", "User")
[Environment]::SetEnvironmentVariable("M2_HOME", "E:\AppData\maven", "User")
# 注意:Maven 通常读 %USERPROFILE%\.m2,Junction 方式更稳定

Go / Rust / Ruby

[Environment]::SetEnvironmentVariable("GOMODCACHE", "E:\AppData\go\pkg\mod", "User")
[Environment]::SetEnvironmentVariable("CARGO_HOME", "E:\AppData\cargo", "User")
[Environment]::SetEnvironmentVariable("GEM_HOME", "E:\AppData\gem", "User")

VSCode/Cursor 插件路径(修改启动参数)

$codeCmd = (Split-Path (Get-Command code -ErrorAction SilentlyContinue).Source) + "\code.cmd"
if (Test-Path $codeCmd) {
    $content = Get-Content $codeCmd -Raw
    if ($content -notmatch "extensions-dir") {
        # 在调用 Code.exe 的行末尾插入参数
        $newContent = $content -replace '(Code\.exe[^\r\n]*%\*)', '$1 --extensions-dir "E:\AppData\VSCode\extensions"'
        [System.IO.File]::WriteAllText($codeCmd, $newContent, [Text.Encoding]::UTF8)
        Write-Host "已更新 code.cmd" -ForegroundColor Green
    }
}

Docker Desktop(迁移后修改镜像存储位置)

打开 Docker Desktop → Settings → Resources → Advanced → Disk image location → 修改为目标盘路径 → Apply & Restart


Step 3: 验证

# 自动检查所有已知应用的 Junction 状态
powershell -File verify.ps1

# 仅验证指定路径
powershell -File verify.ps1 -Paths "$env:LOCALAPPDATA\pip\Cache", "$env:USERPROFILE\.gradle\caches"

# 验证并统计目标目录数据量
powershell -File verify.ps1 -ScanDir "E:\AppData"

Step 4: 清理残留(可选)

Junction 迁移后 C 盘原目录已是链接(无实体数据),通常无需清理。

仅在确认迁移成功后,如需删除 Junction 本身(极少数情况):

# 安全删除 Junction(只删链接,不影响目标盘数据)
function Remove-JunctionOnly([string]$Path) {
    $item = Get-Item $Path -Force -ErrorAction SilentlyContinue
    if ($item -and ($item.Attributes -band [IO.FileAttributes]::ReparsePoint)) {
        Remove-Item $Path -Force  # 只删 Junction,不递归删目标内容
        Write-Host "已删除 Junction: $Path"
    } else {
        Write-Host "非 Junction,跳过(防止误删): $Path" -ForegroundColor Red
    }
}

Scripts 参考

脚本功能关键参数
scan-usage.ps1扫描 AppData 大目录,标记可迁移路径-MinSizeMB -TopN
migrate-any.ps1通用单目录 Junction 迁移(首选)-SourcePath -DstPath
migrate-vscode.ps1VSCode 完整迁移(缓存+插件+启动脚本)-DstBase -ExtDstPath
migrate-yarn.ps1Yarn 缓存迁移 + 配置更新-DstPath
migrate-npm.ps1npm 缓存迁移 + 配置更新-DstPath
verify.ps1验证 Junction 状态(支持任意路径)-Paths -ScanDir

常见问题

Q: Junction 和软链接(symlink)有什么区别?
A: Junction 是目录级 NTFS 重定向,只需普通用户权限(某些系统需管理员),对应用完全透明。symlink 可指向文件,但需要 SeCreateSymbolicLink 权限。对于目录迁移,Junction 是 Windows 上的首选方案。

Q: 迁移后应用启动报错找不到路径?
A: 确认 Junction 已正确创建(Get-Item $path -Force 的 Attributes 包含 ReparsePoint)。若应用写死了绝对路径到配置文件,还需同步修改配置(见 Step 2)。

Q: 迁移时提示"拒绝访问"?
A: 以管理员身份运行 PowerShell,或先关闭占用该目录的进程(如 VSCode、IDEA 等)。

Q: 目标盘格式有什么要求?
A: 必须是 NTFS 格式。FAT32、exFAT 不支持 Junction。