从XML填充参数列表

时间:2014-04-10 16:31:49

标签: xml powershell

我需要将一个ID列表拉入PowerShell参数验证集,如下所示:

function Do-Stuff {
   [Cmdletbinding()] 
   param(
        [ValidateSet("Seattle","NewYork","London","Atlanta" )]
        [String]$Site
    )

我不想手动指定集合中的城市,而是从已经列出所有内容的现有xml文档中提取。加载后,站点名称将显示在$ xml.var.sites.id中。这是可能的,也许更重要的是,这是一个好主意吗?

3 个答案:

答案 0 :(得分:11)

最近的一个项目不得不这样做。不知道这是多么容易用枚举,谢谢mjolinor!

另一种方法是使用动态参数。使用Get-Help查找帮助:

#Look for the Dynamic Parameters section in here
Get-Help about_Functions_Advanced_Parameters

其他资源:

当前功能定义:

Function New-DynamicParam {
<#
    .SYNOPSIS
        Helper function to simplify creating dynamic parameters

    .DESCRIPTION
        Helper function to simplify creating dynamic parameters

        Example use cases:
            Include parameters only if your environment dictates it
            Include parameters depending on the value of a user-specified parameter
            Provide tab completion and intellisense for parameters, depending on the environment

        Please keep in mind that all dynamic parameters you create will not have corresponding variables created.
           One of the examples illustrates a generic method for populating appropriate variables from dynamic parameters
           Alternatively, manually reference $PSBoundParameters for the dynamic parameter value

    .NOTES
        Credit to http://jrich523.wordpress.com/2013/05/30/powershell-simple-way-to-add-dynamic-parameters-to-advanced-function/
            Added logic to make option set optional
            Added logic to add RuntimeDefinedParameter to existing DPDictionary
            Added a little comment based help

    .PARAMETER Name
        Name of the dynamic parameter

    .PARAMETER ValidateSet
        If specified, set the ValidateSet attribute of this dynamic parameter

    .PARAMETER Mandatory
        If specified, set the Mandatory attribute for this dynamic parameter

    .PARAMETER ParameterSetName
        If specified, set the ParameterSet attribute for this dynamic parameter

    .PARAMETER Position
        If specified, set the Position attribute for this dynamic parameter

    .PARAMETER ValueFromPipelineByPropertyName
        If specified, set the ValueFromPipelineByPropertyName attribute for this dynamic parameter

    .PARAMETER HelpMessage
        If specified, set the HelpMessage for this dynamic parameter

    .PARAMETER DPDictionary
        If specified, add resulting RuntimeDefinedParameter to an existing RuntimeDefinedParameterDictionary (appropriate for multiple dynamic parameters)
        If not specified, create and return a RuntimeDefinedParameterDictionary (appropriate for a single dynamic parameter)

    .EXAMPLE

        function Show-Free
        {
            [CmdletBinding()]
            Param()
            DynamicParam {
                $options = @( gwmi win32_volume | %{$_.driveletter} | sort )
                New-DynamicParam -Name Drive -ValidateSet $options -Position 0 -Mandatory
            }
            begin{
                #have to manually populate
                $drive = $PSBoundParameters.drive
            }
            process{
                $vol = gwmi win32_volume -Filter "driveletter='$drive'"
                "{0:N2}% free on {1}" -f ($vol.Capacity / $vol.FreeSpace),$drive
            }
        } #Show-Free

        Show-Free -Drive <tab>

    # This example illustrates the use of New-DynamicParam to create a single dynamic parameter
    # The Drive parameter ValidateSet populates with all available volumes on the computer for handy tab completion / intellisense

    .EXAMPLE

    # I found many cases where I needed to add many dynamic parameters
    # The DPDictionary parameter lets you specify an existing dictionary
    # The block of code in the Begin block loops through bound parameters and defines variables if they don't exist

        Function Test-DynPar{
            [cmdletbinding()]
            param(
                [string[]]$x = $Null
            )
            DynamicParam
            {
                #Create the RuntimeDefinedParameterDictionary
                $Dictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary

                New-DynamicParam -Name AlwaysParam -options @( gwmi win32_volume | %{$_.driveletter} | sort ) -DPDictionary $Dictionary

                #Add dynamic parameters to $dictionary
                if($x -eq 1)
                {
                    New-DynamicParam -Name X1Param1 -Options 1,2 -mandatory -DPDictionary $Dictionary
                    New-DynamicParam -Name X1Param2 -DPDictionary $Dictionary
                    New-DynamicParam -Name X3Param3 -DPDictionary $Dictionary
                }
                else
                {
                    New-DynamicParam -Name OtherParam1 -mandatory -DPDictionary $Dictionary
                    New-DynamicParam -Name OtherParam2 -DPDictionary $Dictionary
                    New-DynamicParam -Name OtherParam3 -DPDictionary $Dictionary
                }

                #return RuntimeDefinedParameterDictionary
                $Dictionary
            }
            Begin
            {
                #This standard block of code loops through bound parameters...
                #If no corresponding variable exists, one is created
                    foreach($param in $PSBoundParameters.Keys)
                    {
                        if (-not ( Get-Variable -name $param -scope 0 -ErrorAction SilentlyContinue ) )
                        {
                            New-Variable -Name $Param -Value $PSBoundParameters.$param
                            Write-Verbose "Adding variable for dynamic parameter '$param' with value '$($PSBoundParameters.$param)'"
                        }
                    }

                #Appropriate variables should now be defined and accessible
                    Get-Variable -scope 0
            }
        }

    # This example illustrates the creation of many dynamic parameters using New-DynamicParam
        # You must create a RuntimeDefinedParameterDictionary object ($dictionary here)
        # To each New-DynamicParam call, add the -DPDictionary parameter pointing to this RuntimeDefinedParameterDictionary
        # At the end of the DynamicParam block, return the RuntimeDefinedParameterDictionary
        # Initialize all bound parameters using the provided block or similar code
#>
param(

    [string]
    $Name,

    [string[]]
    $ValidateSet,

    [switch]
    $Mandatory,

    [string]
    $ParameterSetName="__AllParameterSets",

    [int]
    $Position,

    [switch]
    $ValueFromPipelineByPropertyName,

    [string]
    $HelpMessage,

    [validatescript({
        if(-not ( $_ -is [System.Management.Automation.RuntimeDefinedParameterDictionary] -or -not $_) )
        {
            Throw "DPDictionary must be a System.Management.Automation.RuntimeDefinedParameterDictionary object, or not exist"
        }
        $True
    })]
    $DPDictionary = $false

)
    #Create attribute object, add attributes, add to collection   
        $ParamAttr = New-Object System.Management.Automation.ParameterAttribute
        $ParamAttr.ParameterSetName = $ParameterSetName
        if($mandatory)
        {
            $ParamAttr.Mandatory = $True
        }
        if($Position -ne $null)
        {
            $ParamAttr.Position=$Position
        }
        if($ValueFromPipelineByPropertyName)
        {
            $ParamAttr.ValueFromPipelineByPropertyName = $True
        }
        if($HelpMessage)
        {
            $ParamAttr.HelpMessage = $HelpMessage
        }

        $AttributeCollection = New-Object 'Collections.ObjectModel.Collection[System.Attribute]'
        $AttributeCollection.Add($ParamAttr)

    #param validation set if specified
        if($ValidateSet)
        {
            $ParamOptions = New-Object System.Management.Automation.ValidateSetAttribute -ArgumentList $ValidateSet
            $AttributeCollection.Add($ParamOptions)
        }


    #Create the dynamic parameter
        $Parameter = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameter -ArgumentList @($Name, [string], $AttributeCollection)

    #Add the dynamic parameter to an existing dynamic parameter dictionary, or create the dictionary and add it
        if($DPDictionary)
        {
            $DPDictionary.Add($Name, $Parameter)
        }
        else
        {
            $Dictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
            $Dictionary.Add($Name, $Parameter)
            $Dictionary
        }
}

使用此功能的示例:

#Function has already been added to this session...
function Do-Stuff {
    [cmdletbinding()] 
    param()
    DynamicParam
    {
        #Example borrowing from TheMadTechnician: 
        #New-DynamicParam -Name Site -ValidateSet $(([xml](gc c:\temp\config.xml)).getElementsByTagName("City").Name) -Mandatory

        #I don't have that file... simplification
        New-DynamicParam -Name Site -ValidateSet "Seattle", "NewYork", "London", "Atlanta" -Mandatory
    }
    Begin
    {
        #This standard block of code loops through bound parameters...
        #If no corresponding variable exists, one is created
            foreach($param in $PSBoundParameters.Keys)
            {
                if (-not ( Get-Variable -name $param -scope 0 -ErrorAction SilentlyContinue ) )
                {
                    New-Variable -Name $param -Value $PSBoundParameters.$param
                    Write-Verbose "Adding variable for dynamic parameter '$param' with value '$($PSBoundParameters.$param)'"
                }
            }
        $Site
    }    
}

最后,最终结果:

Example illustrating IntelliSense with DynamicParam

还有一些其他动态参数的例子

Function New-LabMachine
{
    [cmdletbinding()]
    param(
        [ValidateSet("ADDSNew","ADDSExisting")]
        [string[]]
        $Role
    )
    DynamicParam
    {
        #Define dynamicparam dictionary.  Create a hashtable for splatting params
            $Dictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
            $dict = @{DPDictionary = $Dictionary}

        #Add dynamic parameters to populate and validate Lab names
            $Labs = "Lab1", "Lab2" #@( Get-Lab | Select -ExpandProperty LabName -ErrorAction SilentlyContinue )
            New-DynamicParam -Name LabName -Mandatory -ValidateSet $Labs @dict

        if($Role -contains 'ADDSNew')
        {            
            #AD Forest info
                New-DynamicParam -Name DomainName -Mandatory @dict -HelpMessage "Provide domain name for first domain in forest"
                New-DynamicParam -Name ForestMode -Mandatory -ValidateSet "Win2008","Win2008R2","Win2012","Win2012R2" @dict
        }
        if($Role -contains 'ADDSExisting')
        {
            New-DynamicParam -Name DomainName -Mandatory @dict
            New-DynamicParam -Name username -Mandatory @dict
            New-DynamicParam -Name password -Mandatory @dict
        }

        #Return the dictionary for dynamic params
        $Dictionary
    }
    Begin
    {
        #This standard block of code loops through bound parameters...
        #If no corresponding variable exists, one is created
            foreach($param in $PSBoundParameters.Keys )
            {
                if (-not ( Get-Variable -name $param -scope 0 -ErrorAction SilentlyContinue ) -and "Verbose", "Debug" -notcontains $param )
                {
                    New-Variable -Name $Param -Value $PSBoundParameters.$param -Description DynParam
                    Write-Verbose "Adding variable for dynamic parameter '$param' with value '$($PSBoundParameters.$param)'"
                }
            }

        #Display the bound parameters      
        $PSBoundParameters.keys | ForEach-Object {Get-Variable -Name $_}
    }
}

结果:

enter image description here

从我的角度来看,这些对最终用户非常有帮助。我通常使用它们来提供类似于您的目标的IntelliSense和制表符完成支持。只要它们为您提供的价值超过其轻微的开销和一点额外的复杂性,它们就值得:)

我对文字墙道歉!干杯!

答案 1 :(得分:4)

如果您的城市名称只包含字母数字字符,那么从名称中创建自定义枚举类型可能是更清晰的解决方案,并将参数值指定为该类型:

Add-Type -TypeDefinition @"
   // very simple enum type
   public enum ValidSites
   {
      Seattle,
      NewYork,
      London,
      Atlanta
   }
"@

function Do-Stuff {
   [Cmdletbinding()] 
   param( [ValidSites]$Site )
}

它应该与使用ValidateSet具有相同的效果(包括启用可能值的选项卡完成),而不必修改函数本身中的列表集。

答案 2 :(得分:1)

或者,如果您要使用文件进行验证,可以使用ValidateScript代替ValidateSet,例如:

function Do-Stuff {
   [Cmdletbinding()] 
   param(
        [ValidateScript({if(([xml](gc c:\temp\config.xml)).getElementsByTagName("City").Name -contains $_){$true}else{throw ([xml](gc c:\temp\config.xml)).getElementsByTagName("City").Name}} )]
        [String]$Site
    )

这基于简单易懂的XML文件:

<?xml version="1.0"?>
<City_Validation_List>
    <City Name="Seattle"></City>
    <City Name="Atlanta"></City>
</City_Validation_List>

因此,无论如何,您可以使用ValidateScript运行脚本块作为验证,您可以在其中从XML文件加载内容。另一方面,ValidateSet必须有一个预定义的字符串数组来进行验证。