论 Powershell 是如何干掉我的电脑的

· · 科技·工程

总结

省流:

VSCode 背 30% 的锅

PowerShell 背 10% 的锅

oh-my-posh 背 50% 的锅

我背 10% 的锅

开端

几个月前,我在电脑上愉快的 coding。

然后发现卡的要死,遂打开 taskmgr。

一看吓一跳,硬生生有十几个 pwsh.exe 进程。

taskkill /f /im pwsh.exe 搞定。

但显然,这个东西治标不治本,因为我经常把我的 wintogo 插到机房电脑上用,而机房电脑性能好的一匹,于是这个问题被掩盖了。

发展

前两天,外培,被迫使用自己的电脑,因为我好像应该管这里的机房电脑叫爹

于是,这个问题又出现了。

这一次,taskkill 挡不住了。

问 DeepSeek,它让我运行 Get-CimInstance Win32_Process -Filter "Name = 'pwsh.exe'" | Select-Object CommandLine

结果:

CommandLine
-----------
"C:\Program Files\PowerShell\7\pwsh.exe" -noexit -command "try { . \"c:\Program Files\Microsoft VS Code\resources\app\out\vs\workbe…
"C:\Program Files\PowerShell\7\pwsh.exe" -noexit -command "try { . \"c:\Program Files\Microsoft VS Code\resources\app\out\vs\workbe…
"C:\Program Files\PowerShell\7\pwsh.exe" -c "git diff --no-index --no-renames --unified=0 C:\Users\Gary\AppData\Local\Temp\tmp-2176…
"C:\Program Files\PowerShell\7\pwsh.exe" -c "git diff --no-index --no-renames --unified=0 C:\Users\Gary\AppData\Local\Temp\tmp-2176…
"C:\Program Files\PowerShell\7\pwsh.exe" -c "git diff --no-index --no-renames --unified=0 C:\Users\Gary\AppData\Local\Temp\tmp-2176…
"C:\Program Files\PowerShell\7\pwsh.exe" -c "git diff --no-index --no-renames --unified=0 C:\Users\Gary\AppData\Local\Temp\tmp-2176…
"C:\Program Files\PowerShell\7\pwsh.exe" -c "git diff --no-index --no-renames --unified=0 C:\Users\Gary\AppData\Local\Temp\tmp-2176…
"C:\Program Files\PowerShell\7\pwsh.exe" -c "git diff --no-index --no-renames --unified=0 C:\Users\Gary\AppData\Local\Temp\tmp-2176…
"C:\Program Files\PowerShell\7\pwsh.exe" -c "git diff --no-index --no-renames --unified=0 C:\Users\Gary\AppData\Local\Temp\tmp-2176…
(以下省略一堆一样的)

显然可以看出,这个锅主要在于

"C:\Program Files\PowerShell\7\pwsh.exe" -c "git diff --no-index --no-renames --unified=0 C:\Users\Gary\AppData\Local\Temp\tmp-2176…

那这个命令是谁调用的呢?

高潮

VSCode 有 git 集成。

而每次卡的时机都是保存文件的时候。

而我的工作区内正好有一个 git 仓库。

合理怀疑是 git 集成的锅。

实际上是 DeepSeek 这么说的

而这个东西竟然从 pwsh 调用,而 pwsh 的配置文件中带着 oh-my-posh 的初始化,tsk 中也可见 oh-my-posh 进程,所以卡顿主要是 oh-my-posh 初始化造成的。(oh-my-posh 初始化至少占了 800ms)

结局

知道了问题,自然要对症下药。

我想了个邪招:当 pwsh -c|Command 传入的命令包含 git 时,直接跳过绝大部分非必要的初始化。

$rawArgs = [Environment]::GetCommandLineArgs()

# 检测 -c 或 -Command 参数是否存在并截取传入的命令
$commandToRun = $null
for ($i = 0; $i -lt $rawArgs.Count; $i++) {
    if ($rawArgs[$i] -in '-c', '-Command' -and ($i + 1) -lt $rawArgs.Count) {
        $commandToRun = $rawArgs[$i + 1]
        break
    }
}

# 检测 git
if ( -not ($commandToRun -and ($commandToRun -match '^(.*\\|.*/|)git(\.exe)?(\s|$)'))) {
    # 初始化部分
}

Powered By DeepSeek

效果:

# 优化前
$ Measure-Command { pwsh -c "git status" }

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 3
Milliseconds      : 632
Ticks             : 36329216
TotalDays         : 4.20477037037037E-05
TotalHours        : 0.00100914488888889
TotalMinutes      : 0.0605486933333333
TotalSeconds      : 3.6329216
TotalMilliseconds : 3632.9216

# 优化后
$ Measure-Command { git status }          

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 199
Ticks             : 1994483
TotalDays         : 2.30842939814815E-06
TotalHours        : 5.54023055555556E-05
TotalMinutes      : 0.00332413833333333
TotalSeconds      : 0.1994483
TotalMilliseconds : 199.4483

$ Measure-Command { pwsh -c "git status" }

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 1
Milliseconds      : 43
Ticks             : 10439697
TotalDays         : 1.20829826388889E-05
TotalHours        : 0.000289991583333333
TotalMinutes      : 0.017399495
TotalSeconds      : 1.0439697
TotalMilliseconds : 1043.9697

这样下来之后,再也没有过动不动卡半天外加 cpu、内存占用率满的问题了。

不过即使如此,用 pwsh 调用 git 还是会有大约五倍于直接执行的用时,不过至少在可接受范围内了。

教训:最好不要在任何 Shell 的初始化阶段干耗时的事情,尤其是在要求高效率的时候。