当你编写 PowerShell 脚本或函数时,通常希望通过参数接受用户输入。如果不限制这些参数接受的值,就无法保证不会出现提供不适当值的情况。在本文中,您将学习如何使用 PowerShell 的 \texttt{ValidateSet} 参数验证属性,将这些值限制为您定义的值。
在编写 PowerShell 脚本或函数时,您可以使用许多不同的验证属性来检查传递给参数的值是否可接受,并在它们不可接受时通知用户。
本文重点介绍 \texttt{ValidateSet} 验证属性。您将了解 \texttt{ValidateSet} 的作用,以及为什么您可能希望在代码中使用 \texttt{ValidateSet} 以及如何使用。您还将了解由 \texttt{ValidateSet} 启用的选项完成功能,该功能将帮助您的代码用户提供有效的参数值。
PowerShell ValidateSet: 简要概述
ValidateSet 是一个参数属性,允许您定义一组仅可接受作为该参数值的元素。
例如,也许您有一个脚本,被定义为与 Active Directory 域控制器一起使用。此脚本具有一个参数,用于指定域控制器的名称。将可接受的值列表限制为域控制器的实际名称是否有意义呢?在您预先知道脚本需要的值时,用户就无法使用“foobar”作为值。ValidateSet 为您提供了这种能力。
需求
本文将是一个学习演练。如果你计划跟着做,你将需要以下工具:
- Visual Studio Code或任何其他代码编辑器。我将使用Visual Studio Code。
- 至少需要PowerShell 5.1来运行本文中大部分的代码。有一个部分需要 PowerShell 6.1或更高版本,我会在到达该部分时进行标识
本文中所有代码都在以下环境中测试通过:
Operating System | PowerShell Versions |
---|---|
Windows 7 SP1 | 5.1, Core 6.2 |
Windows 10 1903 | 5.1, Core 6.2 |
Linux Mint 19.2 | Core 6.2 |
为了帮助解释围绕ValidateSet的概念,你将构建一个名为Get-PlanetSize.ps1的小脚本。该脚本返回我们太阳系行星大小的信息。
你将从一个简单的脚本开始,逐渐提升其处理来自最终用户的输入的能力,并使其能够轻松发现可能的参数值。
入门
首先,将下面的PowerShell代码复制并粘贴到你喜欢的文本编辑器中,保存为Get-PlanetSize.ps1。
从PowerShell提示符中运行脚本,你应该会得到以下结果:
信息详尽但不够灵活;返回每个行星的信息,即使你只想要火星的信息。
也许你想要能够指定单个行星而不是返回所有行星的能力。你可以通过引入一个参数来实现这一点。让我们看看如何实现这一点。
使用参数接受输入
为了让脚本接受参数,将一个Param()
块添加到脚本的顶部。将参数命名为Planet
。一个适当的Param()
块如下所示。
在下面的示例中,[Parameter(Mandatory)]
确保向脚本提供了行星名称。如果缺少,则脚本将提示输入。
将这个Planet
参数最简单地加入到脚本中的方法是将行$planets.keys | Foreach-Object {
修改为$Planet | Foreach-Object {
。现在,你不再依赖于之前静态定义的哈希表,而是读取Planet
参数的值。
现在,如果你运行脚本并使用Planet
参数指定一个行星,你将只看到关于该特定行星的信息。
很好。脚本完成了吗?也许还没有。
选项太开放了
如果你试图使用Get-PlanetSize.ps1来查找行星Barsoom的直径会发生什么?
嗯,这不对。Barsoom不在行星列表中,但脚本仍然运行了。我们该如何修复这个问题?
问题在于脚本接受任何输入并使用它,不管它是否是有效的值。脚本需要一种方法来限制哪些值可以被接受作为Planet
参数。输入ValidateSet!
确保仅使用特定值
A ValidateSet list is a comma-separated list of string values, wrapped in single or double-quotes. Adding a ValidateSet attribute to a script or function parameter consists of adding a line of text to the Param()
block, as shown below. Replace the Param()
block in your copy of Get-PlanetSize.ps1 with the one below and save the file.
尝试再次运行脚本,使用Barsoom作为您的Planet
参数。现在会返回一个有用的错误消息。该消息具体说明了出了什么问题,甚至提供了参数可能值的列表。
使PowerShell ValidateSet区分大小写
默认情况下,ValidateSet属性不区分大小写。这意味着它将接受任何字符串,只要它在允许的列表中以任何大小写方案出现。例如,上面的示例将接受Mars
和mars
。如果有必要,您可以通过使用IgnoreCase
选项来强制ValidateSet区分大小写。
ValidateSet中的IgnoreCase
选项是一个验证属性,确定提供给参数的值是否与有效值列表完全匹配。默认情况下,IgnoreCase
设置为$True
(忽略大小写)。如果将其设置为$False
,那么将mars作为Get-PlanetSize.ps1
的Planet
参数的值将生成错误消息。
您可以通过在有效值列表的末尾为其分配$true
值来使用IgnoreCase
选项,如下所示。
现在,当您尝试使用不完全像列表中的值的Planet
的值时,验证将失败。
使用Tab Completion
使用ValidateSet的另一个好处是它提供了Tab Completion。这意味着您可以使用TAB键循环浏览参数的可能值。这极大地提高了脚本或函数的可用性,特别是从控制台使用时。
在下面的示例中,有几点需要注意:
- 在显示最后一个值后,制表符完成会回到第一个值。
- 尽管这些值未按ValidateSet字母顺序列出,但它们以字母顺序呈现。
- 键入初始字母并按TAB键会将制表符完成提供的值限制为以该字母开头的值。


您还可以在PowerShell集成脚本环境(ISE)中利用ValidateSet制表完成,如下例所示。ISE Intellisense功能会在一个漂亮的选择框中显示可能值的列表。
Intellisense返回包含您键入的字母的所有值,而不仅仅是以它开头的值。

现在我们已经介绍了ValidateSet验证属性在Windows 5.1中的情况,让我们看看在PowerShell Core 6.1中添加了什么,看看那是否可以为我们的脚本提供更多的验证功能。
理解PowerShell 6.1中对ValidateSet的更改
随着 PowerShell Core 6.1 的到来,已经添加了两个新的功能到 ValidateSet 验证属性:
- 属性
ErrorMessage
- 通过访问
System.Management.Automation.IValidateSetValuesGenerator
来使用 ValidateSet 中的类
属性
当您向Get-PlanetSize.ps1 提供一个不正确的行星名称时生成的默认错误消息很有帮助,但有点啰嗦:
使用ValidateSet
验证属性的ErrorMessage
属性来设置不同的错误消息,如下例所示。{0}
会自动替换为提交的值,{1}
会自动替换为允许的值列表。
替换脚本文件中的Param()
块并保存。然后再次尝试Get-PlanetSize.ps1 -Planet Barsoom
。注意下面的错误消息少了很多冗长,更具描述性。
接下来,看一看通过 PowerShell 类定义 ValidateSet 中可接受值的新方法。
PowerShell 类
自 PowerShell 版本 5 起已经提供了自定义类型,称为类。随着 PowerShell Core 6.1 的到来,现在可以使用类来为 ValidateSet 提供值。
使用类允许您解决 ValidateSet 的主要限制 – 它是静态的。也就是说,它作为函数或脚本的一部分嵌入,并且只能通过编辑脚本本身来更改。
与 ValidateSet
配合使用的新功能是能够使用 System.Management.Automation.IValidateSetValuesGenerator 类。我们可以将其作为基类,使用继承来创建我们自己的类。要与 ValidateSet
配合使用,类必须基于 System.Management.Automation.IValidateSetValuesGenerator,并且必须实现一个名为 GetValidValues()
的方法。
GetValues()
方法返回您希望接受的值列表。一个包含静态行星列表的 Param()
块被替换为一个 [Planet]
类将如下所示。此示例目前不起作用。继续阅读以了解如何实现这一点。
使用类来进行 ValidateSet 值列表:一个真实的例子
为了演示使用类作为 ValidateSet 值列表,您将使用从 CSV 文本文件加载的列表来替换之前使用的静态行星列表。您将不再需要在脚本本身内部维护一个静态值列表!
为类创建数据源
首先,您需要创建一个包含每个有效值的 CSV 文件。为此,将此数据集复制并粘贴到一个新的文本文件中,并将其保存为与 Get-PlanetSize.ps1 脚本相同文件夹中的 planets.csv。
将脚本添加到类
ValidateSet使用的任何类在ValidateSet尝试使用它之前必须已经定义。这意味着Get-PlanetSize.ps1的结构将无法正常工作。
必须在使用之前定义[Planet]
类,因此它必须放在脚本的开头。适当的骨架类定义如下:
在类的GetValidValues()
方法中,使用Import-CSV
cmdlet导入先前创建的文本文件planets.csv。文件被导入到具有全局范围的变量$planets
中,因此稍后可以在脚本中访问它。
使用return
语句通过GetValidValues()
返回行星名称列表。进行这些更改后,类现在应如下所示。
接下来,从脚本中删除如下所示的$planets
哈希表声明。由[Planet]
类填充的全局变量$planets
包含行星数据。
现在,将剩余的原始代码封装在一个函数中,并将其命名为Get-PlanetDiameter
以区别于脚本的名称。将位于脚本开头的Param()
块放入函数中。将行星的静态列表替换为对[Planet]
类的引用,如下所示。
替换行$output = "行星{0}的直径是{1}公里" -f $_, $planets[$_]
为以下两行。这些行允许脚本查找由Import-CSV
创建的对象数组中的行星,而不是您之前创建的哈希表,您已经从脚本中删除了:
在进行了这一系列更改后,您的最终脚本应该如下所示:
请记住,从此时开始,该脚本只能在PowerShell 6.1或更新版本上运行
现在,您如何使用这个脚本?
运行脚本
您不能直接执行脚本来使用新版本的脚本。现在所有有用的代码都被封装在一个函数中。您需要点源该文件,以允许在PowerShell会话中访问该函数。
一旦点源化,您现在可以在PowerShell会话中使用新的Get-PlanetDiameter
函数,并使用制表符补全。
“所有这一切工作的好处是什么?”我听到你问。“脚本似乎以相同的方式工作,但使用代码更加困难!”
试试这个:
- 打开您之前创建的
planets.csv
文件。 - 添加一行新的名称和直径。
- 保存CSV文件。
在您最初加载脚本的同一会话中,尝试使用 Get-PlanetDiameter
查找新行星的直径。它有效!
以这种方式使用类给我们带来了几个好处:
- 有效值列表现在与代码本身分离,但文件中的任何值更改都会被脚本捕捉到。
- 该文件可以由从未访问脚本的人维护。
- A more complex script could look up information from a spreadsheet, database, Active Directory or a web API.
正如您所看到的,使用类来提供ValidateSet值时的可能性几乎是无穷无尽的。
总结
我们已经涵盖了很多内容,因为我们构建了Get-PlanetSize.ps1
,让我们回顾一下。
在本文中,您已经学到了:
- 什么是ValidateSet验证属性,以及为什么您可能想使用它
- 如何将ValidateSet添加到PowerShell函数或脚本
- 带有ValidateSet的制表符完成的工作原理
- 如何使用
IgnoreCase
属性控制您的ValidateSet是否区分大小写 - 如何在ValidateSet和PowerShell 6.1中使用
ErrorMessage
属性 - 如何使用类在PowerShell 6.1中创建动态的ValidateSet
您还在等什么?开始今天使用ValidateSet吧!
更多阅读
Source:
https://adamtheautomator.com/powershell-validateset/