有时,PowerShell 在准备好使用之前可能会出现问题。 启动问题可能很难进行故障排除,尤其是在想要使用 PowerShell 提供帮助时。 启动有三个主要阶段:
- 进程创建
- PowerShell SessionState 初始化
- 个人资料处理
最常见的问题包括:
- 启动时间长或性能缓慢
- Errors
- 故障
启动序列的步骤
了解 PowerShell 在启动期间完成的步骤很有帮助。 这样,便可以缩小问题发生位置的范围。
步骤 1:进程创建
创建过程有几个步骤:
创建托管窗口
在 Windows 上,主机可以是 Windows 终端、Windows 控制台主机、Visual Studio Code 或任何其他托管应用程序。 此处出现的问题通常与 PowerShell 无关,但也极其罕见。
启动宿主进程
此处出现的问题通常是由损坏的可执行文件或作系统中的问题引起的。
准备 .NET
PowerShell 基于 .NET,需要完全加载。 根据您尝试启动的 PowerShell 版本,您可以在 Windows PowerShell 5.1 中获得完整的 Windows 集成 .NET Framework,或在 PowerShell 7 中使用包含的较新 .NET。
首次启动 PowerShell 期间,PowerShell 和 .NET 运行优化任务。 此优化任务仅在安装、升级后首次启动或缓存为空时运行一次。 首次优化期间,启动时间会更长。 优化期间失败可能会创建损坏的缓存。 损坏的 PowerShell 缓存可能会导致命令发现和模块加载出现问题。
步骤 2:PowerShell SessionState 初始化
加载 PowerShell 二进制文件并初始化引擎涉及处理 PowerShell 配置和一些缓存的数据。
- 处理配置文件:
powershell.config.json和 JEA 及其他远程处理方案使用的 PSSession 配置文件。 这些文件可能包含可能影响语言模式、可用命令和模块以及某些策略设置的设置。 - 检查组策略和 Windows 安全设置策略。 Windows 组策略可以覆盖
powershell.config.json中的设置。 安全策略可以启用 WDAC(Windows Defender 应用程序控制)等功能,该功能还可以限制可用的语言模式。 - 加载默认模块(Microsoft.PowerShell.Core 和 PSReadLine),以及 PSSession 配置中定义的任何模块和程序集。
有关 PowerShell 安全功能的详细信息,请参阅以下文章:
步骤 3:配置文件处理
最后,PowerShell 运行可用的配置文件。 配置文件脚本按以下顺序运行:
- 所有主机,所有用户
- 当前主机所有用户
- 所有主机当前用户
- 当前主机,当前用户
注释
配置文件脚本不会针对远程会话运行。
有关配置文件的详细信息,请参阅 about_Profiles。
缩小问题的范围
删除变量并缩小问题发生位置的特定范围很有帮助。 最容易消除的变量是个人资料。 配置文件通常包含自定义代码,尤其是在用户专属的个人配置文件脚本中。
尝试以禁用配置文件的状态运行 PowerShell:
# PS 5.1:
powershell -NoProfile
# PS 7.*:
pwsh -NoProfile
接下来,应查看问题是否特定于版本。 尝试在 Windows PowerShell 5.1 和 PowerShell 7 中运行您的用户配置文件。 Windows PowerShell 和 PowerShell 7 将配置文件存储在不同的位置。 对于这两个版本,个人资料可能不同。 比较文件以了解差异。 可以尝试在 Windows PowerShell 5.1 中安装您的 PowerShell 配置文件。 但是,请注意,某些 PowerShell 7 命令和模块与 Windows PowerShell 5.1 不兼容。
可以在 Windows PowerShell 5.1 中测试 PowerShell 7 配置文件,而无需覆盖现有配置文件。
在禁用配置文件的情况下启动 Windows PowerShell 5.1。
手动将 PowerShell 7 配置文件点源加载到 Windows PowerShell 5.1 会话中。
. $env:USERPROFILE\Documents\PowerShell\Microsoft.PowerShell_profile.ps1观察问题是否出现。
如果问题仍然存在,你就知道这是一个配置文件外部的环境问题。
尝试在不同的设备上运行配置文件。 如果配置文件在另一台设备上正常工作,那么你就知道问题出在你的原始设备上。
排查常见环境问题
启动期间崩溃
如果 PowerShell 控制台在启动期间(尤其是早期且没有反馈)崩溃,则可能会有损坏的进程缓存。 这是一种罕见的情况,可以通过清除缓存来解决。 可以清除两个缓存位置:
- 用户缓存:
$env:LOCALAPPDATA\Microsoft\Windows\Caches - 系统缓存:
$env:windir\System32\Config\SystemProfile\AppData\Local\Microsoft\Windows\Caches
首先删除用户缓存文件夹的内容,然后再次尝试启动 PowerShell。 如果问题仍然存在,请删除系统缓存的内容,然后重试。
可能还需要删除 PowerShell 分析缓存。 可以在以下位置找到缓存文件:
- Windows PowerShell:
$env:windir\System32\Config\SystemProfile\AppData\Local\Microsoft\Windows\PowerShell - PowerShell 7:
$env:LOCALAPPDATA\Microsoft\PowerShell
仅删除以下文件模式:
ModuleAnalysisCache-*StartupProfileData-*
下次启动 PowerShell 时,将重新创建缓存的数据。
如果问题仍然存在于 Windows PowerShell 5.1 中,则可能需要修复 .NET Framework 安装。 有关详细信息,请参阅 修复 .NET Framework。
排查常见用户配置问题
本部分介绍 PowerShell 启动期间可能发生的一些常见问题,以及如何对其进行故障排除。
配置文件运行时间过长
首先,必须定义“太长”的概念。 PowerShell 只会执行脚本指示它做的事情。 检查所有配置文件路径。 可能同时正在运行多个配置文件脚本。 查看代码以了解它尝试做什么。
确定发生延迟的位置
如果 AllUsers 范围有配置文件脚本,则可能无法编辑这些文件。 请与系统管理员协作查看这些文件。 对于 CurrentUser 范围配置文件脚本,请编辑这些文件以添加计时消息,以帮助找到发生延迟的位置。 例如,可以在配置文件脚本中的不同点添加以下行。
Write-Host "$(Get-Date -Format 'HH:mm:ss.fff') | Profile: Step X"减少依赖项
减少执行配置文件时需要加载的模块数。 在配置文件运行后运行
Get-Module,查看启动期间加载的模块。 默认情况下,PowerShell 加载 Microsoft.PowerShell.Core 和 PSReadLine 模块。 用户配置文件脚本加载了额外的模块。模块可以通过使用这些
Import-Module模块中定义的命令显式加载或隐式加载。 请考虑在启动过程中是否需要加载命令或模块。避免在重定向文件夹中安装模块
在许多情况下,你的
Documents文件夹可以重定向到网络文件共享或 OneDrive。 网络文件访问可能很慢,尤其是在网络拥堵或服务器负载过大的情况下。 取决于 OneDrive 的配置,它还可能会在配置文件执行期间引入延迟或导致错误。可通过几个选项来缓解此问题:
- 不要重定向您的
Documents文件夹,但这可能不是您希望的。 -
Modules将 OneDrive 中的文件夹配置为始终保留在磁盘上。 这可以防止错误和延迟的加载时间。 - 在 AllUsers 范围(位于用户配置文件文件夹外部)中安装模块。
- 不要重定向您的
度量实际性能
如果不知道配置文件需要多长时间才能运行,则无法确定它是否太长。 该
Measure-Commandcmdlet 可以显示命令或脚本块运行的时间。PowerShell 开发经理史蒂夫·李(Steve Lee)撰写了一篇博客文章,介绍如何衡量您的配置文件的性能。 其中包括有关建立性能基线的说明、如何获取详细的计时信息,以及优化用户配置的途径。 请参阅 优化$Profile。
PowerShell 7 在隔离网络中缓慢启动
在此方案中,Windows 计算机位于未连接到 Internet 的网络上。 对于交互式 PowerShell 会话,PowerShell 会自动加载 PSReadLine 模块。 PSReadLine 是一个已签名的模块。 PowerShell 必须验证模块的数字签名。 此验证可能会导致断开连接的环境中出现延迟。 若要测试此理论,请以 非交互 模式启动 PowerShell 7:
pwsh.exe -noninteractive
如果 PowerShell 在非交互式模式下快速启动,则问题很可能是由证书吊销列表(CRL)检查引起的。 在验证数字签名的过程中,.NET 运行时会检查 CRL 以确保签名证书仍然有效。 在断开连接的环境中,计算机无法访问 Internet 上的 CRL。 CRL 检查的默认超时为 15 秒。 这意味着,每次 PowerShell 加载已签名模块时,最长可能需要 15 秒才能超时。
此问题有三种可能的解决方法:
防火墙或代理豁免
允许对 CRL 检查进行直接的 Internet 访问可以防止问题。 在可以控制对 Internet 访问的访问的环境中,可以将防火墙或代理配置为允许访问 CRL URL。 这是影响最少的最简单解决方案。 防火墙日志应显示 PowerShell 尝试访问的 URL。
减少 CRL 超时
降低 CRL 查找超时时间是可能的,但这样做可能会导致其他查找在指定的时间内无法完成,从而失败。 有关如何更改超时的详细信息,请参阅 “管理网络检索和路径验证”。
删除 CRL 检查
CRL 检查设置由组策略管理。 有关详细信息,请参阅 “管理受信任的发布者”。
警告
可以禁用 CRL 检查,只是不推荐这样做。 禁用 CRL 检查会阻止你实际撤销已泄露的证书。
错误:由于此命令是在不同的编程语言模式下定义的,因此无法对其进行点源操作。
PowerShell 与应用程序控制系统一起工作,例如 AppLocker 和 Windows Defender 应用程序控制(WDAC),通过自动在 ConstrainedLanguage 模式下运行。 ConstrainedLanguage 模式限制某些可能危险的功能。 但是,有时需要 FullLanguage 模式才能使用某些命令或功能。 脚本在策略信任时可以在 FullLanguage 模式下运行。 可以通过在 AppLocker 或 WDAC 中配置的文件签名或其他策略机制来指示信任。
启动在应用程序控制下管理的 PowerShell 会话时,会出现以下错误:
Cannot dot-source this command because it was defined in a different language mode. To invoke this
command without importing its contents, omit the '.' operator.
在应用程序控制下,PowerShell 在 ConstrainedLanguage 模式下运行。 当您的配置文件脚本被不受限制或已签名以在 FullLanguage 模式下运行时,会发生此错误。 在 ConstrainedLanguage 模式下运行 PowerShell 时,无法在 FullLanguage 模式下运行受信任的点源代码。
若要解决此问题,必须从配置文件脚本中删除豁免或签名。 如果您需要在配置文件中运行必须在 FullLanguage 模式下运行的代码,请将其移动到被豁免或签名的另一个脚本文件中。 在你的配置文件中调用脚本文件(不要使用点源方式调用)。
有关此问题的详细信息,请参阅 PowerShell 约束语言模式和 Dot-Source 运算符。