Chrome 不恢复上次标签页:两个常被忽略的原因和一份可用的关闭脚本
现象
场景:一台 Windows 机器上跑一个独立的 Chrome for Testing 实例,专门给自动化用,profile 放在 C:\profiles\work,与自己日常用的浏览器完全隔离。想法很简单,任务做完就把浏览器关掉释放内存,下次需要时再启动,最好能接着上次打开的页面继续。
于是加了启动参数 --restore-last-session,又在 profile 里把 session.restore_on_startup 设成 4(继续浏览上次打开的网页)。重启一看,只有一个新标签页,之前开的页面一个都没回来。
没有报错,没有提示,设置看起来完全"没生效"。
原因一:这个值 Chrome 启动后会自己清掉
把浏览器完全退出后,直接去看 profile 里的 Preferences 文件,检索 restore_on_startup:
grep -o '"restore_on_startup":[0-9]*' "C:/profiles/work/Default/Preferences"
没有任何输出。也就是说这个键不是"设了但值不对",而是启动一次之后整条都没了。Chrome 读到它、按其恢复会话,然后把这条配置抹掉,避免下次再恢复同一批页面。
结论很直接:restore_on_startup 不能在第一次配置时写一次了事,必须在每次关闭时重新写回。而且要在进程完全退出之后写,否则运行中的实例在退出时会把内存里的配置整个覆盖回文件。
原因二:exit_type=Crashed 会让 Chrome 跳过恢复
第二个坑更隐蔽。profile 里有个 profile.exit_type 字段,正常退出是 Normal,进程被直接杀掉是 Crashed。Chrome 看到 Crashed 会认为"上次可能是崩溃退出",于是即使配置写着恢复也不执行——它不希望用户一启动就被一堆可能有害的页面环绕。
自动化场景里进程经常是被主动杀掉的,所以这个字段几乎总是 Crashed。查一下就知道:
grep -o '"exit_type":"[A-Za-z]*"' "C:/profiles/work/Default/Preferences"
两个原因叠在一起:配置被清掉,状态又是"崩溃退出",恢复自然一次都不会发生。
修复:关闭时顺手改回来
把两件事都放进关闭脚本:先让浏览器优雅退出(Windows 上向窗口发 WM_CLOSE),给 8 秒超时,还不退再强杀;确认进程退出之后,再改 Preferences。
$ErrorActionPreference = 'SilentlyContinue'
$prefs = "C:\profiles\work\Default\Preferences"
# 1. 优雅关闭,超时强杀(略,见后文进程匹配部分)
# 2. 进程退出后修正配置
$enc = New-Object System.Text.UTF8Encoding($false)
$t = [System.IO.File]::ReadAllText($prefs, [System.Text.Encoding]::UTF8)
$t = $t -replace '"exit_type":"[A-Za-z]*"', '"exit_type":"Normal"'
$t = $t -replace '"exited_cleanly":(true|false)', '"exited_cleanly":true'
if ($t -match '"restore_on_startup":\d+') {
$t = $t -replace '"restore_on_startup":\d+', '"restore_on_startup":4'
} elseif ($t -match '"session":\{') {
$t = $t -replace '"session":\{', '"session":{"restore_on_startup":4,'
} else {
$t = $t -replace '^\{', '{"session":{"restore_on_startup":4},'
}
[System.IO.File]::WriteAllText($prefs, $t, $enc)
几个细节值得说明:
- 写文件用 UTF-8 无 BOM。带 BOM 的 JSON 交给 Chrome 读有解析风险,一旦它判定配置损坏就重建,等于白改。
session键可能整个不存在,所以要兼容三种情况:键在、session 在但键不在、两层都不在。直接替换字符串在这三种情况下行为不同。- 必须等进程真的退出再写。Chrome 退出时会回写整个 Preferences,早写的会被覆盖掉,看起来就是"改了没效果"。
- 用
-replace做文本替换而不是 parse 成对象再序列化。Preferences 是个几万行的单行 JSON,Chrome 对键顺序和格式不敏感,但序列化会丢掉别的字段,风险更大。
改完实测:关之前开着 4 个标签页,重启后 4 个全部回来。冷启动到自动化能接管大约 2 秒,标签页恢复到渲染就绪再等 7 到 8 秒。
顺带一个多实例的坑:别用 taskkill /IM
如果机器上同时跑多个实例、每个实例有独立 profile,关闭某个实例时最容易写错的命令是 taskkill /IM chrome.exe。那会把所有 Chrome 一起关掉,包括正在用的那个日常浏览器。
正确做法是按命令行里的 profile 路径精确匹配进程:
$esc = [regex]::Escape("C:\profiles\work")
Get-CimInstance Win32_Process -Filter "Name='chrome.exe'" |
Where-Object { $_.CommandLine -match $esc } |
ForEach-Object { Stop-Process -Id $_.ProcessId -Force }
两个容易踩的点:
- 必须用
[regex]::Escape。路径里的反斜杠在正则里是转义符,不转义的话C:\profiles\work匹配不到任何东西,脚本会安静地什么也不做,看起来像"杀不掉"。 - 匹配要落到完整路径而不是目录名。
work这样的名字会命中workspace,关一个实例顺手把另一个也关了。
清单
- 每次关闭后重写
session.restore_on_startup = 4,不能只写一次 - 把
profile.exit_type改回Normal,否则 Chrome 跳过恢复 - 写 Preferences 必须在进程退出之后,UTF-8 无 BOM
- 多实例按 profile 路径匹配进程,不要按进程名
这两处都算不上文档里显眼的东西,但它们决定了"配置写了为什么不生效"。排查顺序也清楚了:先看配置文件里键还在不在,再看 exit_type 是什么值,两个都正常再怀疑别的地方。
- 点赞
- 收藏
- 关注作者
评论(0)