为什么仅在 ISE 中第一次使用 Get-Variable 访问参数变量的属性?

Why does accessing a parameter variable's attributes with Get-Variable only work the first time in the ISE?

感谢 Whosebug 的优秀人员,我们收到了关于如何在脚本或函数的 Param() 子句中检索 ValidateSet 中定义的值的非常好的

Param (
    [ValidateSet('Startup', 'Shutdown', 'LogOn', 'LogOff')]
    [String]$Type = 'Startup'
)

(Get-Variable Type).Attributes.ValidValues

唯一让我困扰的是,当您在 PowerShell ISE 中 运行 时,此代码仅在 第一次 时有效。 第二次你运行它,没有输出产生。

是否有解决方法让它始终有效?我们在 Win 7 和 Win 2012 上使用 PowerShell 4.0。

首先,此行为仅在 PowerShell ISE 中出现(在外部完美运行)。这可以用 following post.

来解释

阅读它,您会发现有一个解决方法:

Param (
    [ValidateSet('Startup', 'Shutdown', 'LogOn', 'LogOff')]
    [String] $Type = 'Startup'
)

(Get-Variable Type).Attributes.ValidValues

# Do your stuff here

Remove-Variable Type

tl;dr

  • 从 Windows PowerShell v5.1 / PowerShell 开始,观察到的行为可以说是一个 bug核心 v6.0-beta.4 - 请参阅 this GitHub issue.

  • 无副作用地绕过问题,请使用以下方法:

Param (
    [ValidateSet('Startup', 'Shutdown', 'LogOn', 'LogOff')]
    [String] $Type = 'Startup'
)

$MyInvocation.MyCommand.Parameters['Type'].Attributes.ValidValues

如果 Set-StrictMode -version 2 或更高版本有可能生效或者您正在使用 PSv2,请使用

Param (
    [ValidateSet('Startup', 'Shutdown', 'LogOn', 'LogOff')]
    [String] $Type = 'Startup'
)

($MyInvocation.MyCommand.Parameters['Type'].Attributes |
  Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).ValidValues

可选的背景信息

问题与 ISE 本身无关,而是 repeated dot-sourcing(ISE 恰好通过点源 运行 所有脚本)。

Dot-sourcing 运行s 脚本在 current 范围内(在调用者的范围内,而不是在子范围内)因此通常会修改当前范围的状态,例如通过添加变量。
如果您从交互式会话中点源脚本,您实际上是在修改全局会话状态,例如,这就是 PS 配置文件中定义的加载方式。

在手头的案例中,脚本的点源调用有效地将 参数 变量 $Type 作为 常规可变,按设计。

当您再次点源相同的脚本 时,错误会出现(假设问题中的脚本显示为 ./script.ps1:

  • 在第一次点源调用之后,变量 $Type 的属性仍然完好无损:

    > . ./script.ps1; (Get-Variable Type).Attributes.Count
    Startup
    Shutdown
    LogOn
    LogOff
    3   # PS implicitly adds 2 add'l attributes behind the scenes
    
  • 当你点源再次时,属性丢失:

    > . ./script.ps1; (Get-Variable Type).Attributes.Count
    0