使用最新的严格模式找不到 Powershell 对象

Powershell object cannot be found with strictmode latest

我正在尝试在 set-strictmode -version latest 下工作,它在没有严格模式的情况下工作得很好,不幸的是,我的环境需要最新的严格模式。

它的作用:通过注册表找到带有 EEELinkAdvertisement 的条目并将其分配给 $findEeeLinkAd

$findEeeLinkAd = Get-ChildItem -LiteralPath 'hklm:\SYSTEM\ControlSet001\Control\Class' -Recurse -ErrorAction SilentlyContinue | `
   % {Get-ItemProperty -Path $_.pspath -ErrorAction SilentlyContinue | `
  ? {$_.EeeLinkAdvertisement} -ErrorAction SilentlyContinue }

尽管在管理员中 运行,我还是收到了一堆以下错误:

The property 'EEELinkAdvertisement' cannot be found on this object. Verify that the property exists.
At line:3 char:12
+         ? {$_.EEELinkAdvertisement} }
+            ~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], PropertyNotFoundException
    + FullyQualifiedErrorId : PropertyNotFoundStrict

如有任何帮助,我们将不胜感激!

EeeLinkAdvertisement 是未定义的 属性 对于您抓取的许多项目,所以这是正常的。 你可以这样作弊:

Get-ChildItem -LiteralPath 'hklm:\SYSTEM\ControlSet001\Control\Class' -Recurse -ErrorAction SilentlyContinue | `
   % {Get-ItemProperty -Path $_.pspath -ErrorAction SilentlyContinue | `
  ? {try{$_.EeeLinkAdvertisement}catch{}} -ErrorAction SilentlyContinue }

:

  • 除非您准备好在每次升级到新的 PowerShell 版本时彻底测试每个现有的脚本/函数/写入 PowerShell 模块,我建议避免
    Set-StrictMode -Version Latest 在生产代码中
    - 在开发期间没问题,但是在发布代码时,应该明确指定当时的最高版本,从而锁定中。如果您不这样做,当在更高版本的 PowerShell 中引入新的严格性检查时,现有代码可能会中断。

  • 底部部分展示了如何通常处理在严格模式下访问不存在的属性的尝试。

您可以按如下方式简化您的代码,这隐含地绕过了试图访问不存在于所有输入对象上的属性的问题:

$rootKey = 'hklm:\SYSTEM\ControlSet001\Control\Class'

# Find all keys that have a 'EeeLinkAdvertisement' value.
$findEeeLinkAd = 
  Get-ChildItem -LiteralPath $rootKey -Recurse -ErrorAction Ignore | 
    ForEach-Object { if ($null -ne $_.GetValue('EeeLinkAdvertisement')) { $_ } }

注意从 -ErrorAction SilentlyContinue-ErrorAction Ignore 的切换:后者悄悄地 丢弃 任何错误,而前者 不显示它们,但仍将它们记录在自动$Error集合中。

这利用了 Microsoft.Win32.RegistryKey.GetValue() 方法安静地 忽略 尝试检索不存在的值的数据并且 returns $null .

顺便说一句:应用 common -ErrorAction parameter to the Where-Object (?) and ForEach-Object (%) cmdlet 实际上毫无意义,因为参数是 而不是 应用于代码在传递给这些命令的脚本块 ({ ... }) 中运行。


在严格模式下访问不存在的属性时避免错误:

Set-StrictMode-Version 2 或更高(包括 Latest)导致尝试访问对象上不存在的 属性 以报告 语句-终止 错误。

  • 请注意,这意味着默认情况下只有手头的语句被终止,而整个脚本执行继续.

  • 还要注意,-Off是默认值;也就是说,默认执行 no 严格性检查,这意味着即使尝试引用不存在的 variables 也会 not 默认触发错误; Set-StrictMode -Version 1 检查不存在的 变量 ,但不检查不存在的 属性 .

有几种方法可以避免此类错误:

  • 使用一个Try / Catch statement around the property access, as also shown in ;这也使得在没有 属性 的情况下指定 默认值 变得容易,但是捕获异常是 slow 相比以下基于反射的方法:

    $o = [pscustomobject] @{ foo = 1 }
    
    $propValue = try { $o.NoSuchProperty } catch { 'default value' }
    
  • 暂时子范围中禁用严格模式(这与 try / catch 方法一样慢):

    $o = [pscustomobject] @{ foo = 1 }
    
    # $propValue will effectively receive $null.
    # Strictly speaking: [System.Management.Automation.Internal.AutomationNull]::Value
    $propValue = & { Set-StrictMode -Off; $o.NoSuchProperty }
    
  • 使用反射来测试属性的存在,通过隐藏.psobject.Properties 成员在所有对象上可用;这是最快的方法:

    $o = [pscustomobject] @{ foo = 1 }
    
    $propValue = if ($o.psobject.Properties['NoSuchProperty']) { $o.NoSuchProperty }
    

PowerShell [Core] 7.1+ 中,您可以更简洁地通过 null-conditional operator ?.:

$o = [pscustomobject] @{ foo = 1 }

# Note the `?.`, which only tries to access the property if the expression
# to the left isn't $null.
$propValue = $o.psobject.Properties['NoSuchProperty']?.Value
  • v7.1 的陷阱:如果您将 ?. 直接应用于 变量 ,您必须意外地附上它{...} 中的名称,例如${var}?.Property$var?.Property 没有 按预期工作,因为 PowerShell 然后假定变量名称是 var? - 请参阅 GitHub issue #11379 以尝试更改它。

类似地,null-coalescing operator, ??(它本身在 v7.0 中可用)可以简化提供 default 值(在早期版本中,您可以通过添加else 分支到上面的 if 语句):

$o = [pscustomobject] @{ foo = 1 }

# The RHS of `??` is used if the LHS evaluates to $null.
$propValue = $o.psobject.Properties['NoSuchProperty']?.Value ?? 'default value'