什么是Windows PowerShell?它吃什么?第3部分:将参数传递给脚本和函数,创建cmdlet



系列第二部分介绍了PowerShell编程语言的基础知识,但是现在值得弄清楚如何使用上面编写的代码来执行管理任务。最明显的方法是运行脚本。除此之外,还可以创建自己的cmdlet。

目录:


位置参数
Param()块
其他参数属性
通过管道传递参数
函数主体结构
属性[CmdletBinding()]和高级函数
脚本编写模块并创建cmdlet

位置参数


在脚本和函数中,您可以传递其位置值写入到$ args内置变量的位置参数(参数)。该一维数组不需要事先声明,并且其范围限于脚本或函数。例如,运行最简单的脚本:

Write-Host " :" $args.count
Write-Host ":" $args


图片

在函数中,位置参数的使用方式相同:

function Print-Args {
    Write-Host " :" $args.count
    Write-Host " 0:" $args[0]
    Write-Host " 1:" $args[1] 
}

Print-Args“零”“一个”

请注意,在调用Print-Args时,我们不要在参数之间加上逗号:不是传递给函数的数组,而是写入一维数组$ args的单个值-其范围受函数主体的限制。

图片

上述方法允许您将任意数量的参数传输到脚本或函数,但是在调用时必须遵循其顺序,并且只能通过数组的索引访问它们-这并不总是很方便。

参数块()


在脚本和函数中,使用命名参数要方便得多。上一篇文章中,我们讨论了描述它们的一种方法:

function test ($arg0, ..., $argN)
{
       
}

开发人员熟悉类似的语法,但是调用函数时,参数(如果有)之间用空格隔开,并且不用括号括起来,这会引起一定的不协调。这就是shell语言的特殊性:为了以交互方式使用shell,值之间的空格要方便得多。的测试($ value0)调用也正确的,但是在这种情况下,所述参数是在括号中的整个表达,即($ value0)而不是$ value0。以这种方式传递几个参数将不起作用。调用test($ value0,$ value1)的结果是,该函数将仅接收一个-两个元素的数组,分别包含值$ value0$ value1

Microsoft Corporation建议使用Param()块-此语法更为通用,不仅可以指定函数参数,还可以指定脚本参数:

param (
    $arg0, $arg1
)

Write-Host $arg0 $arg1


图片

在函数主体中,它看起来像这样:

function test {
     param (
           $arg0, $arg1
     )
}

如果函数的参数列表很小,则Param()块只会使设计混乱,但是在许多情况下,它使代码更易读,并且尤其是一种好的编程风格的元素。

附加参数属性


描述函数参数或脚本参数时,可以设置它们的附加属性。最简单的示例是以下类型的强制安装:

param([int]$arg0)

要么

function test ([int]$arg0) {
}

除了类型转换外,您还可以使用[parameter()]属性:

param(
    [parameter(Argument1=value1, Argument2=value2)]
    $ParameterName
)

有了它的帮助,很容易使该参数成为必需参数。注意同时使用几个属性-在这种情况下,它们彼此跟随:

param([parameter(Mandatory=$true)][int]$arg0)

要么

function test ([parameter(Mandatory=$true)][int]$arg0) { 
}

要么

function test {
          parameter([parameter(Mandatory=$true)][int]$arg0)
}


图片

Position允许您指定参数的顺序(默认情况下,它对应于声明的顺序):

param(
                    [parameter(Mandatory=$true, Position=0)]
                    [int]
                    $arg0,

                    [parameter(Position=1)]
                    [string]
                    $arg1,

                    [parameter(Position=2)]
                    [array]
                    $arg2
)

[Parameter()]属性具有其他参数,有关其完整列表,请访问Microsoft 网站此处描述了其他属性,您可以借助它们来验证传递的值,使用正则表达式检查它们等。以下是一些:

[Alias()]为参数设置别名:

param(
    [parameter(Mandatory=$true)]
    [alias("ARG","ArgumentName")]
    [string[]]
    $arg0
)

强制转换运算符[string []]表示参数值是一个字符串数组。

[AllowNull()]允许$​​ null作为必需参数:

param(
    [parameter(Mandatory=$true)]
    [AllowNull()]
    [string]
    $arg0
)

[AllowEmptyString()]允许将空字符串作为必需参数:

param(
    [parameter(Mandatory=$true)]
    [AllowEmptyString()]
    [string]
    $arg0
)

[AllowEmptyCollection()]允许将空数组作为必需参数:

param(
    [parameter(Mandatory=$true)]
    [AllowEmptyCollection()]
    [string[]]
    $arg0
)

[ValidatePattern()]验证使用正则表达式:

param(
    [parameter(Mandatory=$true)]
    [ValidatePattern("[0-9][0-9][0-9][0-9]")]
    [string[]]
    $arg0
)

[ValidateLength()]检查字符串参数的长度:

param(
    [parameter(Mandatory=$true)]
    [ValidateLength(1,10)]
    [string]
    $arg0
)

通过管道传递参数


在本系列第一篇文章中,我们讨论了通过管道将数据传输到cmdlet的可能性。在PowerShell中,cmdlet和函数返回对象或对象数组(语句的结果),并在输入处接收它们。要看到这一点,我们使用Get-Help准备其中一个cmdlet:

Get-Help Stop-Process -Parameter Name


图片

通过管道,您可以接受为其设置了相应属性的参数值(ByValue和/或ByPropertyName)。在第一种情况下,该参数将与通过管道接收的对象进行匹配,前提是其类型对应于预期的参数。在第二个参数中,值将是传入对象的属性,该对象的名称与该参数的名称或别名相对应。要设置属性,请将[parameter()]与布尔参数ValueFromPipeline和ValueFromPipelineByPropertyName一起使用,其默认值为$ false:

param(
    [parameter(Mandatory=$true,
    ValueFromPipeline=$true)]
    [string[]]
    $Name
)

要么

param(
    [parameter(Mandatory=$true,
    ValueFromPipelineByPropertyName=$true)]
    [string[]]
    $Name
)

通常在必要时使用ValueFromPipelineByPropertyName来传递多个参数,以免造成混淆,并且该参数可以与ValueFromPipeline同时使用:

param(
    [parameter(Mandatory=$true,
    ValueFromPipeline=$true,
    ValueFromPipelineByPropertyName=$true)]
    [string[]]
    $Name
)

Write-Host $Name


图片

如您所见,脚本也可以通过管道接收参数,但是,对于高级功能上述属性的实际应用更有可能,下面将对此进行讨论。

功能体结构


在PowerShell中,一个函数可以包括语句括号中包含的三个可选代码块-Begin,Process和End。看起来像这样:

function test
{
   param()
   begin {}
   process {}
   end {}
}

Begin块是第一个要执行一次的块,如果将参数通过管道传递给函数,则将在处理第一个对象之前启动代码。在这种情况下,Begin块中的$ _和$ PSItem变量将不包含值。如果使用显式指定的参数调用该函数,则它们将在Begin块中可用,因为不需要等待从管道接收到对象。接下来,执行Process块:如果参数通过管道传递,则将为每个对象一个一个地启动它。对于明确指定的参数,Process块仅启动一次。该函数通过执行一次End块结束。显然,只有在函数可以从管道接收对象的情况下,才有理由使用这些构造:

function test
{

    param(
        [Parameter(ValueFromPipeline)]
        [string[]]
        $Param1,

        [string]$Param2
    )

    begin
    {
        Write-Host " Begin"
        Write-Host "       ( pipeline):" $Param1
        Write-Host "       ( ):" $Param2
    }

    process {
        Write-Host " Process"
        Write-Host "       ( pipeline):" $Param1
        Write-Host "       ( ):" $Param2
    }

    end
    {
        Write-Host " End"
        Write-Host "       ( pipeline):" $Param1
        Write-Host "       ( ):" $Param2
    }
}

“一个”,“两个”,“三个” | 测试-Param2'四个'

图片

[CmdletBinding()]属性和高级功能


要创建“高级”功能(严格来说是脚本),可以使用[ CmdletBinding() ] 属性特别是,它允许您使用Visual Studio中编译的二进制cmdlet(它们是.NET Core类的类)的功能来定义高级功能。由于此属性主要用于函数中,因此我们将对其停止:

function <Name>
{
    [CmdletBinding(ConfirmImpact=<String>,
    DefaultParameterSetName=<String>,
    HelpURI=<URI>,
    SupportsPaging=<Boolean>,
    SupportsShouldProcess=<Boolean>,
    PositionalBinding=<Boolean>)]

    Param ()

    Begin{}
    Process{}
    End{}
}

实际上,[CmdletBinding()] 通过调用构造函数来初始化CmdletBindingAttribute类的新实例,可以将可选参数传递给该构造函数。有关它们的详细说明,请访问 Microsoft 网站。 CmdletBinding属性允许您控制高级功能的其他功能:添加对-Confirm和-WhatIf(通过SupportsShouldProcess),-Force,-Verbose和-Debug的支持,以及禁用位置参数绑定等。此外,我们将分析特殊参数的使用。-Force

参数用于禁止执行各种操作的请求。-如果

需要模拟启动并显示有关在不使用此参数的情况下执行功能(命令)的后果的信息。通常在函数可以执行破坏性操作时使用。

Remove-Item C:\ Windows \ notepad.exe -WhatIf

图片

-Confirm需要确认,如果该功能可以执行破坏性操作,则也要使用该项目。

function Delete-File {
[CmdletBinding(
    ConfirmImpact = 'High',
    SupportsShouldProcess = $true
)]
    param(
        [string]$File,
        [switch]$Force
    )
    if ($Force -or $PSCmdlet.ShouldProcess($File,"Delete file")) {
        Remove-Item $File
    }
}


图片

要处理-WhatIf和/或-Confirm,将调用ShouldProcess方法(SupportsShouldProcess = $ true),该方法将提示或模拟命令的执行。为了实现-Force处理,我们首先将其置于IF条件中。首先,检查-or运算符左侧的表达式,如果该表达式为true,则测试停止-将不调用ShouldProcess方法。另外,在[CmdletBinding()]属性中,我们指定了ConfirmImpact参数,该参数确定代码对系统的影响程度,并包括-Confirm参数处理程序。此参数可以采用以下值:

或未指定-即使传递了-Confirm参数,也不会显示确认消息。

-该功能对系统有轻微影响,不会造成数据丢失的重大风险。

-中度暴露,几乎没有破坏性操作导致数据丢失的风险。

-由于破坏性行为,该代码极有可能导致数据丢失。

默认情况下,对于PowerShell会话,暴露级别被认为是高。当前值存储在$ ConfirmPreference变量中,并且如果代码对系统具有相同或更高级别的影响,将始终显示确认请求。

选项-Verbose-Debug需要显示调试信息。他们的使用被认为是一种很好的编程风格(不用担心Write-Host了,在高级功能中没有必要)。第一个参数显示有关进度的信息,第二个参数显示详细的调试信息。它还可以切换到分步执行代码。-Verbose和-Debug的行为定义如下:

function Get-Something {
[CmdletBinding()]
    param()
    if ($PSBoundParameters.Verbose) {$VerbosePreference = "Continue"}
    if ($PSBoundParameters.Debug) {$DebugPreference = "Continue"}
    Write-Verbose "Type some verbose information"
    Write-Debug "Type some debug information"
}

为了使用特殊参数,我们使用了$ PSBoundParameters变量。默认情况下,$ VerbosePreference和$ DebugPreference的值等于'SilentlyContinue',因此,即使指定了相应的参数,调试信息也不会显示-它们必须转移到'Continue'状态。

脚本模块和Cmdlet创建


让我们开始创建自己的cmdlet。实际上,这些是高级功能,在所谓的脚本模块 -扩展名为.psm1的文本文件。它们存储在环境变量PSModulePath中定义的目录中。您可以使用以下命令查看它们的路径:

Get-ChildItem Env:\ PSModulePath | Format-Table -AutoSize

标准集如下所示:

C:\ Users \%UserName%\ Documents \ WindowsPowerShell \ Modules
C:\ Program Files \ WindowsPowerShell \ Modules
C:\ Windows \ System32 \ WindowsPowerShell \ v1.0 \ Modules

使用上一节中扩展的Delete-File功能创建ModuleName.psm1文件后,您需要将其保存,例如,在C:\ Users \%UserName%\ Documents \ WindowsPowerShell \ Modules \ ModuleName中。请注意,脚本模块必须存储在单独的子目录中,该子目录的名称应与.psm1文件的基本名称(无扩展名)一致。运行Import-Module ModuleName命令后,Delete-File函数将对用户可用,并且由于它是高级的,从实际的角度来看,它是相同的cmdlet。

图片

在本文中,我们已经详细研究了参数向函数和脚本的传递。本系列的下一部分将重点介绍面向对象的编程。

第1部分:Windows PowerShell基础知识
2: Windows PowerShell
4: ,



All Articles