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

[玩转系统] PowerShell 参数:完整指南

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

PowerShell 参数:完整指南


所有 PowerShell 命令都可以有一个或多个参数,有时称为参数。如果您没有在 PowerShell 函数中使用 PowerShell 参数,那么您就没有编写出好的 PowerShell 代码!

在本文中,您将了解创建和使用 PowerShell 参数或参数的各个方面!

这是我的书《PowerShell for SysAdmins》中的示例。如果您想学习 PowerShell 或学习一些行业技巧,请查看!

为什么需要参数?

当您开始创建函数时,您可以选择是否包含参数以及这些参数的工作原理。

假设您有一个安装 Microsoft Office 的函数。也许它会在函数内部静默调用 Office 安装程序。该函数的作用对于我们的目的来说并不重要。基本函数如下所示,包含函数名称和脚本块。

function Install-Office {
    ## run the silent installer here
}

在此示例中,您只需运行不带参数的 Install-Office 即可完成任务。

Install-Office 函数是否有参数并不重要。它显然没有任何强制参数;否则,PowerShell 不会让我们在不使用参数的情况下运行它。

何时使用 PowerShell 参数

Office 有很多不同的版本。也许您需要安装Office 2013和2016。目前,您无法指定这一点。每次想要更改行为时,您都可以更改函数的代码。

例如,您可以创建两个单独的函数来安装不同的版本。

function Install-Office2013 {
    Write-Host 'I installed Office 2013. Yippee!'
}

function Install-Office2016 {
    Write-Host 'I installed Office 2016. Yippee!'
}

这样做是有效的,但它不可扩展。它迫使您为每个发布的 Office 版本创建单独的功能。当你不需要时,你将不得不复制大量代码。

相反,您需要一种在运行时传递不同值来更改函数行为的方法。你怎么做到这一点?

是的!参数或者有些人所说的参数。

由于我们希望安装不同版本的Office而不需要每次都更改代码,因此您必须向此函数添加至少一个参数。

在快速想出要使用的 PowerShell 参数之前,首先要问自己一个问题: “您预计此功能需要的最小更改是什么?”。

请记住,您需要重新运行此函数,而不更改函数内部的任何代码。在这个例子中,这个参数你可能已经很清楚了;您需要添加一个 Version 参数。但是,当你的函数有数十行代码时,答案就不会太明显了。只要您尽可能准确地回答这个问题,它总是会有帮助的。

所以您知道您需要一个 Version 参数。怎么办?您现在可以添加一个,但就像任何优秀的编程语言一样,有多种方法可以剥掉那只猫的皮。

在本教程中,我将根据我近十年的 PowerShell 经验向您展示创建参数的“最佳”方法。但是,请注意,这并不是创建参数的唯一方法。

有一种叫做位置参数的东西。这些参数允许您将值传递给参数而无需指定参数名称。位置参数有效,但不被视为“最佳实践”。为什么?因为它们更难阅读,特别是当您在函数上定义了许多参数时。

创建简单的 PowerShell 参数

在函数上创建参数需要两个主要组件:一个 param 块 和参数本身。 param 块由 param 关键字后跟一组括号定义。

function Install-Office {
    [CmdletBinding()]
    param()
    Write-Host 'I installed Office 2016. Yippee!'
}

此时,该函数的实际功能并没有发生任何变化。我们刚刚组装了一些管道,为第一个参数做好准备。

一旦我们将 param 块放置到位,您现在就可以创建参数了。我建议您创建参数的方法包括 Parameter 块,后面是参数的类型,后面是下面的参数变量名称。

function Install-Office {
    [CmdletBinding()]
    param(
        [Parameter()]
        [string]$Version
    )
    Write-Host 'I installed Office 2016. Yippee!'
}

我们现在已经在 PowerShell 中创建了一个函数参数,但是这里到底发生了什么?

Parameter 块是每个参数的可选但推荐的部分。与param块一样,它是“功能管道”,它准备用于添加附加功能的参数。第二行是定义参数类型的地方。

在本例中,我们选择将 Version 参数转换为字符串。定义显式类型意味着传递给此参数的任何值将始终尝试“转换”为字符串(如果尚未转换)。

该类型不是强制性的,但强烈鼓励使用。明确定义参数的类型将显着减少以后出现的许多不需要的情况。相信我。

现在您已经定义了参数,您可以运行 Install-Office 命令,并使用 Version 参数向其传递版本字符串,例如 2013 。传递给 Version 参数的值有时称为参数的参数或值。

PS> Install-Office -Version 2013
I installed Office 2016. Yippee!

到底发生了什么事?你告诉它你想安装2013版本,但它仍然告诉你它安装的是2016版本。当你添加参数时,你必须记住将函数的代码更改为变量。当参数传递给函数时,该变量将扩展为传递的任何值。

更改 2016 的静态文本并将其替换为 Version 参数变量,并将单引号转换为双引号,以便变量扩展。

function Install-Office {
    [CmdletBinding()]
    param(
        [Parameter()]
        [string]$Version
    )
    Write-Host "I installed Office $Version. Yippee!"
}

现在您可以看到,传递给 Version 参数的任何值都将作为 $Version 变量传递到函数中。

强制参数属性

还记得我提到过 [Parameter()] 行只是“函数管道”,需要为进一步的工作准备函数吗?向参数添加参数属性是我之前谈到的额外工作。

参数不一定是变量的占位符。 PowerShell 有一个称为参数属性和参数验证的概念。参数属性以多种不同的方式改变参数的行为。

例如,您要设置的最常见参数属性之一是 Mandatory 关键字。默认情况下,您可以调用 Install-Office 函数而不使用 Version 参数,并且它会正常执行。 Version 参数是可选的。当然,它不会扩展函数内的 $Version 变量,因为没有值,但它仍然会运行。

很多时候,在创建参数时,您会希望用户始终使用该参数。您将依赖于函数代码中某处的参数值,如果未传递参数,函数将失败。在这些情况下,您希望强制用户将此参数值传递给您的函数。您希望该参数成为强制

一旦您像这里一样构建了基本框架,强制用户使用参数就很简单。您必须在参数的括号内包含关键字 Mandatory。一旦完成,执行不带参数的函数将停止执行,直到输入值。

function Install-Office {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$Version
    )
    Write-Host "I installed Office $Version. Yippee!"
}

PS> Install-Office
cmdlet Install-Office at command pipeline position 1
Supply values for the following parameters:
Version:

该函数将等待,直到您为 Version 参数指定一个值。一旦您执行此操作并按 Enter,PowerShell 将执行该函数并继续。如果您为参数提供值,PowerShell 不会每次都提示您输入该参数。

PowerShell 参数验证属性

强制参数是您可以添加的最常见的参数属性之一,但您也可以使用参数验证属性。在编程中,尽可能严格地限制用户输入始终是至关重要的。限制用户(甚至您!)可以传递给您的函数或脚本的信息将消除函数内必须考虑各种情况的不必要的代码。

通过实例学习

例如,在 Install-Office 函数中,我演示了将值 2013 传递给它,因为我知道它会起作用。我写了代码!我假设(永远不要在代码中这样做!)很明显,任何了解任何事情的人都会将版本指定为 2013 或 2016。好吧,对你来说显而易见的事情对其他人来说可能并不那么明显人们。

如果您想了解有关版本的技术信息,如果 Microsoft 仍在使用版本控制架构,则将 2013 版本指定为 15.0 并将 2016 版本指定为 16.0 可能会更准确,他们过去有过。但是,如果您假设他们要指定 2013 或 2016 的版本,那么您在函数内部有代码来查找包含这些版本的文件夹或其他内容,该怎么办?

下面是您可能在文件路径中使用 $Version 字符串的示例。如果有人传递的值未完成 Office2013Office2016 的文件夹名称,它将失败或执行更糟糕的操作,例如删除意外的文件夹或更改您没有更改的内容。 t 占。

function Install-Office {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$Version
    )
    Get-ChildItem -Path "\SRV1\Installers\Office$Version"
}
PS> Install-Office -Version '15.0'
Get-ChildItem : Cannot find path '\SRV1\Installers\Office15.0' because it does not exist.
At line:7 char:5

Get-ChildItem -Path "\SRV1\Installers\Office$Version"

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

CategoryInfo          : ObjectNotFound: (\SRV1\Installers\Office15.0:String) [Get-ChildItem], ItemNotFoundExcep
tion
FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand

要限制用户输入您期望的内容,您可以添加一些 PowerShell 参数验证。

使用 ValidateSet 参数验证属性

您可以使用多种参数验证。如需完整列表,请运行 Get-Help about_Functions_Advanced_Parameters。在此示例中,ValidateSet 属性可能是最好的。

ValidateSet 验证属性允许您指定允许作为参数值的值列表。由于我们只考虑字符串 20132016,我想确保用户只能指定这些值。否则,该功能将立即失败,并通知他们原因。

您可以在原始 Parameter 关键字下添加参数验证属性。在此示例中,参数属性的括号内有一个项目数组; 20132016。参数验证属性告诉 PowerShell,Version 的唯一有效值是 20132016。如果您尝试传递集合中的内容之外的内容,您将收到一条错误消息,通知您只有特定数量的可用选项。

function Install-Office {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ValidateSet('2013','2016')]
        [string]$Version
    )
    Get-ChildItem -Path "\SRV1\Installers\Office$Version"

}
PS> Install-Office -Version 15.0
Install-Office : Cannot validate argument on parameter 'Version'. The argument "15.0" does not belong to the set
"2013,2016" specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command
again.
At line:1 char:25

Install-Office -Version 15.0

                    ~~~~

CategoryInfo          : InvalidData: (:) [Install-Office], ParameterBindingValidationException
FullyQualifiedErrorId : ParameterArgumentValidationError,Install-Office

ValidateSet 属性是常用的验证属性。有关限制参数值的所有方式的完整详细信息,请通过运行 Get-Help about_Functions_Advanced_Parameters 查看 Functions_Advanced_Parameters 帮助主题。

参数集

假设您只想将某些 PowerShell 参数与其他参数一起使用。也许您已向 Install-Office 函数添加了 Path 参数。此路径将安装安装程序的任何版本。在这种情况下,您不希望用户使用 Version 参数。

您需要参数集。

参数可以分组为只能与同一组中的其他参数一起使用的组。使用下面的函数,您现在可以使用 Version 参数和 Path 参数来获取安装程序的路径。

function Install-Office {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ValidateSet('2013','2016')]
        [string]$Version,
        
        [Parameter(Mandatory)]
        [string]$Path
    )
    
    if ($Version) {
        Get-ChildItem -Path "\SRV1\Installers\Office$Version"
    } elseif ($Path) {
        Get-ChildItem -Path $Path
    }
}

但这会带来一个问题,因为用户可以使用这两个参数。此外,由于这两个参数都是强制性的,因此当这不是您想要的时,他们将被迫使用这两个参数。为了解决这个问题,我们可以将每个参数放置在一个参数集中,如下所示。

function Install-Office {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ParameterSetName = 'ByVersion')]
        [ValidateSet('2013','2016')]
        [string]$Version,
        
        [Parameter(Mandatory, ParameterSetName = 'ByPath')]
        [string]$Path
    )
    
    if ($Version) {
        Get-ChildItem -Path "\SRV1\Installers\Office$Version"
    } elseif ($Path) {
        Get-ChildItem -Path $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 文件看起来像这样:

ComputerName,Version
PC1,2016
PC2,2013
PC3,2016

您想要在该计算机上安装每台计算机旁边的 Office 版本。

首先,您必须在函数中添加一个 ComputerName 参数,以便为函数的每次迭代传递不同的计算机名称。下面我创建了一些伪代码来表示虚构函数中可能存在的一些代码,并添加了一个 Write-Host 实例来查看变量如何在函数内扩展。

function Install-Office {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ValidateSet('2013','2016')]
        [string]$Version,
    
        [Parameter()]
        [string]$ComputerName
    )

    <#
    ## Connect to the remote with some code here
    Invoke-Command -ComputerName $ComputerName -ScriptBlock {
        ## Do stuff to install the version of office on this computer
        Start-Process -FilePath 'msiexec.exe' -ArgumentList 'C:\Setup\Office{0}.msi' -f $using:Version
    }
    #>
    Write-Host "I am installing Office version [$Version] on computer [$ComputerName]"
}

ComputerName 参数添加到函数后;您可以通过读取 CSV 文件并将计算机名称和版本的值传递给 Install-Office 函数来实现此目的。

$computers = Import-Csv -Path 'C:\ComputerOfficeVersions.csv'
foreach ($pc in $computers) {
    Install-Office -Version $_.Version -ComputerName $_.ComputerName
}

构建参数的管道输入

这种读取 CSV 行并使用循环将每行的属性传递给函数的方法是“旧”方法。在本节中,您希望完全放弃 foreach 循环并使用管道。

按原样,该函数根本不支持管道。假设您可以使用管道将每个计算机名称和版本传递给函数,这是直观的。下面,我们读取 CSV 并将其直接传递给 Install-Office, 但这不起作用。

PS> Import-Csv -Path 'C:\ComputerOfficeVersions.csv' | Install-Office

你可以假设你想要的一切,但这并不能让它发挥作用。当您知道 Import-Csv 将其作为对象属性发送时,我们会收到输入 Version 参数的提示。为什么不起作用?因为您还没有添加任何管道支持。

PowerShell函数中有两种管道输入; ByValue(整个对象)和ByPropertyName(单个对象属性)。您认为将 Import-Csv 的输出拼接到 Install-Office 输入的最佳方式是什么?

根本不需要任何重构,您就可以使用 ByPropertyName 方法,因为毕竟 Import-Csv 已经返回属性 VersionComputerName 因为它们是 CSV 中的列。

向自定义函数添加管道支持比您想象的要简单得多。它只是用两个关键字之一表示的参数属性; ValueFromPipelineValueFromPipelineByPropertyName

在示例中,您希望将从 Import-Csv 返回的 ComputerNameVersion 属性绑定到 Version Install-Office 的 > 和 ComputerName 参数,因此您将使用 ValueFromPipelineByPropertyName

由于我们想要绑定这两个参数,因此您需要将此关键字添加到两个参数中,如下所示,并使用管道重新运行该函数。

function Install-Office {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
        [ValidateSet('2013','2016')]
        [string]$Version,
        
        [Parameter(ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [string]$ComputerName
)

    <#
    ## Connect to the remote with some code here
    Invoke-Command -ComputerName $ComputerName -ScriptBlock {
        ## Do stuff to install the version of office on this computer
        Start-Process -FilePath 'msiexec.exe' -ArgumentList 'C:\Setup\Office{0}.msi' -f $using:Version
    }
    #>
    Write-Host "I am installing Office version [$Version] on computer [$ComputerName]"
}

这很奇怪。它仅针对 CSV 中的最后一行运行。这是怎么回事?它只是执行最后一行的函数,因为您跳过了在没有管道支持的情况下构建函数时不需要的概念。

不要忘记进程块!

当您需要创建涉及管道支持的函数时,您必须(至少)在调用的函数内包含一个“嵌入”块。 处理。此过程块告诉 PowerShell 在接收管道输入时,为每次迭代运行该函数。默认情况下,它只会执行最后一个。

从技术上讲,您也可以添加其他块,例如 beginend,但脚本编写者并不经常使用它们。

为了告诉 PowerShell 对每个传入的对象执行此函数,我将添加一个包含其中代码的 process 块。

function Install-Office {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
        [ValidateSet('2013','2016')]
        [string]$Version,
        
        [Parameter(ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [string]$ComputerName
)

    process {
        <#
        ## Connect to the remote with some code here
        Invoke-Command -ComputerName $ComputerName -ScriptBlock {
            ## Do stuff to install the version of office on this computer
            Start-Process -FilePath 'msiexec.exe' -ArgumentList 'C:\Setup\Office{0}.msi' -f $using:Version
        }
        #>
        Write-Host "I am installing Office version [$Version] on computer [$ComputerName]"
    }
}

现在您可以看到从 Import-Csv 返回的每个对象的 VersionComputerName 属性已传递给 Install-Office绑定VersionComputerName 参数。

资源

要更深入地了解函数参数的工作原理,请查看我有关 PowerShell 函数的博客文章。

我还鼓励您查看我的题为“构建高级 PowerShell 函数和模块”的 Pluralsight 课程,深入了解有关 PowerShell 函数、函数参数和 PowerShell 模块的所有信息。

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

取消回复欢迎 发表评论:

关灯