当前位置:网站首页 > 更多 > 玩电脑 > 正文

[玩转系统] PowerShell ValidateSet:从列表中选择

作者:精品下载站 日期:2024-12-14 20:52:19 浏览:13 分类:玩电脑

PowerShell ValidateSet:从列表中选择


当您编写 PowerShell 脚本或函数时,您通常希望通过参数接受用户输入。如果您不限制这些参数接受的值,则可以保证会出现提供不适当值的情况。在本文中,了解如何使用 PowerShell ValidateSet 参数验证属性将这些值限制为仅您定义的值。

编写 PowerShell 脚本或函数时,您可以使用许多不同的验证属性来检查提供给参数的值是否可接受,如果不可接受,则提醒用户。

本文重点介绍 ValidateSet 验证属性。您将了解 ValidateSet 的作用、为什么要在代码中使用 ValidateSet 以及如何操作。您还将了解 ValidateSet 启用的制表符完成功能,该功能将帮助您的代码用户提供有效的参数值。

PowerShell ValidateSet:简要概述

ValidateSet 是一个参数属性,允许您定义一组仅接受作为该参数值的元素。

例如,也许您有一个定义为与 Active Directory 域控制器一起使用的脚本。该脚本有一个参数指定域控制器的名称。将可接受值列表限制为域控制器的实际名称是否有意义?当您事先知道脚本需要什么值时,用户没有理由能够使用“foobar”作为值。 ValidateSet 为您提供了这种功能。

要求

本文将是一个学习演练。如果您打算跟随,您将需要以下内容:

  • Visual Studio Code 或任何其他代码编辑器。我将使用 Visual Studio Code。
  • 本文中的大部分代码至少使用 PowerShell 5.1。有一个部分需要 PowerShell 6.1 或更高版本,当我们讨论它时我会指出这一点

本文所有代码均已在以下环境中测试:

Windows 7 SP1

5.1、核心6.2

Windows 10 1903

5.1、核心6.2

Linux Mint 19.2

核心6.2

为了帮助解释有关 ValidateSet 的概念,您将构建一个名为 Get-PlanetSize.ps1 的小脚本。该脚本返回有关太阳系中行星大小的信息。

您将从一个简单的脚本开始,逐步提高其处理最终用户输入的能力,并使他们能够轻松发现其可能的参数值。

入门

首先,首先将下面的 PowerShell 代码复制并粘贴到您喜欢的文本编辑器中,并将其另存为 Get-PlanetSize.ps1

$planets = [ordered]@{
     'Mercury' = 4879
     'Venus'   = 12104
     'Earth'   = 12756
     'Mars'    = 6805
     'Jupiter' = 142984
     'Saturn'  = 120536
     'Uranus'  = 51118
     'Neptune' = 49528
     'Pluto'   = 2306
 }
 $planets.keys | Foreach-Object {
     $output = "The diameter of planet {0} is {1} km" -f $_, $planets[$_]
     Write-Output $output
 }

从 PowerShell 提示符运行该脚本,您应该得到以下信息:

PS51> .\Get-PlanetSize.ps1
 The diameter of planet Mercury is 4879 km
 The diameter of planet Venus is 12104 km
 The diameter of planet Earth is 12756 km
 The diameter of planet Mars is 6805 km
 The diameter of planet Jupiter is 142984 km
 The diameter of planet Saturn is 120536 km
 The diameter of planet Uranus is 51118 km
 The diameter of planet Neptune is 49528 km
 The diameter of planet Pluto is 2306 km

信息丰富但不太灵活;即使您只想要火星的信息,也会返回每个行星的信息。

也许您希望能够指定单个行星而不是返回所有行星。您可以通过引入一个参数来做到这一点。接下来让我们看看如何实现这一目标。

使用参数接受输入

要允许脚本接受参数,请将 Param() 块添加到脚本顶部。调用参数Planet。合适的 Param() 块如下所示。

在下面的示例中,行[Parameter(Mandatory)] 确保行星名称始终提供给脚本。如果缺少,则脚本将提示输入一个。

Param(
     [Parameter(Mandatory)]
     $Planet
 )

将此 Planet 参数合并到脚本中的最简单方法是更改行 $planets.keys | Foreach-Object {$Planet | Foreach-对象{.现在,您不再依赖于之前静态定义的哈希表,而是读取 Planet 参数的值。

现在,如果您运行脚本并使用 Planet 参数指定行星,您将只能看到有关该特定行星的信息。

PS51> .\Get-PlanetSize.ps1 -Planet Mars
 The diameter of planet Mars is 6805 km

出色的。剧本完成了吗?也许不会。

选项太开放

如果您尝试使用 Get-PlanetSize.ps1 查找行星 Barsoom 的直径,会发生什么?

PS51> .\Get-PlanetSize.ps1 -Planet Barsoom
The diameter of planet Barsoom is  km

嗯,这是不对的。 Barsoom 不在行星列表中,但脚本仍然运行。我们该如何解决这个问题?

这里的问题是脚本接受任何输入并使用它,无论它是否是有效值。该脚本需要一种方法来限制 Planet 参数接受哪些值。输入ValidateSet

确保仅使用某些值

ValidateSet 列表是以逗号分隔的字符串值列表,用单引号或双引号括起来。向脚本或函数参数添加 ValidateSet 属性包括向 Param() 块添加一行文本,如下所示。将 Get-PlanetSize.ps1 副本中的 Param() 块替换为下面的块并保存文件。

Param(
     [Parameter(Mandatory)]
     [ValidateSet("Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune","Pluto")]
     $Planet
 )

尝试使用 Barsoom 作为 Planet 参数再次运行脚本。现在返回了一条有用的错误消息。该消息具体说明了问题所在,甚至提供了参数的可能值列表。

PS51> .\Get-PlanetSize.ps1 -Planet Barsoom
 Get-PlanetSize.ps1 : Cannot validate argument on parameter 'Planet'. The argument "Barsoom" does not belong to the set
 "Mercury,Venus,Earth,Mars,Jupiter,Saturn,Uranus,Neptune,Pluto" specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command again.
 At line:1 char:32
 .\Get-PlanetSize.ps1 -Planet Barsoom
 ~~~
 CategoryInfo          : InvalidData: (:) [Get-PlanetSize.ps1], ParameterBindingValidationException
 FullyQualifiedErrorId : ParameterArgumentValidationError,Get-PlanetSize.ps1 

使 PowerShell ValidateSet 区分大小写

默认情况下,ValidateSet 属性不区分大小写。这意味着它将允许任何位于允许列表中且具有任何大小写方案的字符串。例如,上面的示例将像接受 mars 一样轻松地接受 Mars。如有必要,您可以使用 IgnoreCase 选项强制 ValidateSet 区分大小写。

ValidateSet 验证属性中的 IgnoreCase 选项确定提供给参数的值是否与有效值列表完全匹配。默认情况下,IgnoreCase 设置为 $True(忽略大小写)。如果将此设置为 $False,则提供 mars 作为 Get-PlanetSize.ps1Planet 参数的值> 将生成错误消息。

您可以通过在有效值列表末尾为其分配一个 $true 值来使用 IgnoreCase 选项,如下所示。

[ValidateSet("Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune", "Pluto", IgnoreCase = $false)]  

现在,当您尝试使用与列表中的值不完全一样的 Planet 值时,验证将会失败。

使用制表符补全

使用 ValidateSet 的另一个好处是它可以为您提供制表符补全功能。这意味着您可以使用 TAB 键循环浏览参数的可能值。这极大地提高了脚本或函数的可用性,尤其是在控制台中。

在下面的示例中,有几点需要注意:

  • 显示最后一个值后,制表符完成会循环回到第一个值。
  • 这些值按字母顺序显示,即使它们未按字母顺序列在 ValidateSet 中。
  • 键入首字母并按 TAB 键会将制表符补全提供的值限制为以该字母开头的值。

您还可以利用 PowerShell 集成脚本环境 (ISE) 中的 ValidateSet 选项卡完成功能,如下面的示例所示。 ISE Intellisense 功能会在一个漂亮的选择框中向您显示可能值的列表。

Intellisense 返回包含您键入的字母的所有值,而不仅仅是以该字母开头的值。

现在我们已经介绍了 Windows 5.1 中的 ValidateSet 验证属性,让我们看一下 PowerShell Core 6.1 中添加的内容,看看是否可以为我们的脚本提供更多验证功能。

了解 PowerShell 6.1 中 ValidateSet 的更改

随着 PowerShell Core 6.1 的到来,ValidateSet 验证属性中添加了两项新功能:

  • ErrorMessage 属性
  • 通过访问 System.Management.Automation.IValidateSetValuesGenerator 来使用 ValidateSet 中的类

ErrorMessage 属性

当您向 Get-PlanetSize.ps1 提供不正确的行星名称时生成的默认错误消息很有帮助,但有点冗长:

PS61> .\Get-PlanetSize.ps1 -Planet Barsoom
 Get-PlanetSize.ps1 : Cannot validate argument on parameter 'Planet'. The argument "Barsoom" does not belong to the set
 "Mercury,Venus,Earth,Mars,Jupiter,Saturn,Uranus,Neptune,Pluto" specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command again.
 At line:1 char:32
 .\Get-PlanetSize.ps1 -Planet Barsoom
 ~~~
 CategoryInfo          : InvalidData: (:) [Get-PlanetSize.ps1], ParameterBindingValidationException
 FullyQualifiedErrorId : ParameterArgumentValidationError,Get-PlanetSize.ps1 

使用 ValidateSet 验证属性的 ErrorMessage 属性可以设置不同的错误消息,如下面的示例所示。 {0} 自动替换为提交的值,{1} 自动替换为允许值的列表。

Param(
     [Parameter(Mandatory)]
     [ValidateSet("Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune","Pluto",ErrorMessage="Value '{0}' is invalid. Try one of: '{1}'")]
     $Planet
 )

替换脚本文件中的 Param() 块并保存。然后再次尝试 Get-PlanetSize.ps1 -Planet Barsoom。请注意,下面的错误内容要少得多,并且更具描述性。

PS61> .\Get-PlanetSize.ps1 -Planet Barsoom
 Get-PlanetSize.ps1 : Cannot validate argument on parameter 'Planet'. Value 'Barsoom' is invalid. Try one of: 'Mercury,Venus,Earth,Mars,Jupiter,Saturn,Uranus,Neptune,Pluto'
 At line:1 char:32
 .\Get-PlanetSize.ps1 -Planet Barsoom
 ~~~
 CategoryInfo          : InvalidData: (:) [Get-PlanetSize.ps1], ParameterBindingValidationException
 FullyQualifiedErrorId : ParameterArgumentValidationError,Get-PlanetSize.ps1 

接下来,了解一种通过 PowerShell 类定义 ValidateSet 中可接受值的新方法。

PowerShell 类

自定义类型(在 PowerShell 中称为类)自版本 5 起就已可用。随着 PowerShell Core 6.1 的到来,出现了一项新功能,允许使用类来提供 ValidateSet 的值。

使用类可以解决 ValidateSet 的主要限制——它是静态的。也就是说,它作为函数或脚本的一部分嵌入,并且只能通过编辑脚本本身来更改。

与 ValidateSet 配合使用的新功能是能够使用 System.Management.Automation.IValidateSetValuesGenerator 类。我们可以使用继承来将其用作我们自己的类的基础。要使用 ValidateSet,该类必须基于 System.Management.Automation.IValidateSetValuesGenerator,并且必须实现名为 GetValidValues() 的方法。

GetValues() 方法返回您希望接受的值的列表。将静态行星列表替换为 [Planet] 类的 Param() 块如下所示。这个例子还不能运行。继续阅读以了解如何实现这一点。

Param(
     [Parameter(Mandator)]
     [ValidateSet([Planet],ErrorMessage="Value '{0}' is invalid. Try one of: {1}")]
     $Planet
 )

使用类作为 ValidateSet 值列表:一个真实示例

为了演示如何使用 ValidateSet 值列表的类,您将用从 CSV 文本文件加载的列表替换之前使用的静态行星列表。您不再需要在脚本本身内部维护静态值列表!

为类创建数据源

首先,您需要创建一个包含每个有效值的 CSV 文件。为此,请将这组数据复制并粘贴到新的文本文件中,并将其另存为 planets.csvGet-PlanetSize.ps1 脚本位于同一文件夹中。

Planet,Diameter
 "Mercury","4879"
 "Venus","12104"
 "Earth","12756"
 "Mars","6805"
 "Jupiter","142984"
 "Saturn","120536"
 "Uranus","51118"
 "Neptune","49528"
 "Pluto","2306"

将类添加到脚本中

ValidateSet 使用的任何类必须在 ValidateSet 尝试使用它之前已经定义。这意味着 Get-PlanetSize.ps1 的结构将无法正常工作。

[Planet] 类必须在使用之前定义,因此它必须位于脚本的开头。合适的骨架类定义如下所示:

class Planet : System.Management.Automation.IValidateSetValuesGenerator {
     [String[]] GetValidValues() {
 }
 }

在类的 GetValidValues() 方法中,使用 Import-CSV cmdlet 导入之前创建的文本文件 planets.csv。该文件被导入到一个名为 $planets 的全局变量中,以便稍后在脚本中访问它。

使用 return 语句通过 GetValidValues() 返回行星名称列表。经过这些更改后,类现在应如下所示。

class Planet : System.Management.Automation.IValidateSetValuesGenerator {
     [String[]] GetValidValues() {
             $Global:planets = Import-CSV -Path planets.csv
             return ($Global:planets).Planet
     }
 }

接下来,从脚本中删除 $planets 哈希表声明,如下所示。由 [Planet] 类填充的全局变量 $planets 包含行星数据。

$planets = [ordered]@{
     'Mercury' = 4879
     'Venus'   = 12104
     'Earth'   = 12756
     'Mars'    = 6805
     'Jupiter' = 142984
     'Saturn'  = 120536
     'Uranus'  = 51118
     'Neptune' = 49528
     'Pluto'   = 2306
 }

现在将剩余的原始代码包装在一个函数中,并将其命名为 Get-PlanetDiameter 以将其与脚本的名称区分开来。将脚本开头的 Param() 块放置在函数内。将行星的静态列表替换为对 [Planet] 类的引用,如下所示。

[ValidateSet([Planet],ErrorMessage="Value '{0}' is invalid. Try one of: {1}")]`

$output="The直径 of Planet {0} is {1} km" -f $_, $planets[$_] 替换为以下两行。这些允许脚本在由 Import-CSV 创建的对象数组中查找行星,而不是在您之前创建的哈希表中查找,该哈希表已从脚本中删除:

$targetplanet = $planets | Where -Property Planet -match $_
 $output = "The diameter of planet {0} is {1} km" -f $targetplanet.Planet, $targetplanet.Diameter

经过这组更改后,您的最终脚本需要如下所示:

class Planet : System.Management.Automation.IValidateSetValuesGenerator {
     [String[]] GetValidValues() {
         $Global:planets = Import-CSV -Path planets.csv
         return ($Global:planets).Planet
     }
 }
 Function Get-PlanetDiameter {
     Param(
         [Parameter(Mandatory)]
         [ValidateSet([Planet],ErrorMessage="Value '{0}' is invalid. Try one of: {1}")]
         $Planet
     )
     $Planet | Foreach-Object {
         $targetplanet = $planets | Where -Property Planet -match $_
         $output = "The diameter of planet {0} is {1} km" -f $targetplanet.Planet, $targetplanet.Diameter
         Write-Output $output
     }
 }

请记住,从此时起,该脚本仅适用于 PowerShell 6.1 或更高版本

现在,您如何使用这个脚本?

运行脚本

仅仅通过执行脚本并不能直接使用新版本的脚本。所有有用的代码现在都包装在一个函数中。您需要点源该文件,以允许从 PowerShell 会话中访问该函数。

PS61> . .\Get-PlanetSize.ps1

点源化后,您现在可以在 PowerShell 会话中访问新的 Get-PlanetDiameter 函数,并使用制表符完成功能。

所有这些工作有什么好处?”,我听到你问。 “脚本似乎以同样的方式工作,但使用代码更困难!”

尝试这个:

  • 打开您之前创建的 planets.csv 文件。
  • 添加具有新名称和直径的新行。
  • 保存 CSV 文件。

在您最初获取脚本的同一会话中,尝试使用 Get-PlanetDiameter 查找新行星的直径。有用!

以这种方式使用类给我们带来了几个好处:

  • 有效值列表现在与代码本身分开,但文件中值的任何更改都会由脚本获取。
  • 该文件可以由从不访问该脚本的人维护。
  • 更复杂的脚本可以从电子表格、数据库、Active Directory 或 Web API 查找信息。

正如您所看到的,当使用类提供 ValidateSet 值时,可能性几乎是无限的。

包起来

在构建 Get-PlanetSize.ps1 时,我们已经涵盖了很多基础知识,所以让我们回顾一下。

在这篇文章中您了解到:

  • ValidateSet 验证属性是什么以及您可能想要使用它的原因
  • 如何将 ValidateSet 添加到 PowerShell 函数或脚本
  • 制表符补全如何与 ValidateSet 配合使用
  • 如何使用 IgnoreCase 属性来控制您的 ValidateSet 是否区分大小写
  • 如何将 ErrorMessage 属性与您的 ValidateSet 和 PowerShell 6.1 结合使用
  • 如何使用类通过 PowerShell 6.1 制作动态 ValidateSet

你在等什么?立即开始使用ValidateSet

进一步阅读

  • 参数属性
  • 关于课程

您需要 登录账户 后才能发表评论

取消回复欢迎 发表评论:

关灯