所有 PowerShell 命令都可以有一个或多个参数,有时被称为参数。如果您在 PowerShell 函数中不使用 PowerShell 参数,那么您就不是在编写良好的 PowerShell 代码!
在本文中,您将学习有关创建和使用 PowerShell 参数或参数的几乎所有方面!
这是我书中《PowerShell for SysAdmins》的示例。如果您想学习 PowerShell 或学习一些行业技巧,来看看吧!
为什么需要参数?
当您开始创建函数时,您可以选择包括参数或不包括参数以及这些参数的工作方式。
假设您有一个安装 Microsoft Office 的函数。也许它在函数内部静默调用 Office 安装程序。函数的作用对我们来说并不重要。基本函数如下,其中包括函数的名称和脚本块。
在这个例子中,您只是运行了 Install-Office
,而没有使用参数,它会执行其操作。
无论 Install-Office
函数是否有参数都没关系。显然它没有任何强制性参数;否则,PowerShell 将不允许我们在不使用参数的情况下运行它。
何时使用 PowerShell 参数
办公软件有很多不同的版本。也许你需要安装 Office 2013 和 2016 版本。目前,你没有办法指定这一点。每次想要改变行为时,你都可以更改函数的代码。
例如,你可以创建两个单独的函数来安装不同的版本。
这样做是可行的,但不具有可扩展性。它强迫你为每个发布的 Office 版本创建一个单独的函数。当你无需这样做时,你将不得不复制大量的代码。
相反,你需要一种方法在运行时传入不同的值来改变函数的行为。你怎么做呢?
是的!参数或有些人称之为参数。
由于我们想要安装不同版本的 Office 而无需每次更改代码,你必须向该函数添加至少一个参数。
在你迅速想出要使用的 PowerShell 参数之前,首先要问自己一个问题;“在这个函数中你预计需要做出的最小更改是什么?”。
记住,你需要重新运行这个函数而不改变函数内部的任何代码。在这个例子中,参数对你来说可能很明显;你需要添加一个Version
参数。但是,当你有一个有几十行代码的函数时,答案可能不太明显。只要尽可能准确地回答这个问题,它总是会有所帮助。
所以你知道你需要一个Version
参数。现在呢?现在你可以添加一个,但像任何一个优秀的编程语言一样,有多种方法来实现这个目标。
在这个教程中,我将向您展示基于我近十年的PowerShell经验创建参数的“最佳”方法。然而,要知道这并不是创建参数的唯一方式。
还有一种叫做位置参数的东西。这些参数允许您向参数传递值,而无需指定参数名称。位置参数能够工作,但并不被认为是“最佳实践”。为什么?因为当您在函数上定义了许多参数时,它们很难阅读。
创建一个简单的PowerShell参数
在函数上创建参数需要两个主要组件;一个参数块和参数本身。一个param块由param
关键字定义,后面跟着一对括号。
在这一点上,函数的实际功能还没有改变。我们只是组合了一些管道,为第一个参数做准备。
一旦我们有了param块,现在您将创建参数。我建议您创建参数的方法包括Parameter
块,后跟参数类型,然后是参数变量名称。
我们现在已经在PowerShell中创建了一个函数参数,但这里究竟发生了什么?
Parameter
块是每个参数的可选但建议的一部分。像param块一样,它是“函数管道”,为参数添加额外功能做准备。第二行是您定义参数类型的地方。
在这种情况下,我们选择将Version
参数转换为字符串。定义显式类型意味着传递给该参数的任何值,如果尚未是字符串,将始终尝试“转换”为字符串。
类型不是强制性的,但强烈建议使用。明确定义参数的类型将显著减少以后许多不必要的情况。相信我。
现在,参数已经定义好了,你可以运行Install-Office
命令,并将Version
参数传递一个版本字符串,比如2013。传递给Version
参数的值有时称为参数的参数或值。
这里到底发生了什么?你告诉它你想安装2013版,但它仍然告诉你已经安装了2016版。当你添加一个参数时,你必须记住将函数的代码更改为变量。当参数传递给函数时,该变量将扩展为传递的任何值。
更改2016的静态文本,并将其替换为Version
参数变量,并将单引号转换为双引号,以便变量会被展开。
现在你可以看到,无论你传递给Version
参数的值是什么,它都会作为$Version
变量传递到函数中。
强制性参数属性
回想一下,我提到过[Parameter()]
行只是“函数管道”,需要为函数做好进一步的准备工作吗?向参数添加参数属性就是我之前提到的额外工作。
A parameter doesn’t have to be a placeholder for a variable. PowerShell has a concept called parameter attributes and parameter validation. Parameter attributes change the behavior of the parameter in a lot of different ways.
例如,你将设置的最常见的参数属性之一是Mandatory
关键字。默认情况下,你可以调用Install-Office
函数而不使用Version
参数,它会正常执行。版本参数是可选的。当然,它不会在函数内部展开$Version变量,因为没有值,但它仍然会运行。
在创建参数时,很多时候你会希望用户始终使用该参数。你将依赖于函数内部某处的参数值,如果参数没有传递,函数将失败。在这些情况下,你希望强制用户传递这个参数值给你的函数。你希望该参数变成强制性的。
一旦你建立了基本框架,让用户强制使用参数就很简单,就像你在这里所做的一样。你必须在参数的括号内包含关键字Mandatory
。一旦你完成了这一步,执行没有该参数的函数将会停止执行,直到输入了一个值。
函数将等待你为Version
参数指定一个值。一旦你这样做并按下Enter,PowerShell将执行该函数并继续。如果你为参数提供了一个值,PowerShell将不会每次提示你输入参数。
PowerShell参数验证属性
使参数变为必填项是你可以添加的最常见的参数属性之一,但你还可以使用参数验证属性。在编程中,限制用户输入是非常重要的,尽量让输入更为严格。限制用户(甚至是你自己!)可以传递给函数或脚本的信息,将消除函数内部不必要的代码,这些代码必须考虑各种情况。
通过示例学习
例如,在Install-Office
函数中,我演示了向其传递值2013
的过程,因为我知道它会起作用。我写了这段代码!我在代码中假设(在代码中永远不要这样做!)任何了解的人都会指定版本为2013或2016。嗯,对你来说很明显的事情对其他人来说可能并不那么明显。
如果你想要更详细地了解版本,可能更准确的是将2013版本指定为15.0
,将2016版本指定为16.0
,如果Microsoft仍然使用过去的版本架构。但是,如果由于你假设他们将指定2013或2016的版本,你的函数内部有一些查找具有这些版本的文件夹或其他内容的代码,该怎么办呢?
下面是一个示例,你可能正在使用$Version
字符串构建文件路径。如果有人传递一个不完整的文件夹名称,如Office2013
或Office2016
的值,它将失败,或者执行更糟糕的操作,比如删除意外的文件夹或更改你没有考虑到的东西。
为了限制用户输入的内容,您可以添加一些 PowerShell 参数验证。
使用 ValidateSet 参数验证属性
有各种各样的参数验证方法可供选择。要查看完整列表,请运行 Get-Help about_Functions_Advanced_Parameters
。在这个示例中,ValidateSet
属性可能是最合适的选择。
ValidateSet
验证属性允许您指定允许作为参数值的值列表。由于我们只考虑字符串 2013 或 2016,我希望确保用户只能指定这些值。否则,函数将立即失败,并通知他们原因。
您可以在原始 Parameter
关键字的正下方添加参数验证属性。在这个示例中,在参数属性的括号内,您有一个项目数组;2013 和 2016。参数验证属性告诉 PowerShell,Version
的有效值只能是 2013 或 2016。如果尝试传递不在集合中的值,您将收到一个错误通知,告诉您只有特定的选项可用。
ValidateSet
属性是常用的验证属性。要了解参数值受限的所有方式的详细信息,请查看运行 Get-Help about_Functions_Advanced_Parameters
命令的 Functions_Advanced_Parameters
帮助主题。
参数集
假设您只想让某些 PowerShell 参数与其他参数一起使用。也许您已向 Install-Office
函数添加了一个 Path
参数。此路径将安装安装程序的任何版本。在这种情况下,您不希望用户使用 Version
参数。
您需要参数集。
参数可以分组成只能与同一组中的其他参数一起使用的集合。使用下面的函数,您现在可以同时使用 Version
参数和 Path
参数来生成安装程序的路径。
然而,这会带来一个问题,因为用户可能会同时使用两个参数。此外,由于两个参数都是强制性的,当不希望如此时,他们将被强制使用两个参数。为了解决这个问题,我们可以像下面这样将每个参数放在一个参数集中。
通过在每个参数上定义参数集名称,这允许您一起控制参数组。
默认参数集
如果用户尝试不带参数运行 Install-Office
会发生什么?这是没有考虑到的,您将会看到友好的错误消息。

要修复这个问题,您需要在CmdletBinding()
区域内定义一个默认参数集。这会告诉函数,如果没有显式使用参数,则选择要使用的参数集,将[CmdletBinding()]
更改为[CmdletBinding(DefaultParameterSetName = 'ByVersion')]
。
现在,每当您运行Install-Office
时,它都会提示您输入Version
参数,因为它将使用该参数集。
管道输入
到目前为止的示例中,您一直在创建具有 PowerShell 参数的函数,这些参数只能使用典型的-ParameterName Value语法传递。但是,正如您已经学到的那样,PowerShell具有直观的管道,允许您无缝地将对象从一个命令传递到另一个命令,而不使用“典型”的语法。
当您使用管道时,您可以使用管道符号|
将命令“链接”在一起,使用户能够将Get-Service
的输出发送到Start-Service
,从而将Name
参数传递给Start-Service
作为一种快捷方式。
使用循环的“旧”方法
在您正在处理的自定义函数中,您正在安装 Office 并具有Version
参数。假设您在一个 CSV 文件中的一行中有一个计算机名列表,并在第二行中有需要在这些计算机上安装的 Office 版本。CSV 文件看起来像这样:
您希望在每台计算机旁边安装该计算机的 Office 版本。
首先,您需要在函数上添加一个ComputerName
参数,以便在每次函数迭代时传递不同的计算机名称。下面我创建了一些伪代码,代表可能存在于虚构函数中的一些代码,并添加了一个Write-Host
实例,以查看函数内部变量的展开情况。
一旦您在函数中添加了ComputerName
参数,您可以通过读取CSV文件并将计算机名称和版本的值传递给Install-Office
函数来实现这一点。
为参数构建管道输入
通过读取CSV行并使用循环将每行的属性传递给函数的方法是“旧”方法。在此部分,您要完全放弃foreach
循环,而是改用管道。
目前,该函数根本不支持管道。直观上,您可能会认为可以使用管道将每台计算机的名称和版本传递给函数。在下面的示例中,我们正在读取CSV并直接将其传递给Install-Office
,但这种方法行不通。
你可以假设任何你想要的,但那并不能让它正常工作。当你知道Import-Csv
正在将Version
参数作为对象属性发送时,我们会被提示输入Version
参数。为什么它不起作用呢?因为你还没有添加任何管道支持。
在PowerShell函数中有两种类型的管道输入;按值(整个对象)和按属性名称(单个对象属性)。你认为哪种方法是将Import-Csv
的输出与Install-Office
的输入连接在一起的最佳方式?
即使完全没有重构,你也可以使用按属性名称
的方法,因为毕竟Import-Csv
已经返回了CSV中的列Version
和ComputerName
这些属性。
为自定义函数添加管道支持要比你想象的简单得多。它仅仅是一个参数属性,表示为两个关键字之一;ValueFromPipeline
或ValueFromPipelineByPropertyName
。
在这个示例中,你想要将Import-Csv
返回的ComputerName
和Version
属性绑定到Install-Office
的Version
和ComputerName
参数,所以你将使用ValueFromPipelineByPropertyName
。
由于我们想要绑定这两个参数,你将在两个参数中都添加这个关键字,如下所示,并使用管道重新运行函数。
这很奇怪。它只对 CSV 中的最后一行运行了。发生了什么?它只执行了最后一行,因为你忽略了在构建不支持管道的函数时不需要的一个概念。
不要忘记进程块!
当你需要创建一个涉及管道支持的函数时,你必须至少在你的函数内包含一个叫做process
的“嵌入式”块。
这个处理块告诉 PowerShell,在接收到管道输入时,对每次迭代运行该函数。默认情况下,它只会执行最后一个。
你可以在函数中技术上添加其他块,比如begin
和end
,但是脚本作者不常用它们。
现在你可以看到,每个对象的`Version`和`ComputerName`属性从`Import-Csv`返回后,被传递给`Install-Office`并绑定到`Version`和`ComputerName`参数。
资源
要深入了解函数参数是如何工作的,查看我的关于PowerShell函数的博客文章。
I also encourage you to check my Pluralsight course entitled Building Advanced PowerShell Functions and Modules for an in-depth breakdown of everything there is to know about PowerShell functions, function parameters, and PowerShell modules.