修复:PowerShell 控制台和脚本启动缓慢

Jacki

我注意到 PowerShell 控制台有时需要很长时间才能打开。此问题出现在不同的计算机上。在某些情况下,PowerShell 可能需要几分钟才能加载。这会影响命令 shell 本身(powershell.exe 或 pwsh.exe)的打开以及通过 GPO 或计划任务中的脚本启动的登录 PowerShell 脚本的执行时间。这篇文章解释了如何确定 PowerShell 启动缓慢的潜在原因并减少加载时间。

以下是 PowerShell 加载时间较长的一些常见原因:

  • 每次 PowerShell.exe 进程启动时都会加载 PowerShell 配置文件中的用户定义函数
  • 自动安装并加载大量 PS 模块
  • PSReadLine 模块加载一个包含 PowerShell 命令历史记录的巨大文件
  • PowerShell 和模块加载的显着减慢是由 .NET Framework 组件中的冲突或损坏引起的。

测量命令cmdlet 可用于测量 PowerShell 进程的加载时间。使用此命令检查 PowerShell 在正常模式下的加载时间:

powershell -noprofile -ExecutionPolicy Bypass ( Measure-Command { powershell "Write-Host 1" } ).TotalSeconds

建议阅读:

该命令将显示 PowerShell 进程加载和执行一个简单命令所花费的时间。

当 PowerShell 启动时,它可能会报告加载所需的时间。

Loading personal and system profiles took XXX ms

正如您在此示例中看到的,PowerShell 进程的加载时间为 12 秒。这是相当多的。如果 PowerShell 配置文件的加载时间过长,则会自动显示此消息超过500毫秒

这表明 PowerShell 配置文件在每次进程启动时都执行缓慢的代码。

比较未加载配置文件时 PowerShell 进程的启动速度。为此,请使用以下命令运行 shell 进程-无配置文件选项:

powershell.exe -noprofile

或运行 PowerShell Core:

pwsh.exe -noProfile

正如您所看到的,当配置文件未加载时,PowerShell 的启动速度要快得多。 PowerShell 配置文件是用于配置用户环境的 PS1 文件。它们还允许用户运行特定命令并加载自定义功能或模块。

默认情况下,不使用 PowerShell 配置文件(不创建配置文件)。以下命令显示 PowerShell 进程启动时将加载的所有配置文件的列表。

$profile | select *

手动检查用户所有配置文件的内容,或使用以下命令显示它们:
Get-Content $PROFILE.AllUsersAllHosts
Get-Content $PROFILE.AllUsersCurrentHost
Get-Content $PROFILE.CurrentUserAllHosts
Get-Content $PROFILE.CurrentUserCurrentHost

就我而言,一些命令被添加到Microsoft.PowerShell_profile.ps1用户文件。检查配置文件以确保所有命令和功能都是必需的。如果可能,删除不必要的命令并优化代码。

安装大量模块可能是 PowerShell 启动缓慢的另一个可能原因。 PowerShell 自动从以下文件夹加载模块:

C:Users%username%DocumentsWindowsPowerShellModules
C:Program FilesWindowsPowerShellModules
C:Windowssystem32WindowsPowerShellv1.0Modules

他们的列表可以显示如下:

$Env:PSModulePath -split ';'

列出已加载到会话中的 PS 模块。

Get-Module -ListAvailable

列出已安装的第三方模块。

Get-InstalledModule

检查列表以确定您是否需要所有模块。卸载任何未使用的 PowerShell 模块。:
Remove-Module -Name BurntToast
Uninstall-Module -Name BurntToast -AllVersions -Force

手动安装的 PowerShell 模块必须手动卸载。

要分析每个模块的加载时间,请使用以下命令:

Measure-Command { Import-Module ModuleName -Force }

或者批量检查所有PS模块的加载时间:
$modules = Get-InstalledModule| Select-Object -ExpandProperty Name -Unique
foreach ($mod in $modules) {
$time = Measure-Command { Import-Module $mod -Force }
[PSCustomObject]@{
Module = $mod
LoadTimeSec = $time.Totalseconds
}
}

查看哪些模块加载时间最长。

要防止所有 PS 模块自动加载,请将以下行添加到配置文件中:

$PSModuleAutoLoadingPreference="None"

在这种情况下,您可以使用以下命令手动加载所需的模块:

Import-Module [ModuleName]

请务必将以下常用模块添加到您的配置文件中:

Import-Module Microsoft.PowerShell.Utility
Import-Module Microsoft.PowerShell.Management

加载 VMware 基础设施管理 PowerShell 模块时还存在一个已知问题,也称为VMware PowerCLI。在未连接到 Internet 的计算机上加载该模块可能需要几分钟的时间。问题是,加载时,模块会尝试检查已撤销证书的外部列表(CRL、证书吊销列表)。由于缺乏互联网连接,此过程超时。解决办法是在 Windows 中禁用 CRL 检查。您可以通过注册表禁用 CRL 检查:

reg add HKLMSYSTEMCurrentControlSetServicesSstpSvcParameters /v NoCertRevocationCheck /t REG_DWORD /d 0x00000001 /f

或者在 Internet 属性小程序中:inetcpl.cpl->先进的–> 取消勾选该选项检查发布者(服务器)证书是否吊销

PowerShell 可能需要很长时间才能启动的原因通常是计算机上安装的防病毒软件。您可以检查 PowerShell 进程启动时加载了哪些外部 DLL 和模块。

  1. 打开cmd.exe并运行命令:powershell.exe -c "Write-Host $PID;Start-Sleep -s 60"
  2. 此命令返回正在运行的 PowerShell.exe 进程的进程 ID (PID)。复制PID并将其粘贴到新 PowerShell 会话中的以下命令中:
    Get-Process | where {$_.Id -eq <YOUR_PID>} | select -ExpandProperty modules
  3. PowerShell 进程启动时,您将收到加载的 DLL 列表。检查您的防病毒库是否已列出。如果是这样,请尝试将 pwsh.exe 和 powershell.exe 进程添加到防病毒例外中。

例如,您可以使用以下命令将排除项添加到内置 Windows Defender 防病毒软件:

Add-MpPreference -ExclusionProcess "C:WindowsSystem32WindowsPowerShellv1.0powershell.exe","C:Program FilesPowerShell7pwsh.exe"

如果您怀疑 PowerShell 启动缓慢是由 .NET Framework 库操作缓慢引起的,您可以使用以下命令将所有使用的程序集编译为本机机器代码:ngen.exe工具(本机图像生成器)。这将显着提高 .NET 应用程序的性能,包括 PowerShell:

$env:PATH = [Runtime.InteropServices.RuntimeEnvironment]::GetRuntimeDirectory()
[AppDomain]::CurrentDomain.GetAssemblies() | ForEach-Object {
$path = $_.Location
if ($path) {
$name = Split-Path $path -Leaf
Write-Host -ForegroundColor Yellow "`r`nRunning ngen.exe on '$name'"
ngen.exe install $path /nologo
}
}

经典之作过程监控器工具还可以用于分析PowerShell启动时间。 (https://technet.microsoft.com/en-us/sysinternals/processmonitor.aspx)。跑步ProcMon64.exe,对 PowerShell.exe 进程启用过滤器,打开 PS shell,并识别导致最大延迟的操作。

在我的示例中,读取 PSReadline 模块维护的 PowerShell 命令历史记录文件大约需要 30 秒。 (C:UsersusernameAppDataRoamingMicrosoftWindowsPowerShellPSReadLineConsoleHost_history.txt)。该问题是由ConsoleHost_history.txt文件大小超过 2 GB。清理历史文件可以显着减少 PowerShell 的启动时间。