Windows PowerShell:在脚本中编写 Cmdlet

Don Jones

Windows PowerShell v2 中一项很酷的新功能是能够编写性能明显改进的函数。这些函数完全是在脚本中编写的,与使用 C# 或 Visual Basic 编写并在 Visual Studio 中编译的“真正”Cmdlet 具有相同的功能。这些高级函数 在 V2 开发周期的早期最初称为“脚本 Cmdlet”,可以帮助您编写更灵活的函数,然后您就可以将它们与常规 Cmdlet 一起无缝使用。

在绑定中完成所有操作

纯粹的函数与完整 Cmdlet 之间的真正差别在于 Cmdlet 支持功能强大的参数绑定。您可以使用位置参数、命名参数和强制参数,甚至可以执行基本的参数验证检查,所有这些都只需要向 Shell 说明参数。例如:

动手创建自己的模块

那么,这如何帮助您更轻松地分发脚本?答案是第二种模块:脚本模块。这不过是一个普通的 Windows PowerShell 脚本,其文件扩展名为 .psm1,而不是一般的 .ps1。将 mymodule.psm1 放到 \modules 文件夹中之后,就可以运行 Import-Module MyModule,这将执行您的脚本。

通常,脚本模块完全由函数组成。也就是说,在导入该模块时,实际上不会执行任何操作,而仅仅将脚本模块中的函数加载到 Shell 中,并供整个 Shell 使用。假设您有一个类似下面的脚本模块:

function Get-Inventory {
    [CmdletBinding()]
    param (
        [parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [string[]]$computername,
        
        [parameter(Mandatory=$false)]
        [alias("PF")]
        [switch]$pingfirst,
        
        [parameter(Mandatory=$true,Position=0)]
        [AllowEmptyString()]
        [string]$class
        
    )
    PROCESS {
    }
}

此语句中声明了三个参数:

  • computername 是一个字符串或字符串数组。该参数是强制性的,并且接受字符串管道输入,这表示如果您传送一组字符,这些字符将会自动放入 $computername 变量。
  • pingfirst 不是强制性的,但如果您确实要使用它,则应该使用 -PF 别名。这样就可以少键入一些字母。这是一个开关参数,也即它不接受值。它要么打开,要么关闭。
  • class 也是强制性的,但您甚至不需要键入 -class 参数名。只需要在运行函数时,为该参数提供一个适当的值作为“第一个位置”值。虽然这是强制性的,但它接受空字符串。

联机帮助中有更多属性和大量示例。运行 help about_Functions_Advanced_Parameters 可以查看所有属性和示例。

访问公用参数

Shell 定义了多个由所有 Cmdlet 共享的公用参数。其中一个公用参数是 -verbose,用于告知 Cmdlet 输出比平时更多的关于正在执行的操作的信息。但是,以下函数定义将导致错误:

function Test-Something {
    [CmdletBinding()]
    param (
        [switch]$verbose
    )
    PROCESS {
    }
}

这是因为您不能重新定义某个公用参数,如 -verbose。那么,如何知道运行的函数中是否包含 -verbose 呢?事实证明,这完全没有必要。Windows PowerShell 会为您跟踪该参数。您只需调用 Write-Verbose,如果 -verbose 未使用,Windows PowerShell 将忽略这些调用:

function Test-Something {
    PROCESS {
        Write-Verbose "Starting cmdlet"
    }
}

test-something –verbose

确认影响

另一对公用参数是 -whatif 和 -confirm。对计算机进行某种更改的任何 Cmdlet 都应该识别这两个参数。这对参数使您可以选择是让 Cmdlet 显示通常执行的操作 (-whatif),还是让 Cmdlet 分别确认每个操作 (-confirm)。这些参数统称为 ShouldProcess,您可以声明一个支持它们的函数,如下所示:

function Delete-Things {
    [CmdletBinding(
        SupportsShouldProcess=$true,
        ConfirmImpact="Medium"
    )]
    PROCESS {
    }
}

此声明启用 -whatif 和 -confirm 作为您函数的参数。此外,还指定您的函数对操作系统的影响级别为“Medium”。对于“Medium”的含义并没有严格的指导原则 - 我认为它是指不太可能造成完全的灾难。真实情况是 Shell 的 $ConfirmPreference 变量默认设置为“High”。当 Cmdlet 的影响小于 $ConfirmPreference 时,则 Cmdlet 运行时将不会进行确认,除非指定了 -whatif 或 –confirm。

如果 Cmdlet 的影响与 $ConfirmPreference 相同或者前者大于后者,则每次运行 Cmdlet 时,其行为将像指定了 –confirm 一样,即使您忘记了指定亦如此。因此,如果您的函数将执行确实很危险 的操作,请将 ConfirmImpact 指定为“High”,以便 Cmdlet 始终请求确认。您还可以选择“None”和“Low”。

实际上,Shell 的内置帮助不会向您说明如何要求确认 - 而这不是自动进行的。内置帮助建议您参考 MSDN 联机帮助,而后者是面向 Microsoft .NET Framework 开发人员的,根本不涉及 Shell 的脚本编写语言。因此,我将在这里告诉您如何要求确认:

function Delete-Things {
    [CmdletBinding(
        SupportsShouldProcess=$true,
        ConfirmImpact="High"
    )]
    Param ($param1)
    PROCESS {
        if ($pscmdlet.ShouldProcess($param1)) {
            Write "Deleting..."
        }
    }
}

Delete-Things "organizationalunit"

$pscmdlet 是一个内置变量,您可以在 PROCESS 脚本块中使用它来访问 Cmdlet 级别的功能,包括 ShouldProcess 方法。您传送关于要修改内容的说明后,Shell 将负责显示实际确认消息或“假设分析”消息。

如果 ShouldProcess 返回 $True,则您可以继续。如果它返回 $False,则您不应执行准备要执行的任何操作。了解 $pscmdlet 变量后,将更容易理解那些 MSDN 开发人员文档。这些文档准确地描述了 ShouldProcess 及其配对方法(如 ShouldContinue)的不同用法。

求助!求助!求助!

切记,函数(甚至高级函数)可以将自身的内置帮助包含在特殊格式的注释中,具体内容请参见我在 2010 年 3 月的专栏中的说明。通常,我会首先列出基于注释的帮助,接着列出 CmdletBinding 语句和参数,最后列出 BEGIN{}、PROCESS{} 和 END{} 脚本块。在函数中包含帮助始终 是一个好主意,您永远不知道谁可能会从中受益。

如果您以前编写过管道函数(也称为“筛选函数”),则您已经了解编写“脚本 Cmdlet”需要了解的所有其他事项。您的代码包含在 PROCESS{} 脚本块中,并且它将对流入您的 Cmdlet 的每个对象执行一次。关于这些高级函数的所有其他信息也像比它们稍微简单的对应函数一样。

Windows PowerShell v2 现已推出

尽管它预装在 Windows Server 2008 R2 和 Windows 7 中,但 Windows PowerShell v2 及其附带的 Management Framework 组件现在已经可以在 Windows XP、Windows Server 2003、Windows Vista 和 Windows Server 2008 上使用。请访问 support.microsoft.com/kb/968929,以便获取针对您使用的任何操作系统的下载链接。这应该与您的 v1 脚本兼容;因此,我将来的所有专栏将假设您使用的是 2.0。

广泛的目标读者

Windows PowerShell 团队确实引以为豪的是使 Windows PowerShell 适用于拥有不同技能水平的大量目标读者。高级函数毫无疑问就是只有高级 读者才能体会到其用处的内容示例。

如果您是刚刚接触 Shell,还需要时刻提醒自己运行帮助,那么高级函数可能要将来才用得上。您甚至不需要编写高级函数,就可以成功使用 Shell。当您开始变得更专业并且开始编写可重用的组件时,您会发现高级函数是一个不错的选择。

博客文章此处即是此方面的一个极好示例:首先显示的是一个可完成重要任务的简单命令 - 一个任何管理员都可能会编写的命令。然后,作者逐渐将其命令的功能扩展为一个函数,接着扩展为筛选函数,最后扩展为高级函数,这显示了 Shell 如何随着您需求的增加和技能的提升而扩展。

Don Jones是 Concentrated Technology 的创始人,他会在ConcentratedTech.com 解答有关 Windows PowerShell 和其他技术的问题。他还是Nexus.Realtimepublishers.com的撰稿人,并且他的许多著作还在此网站上以电子版的形式提供。

相关内容

     Windows PowerShell:PowerShell 和 Active Directory

     Windows PowerShell:筛选与格式“两头难”

     Windows PowerShell:始终关注