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

[玩转系统] 生成PowerShell动态参数代码

作者:精品下载站 日期:2024-12-14 08:04:38 浏览:16 分类:玩电脑

生成PowerShell动态参数代码


我们在 PowerShell Cmdlet 工作组中讨论的主题之一是请求更轻松地插入动态参数。我对此有点纠结。一方面,我看到了动态参数的价值。这些参数仅在满足某些条件时才存在,例如当前位置位于 Windows 注册表中。缺点是这些参数很难发现并且难以记录。最重要的是,定义动态参数所需的 PowerShell 代码很复杂,绝对不适合初学者。我认为这就是问题的真正意义所在。因此,我决定编写自己的工具,以便更轻松地插入动态参数。

定义需求

第一步是确定为什么需要动态参数。很多时候,我认为参数集可能会解决问题。但假设您需要一个基于某些条件的参数。这是一个例子。这是使用 Out-Gridview 显示驱动器使用情况的演示功能。

Function Get-DriveGridView {
    [cmdletbinding(DefaultParameterSetName = "computer")]
    [OutputType("none","object")]
    Param(
        [parameter(Position = 0, ValueFromPipeline, ParameterSetName = "computer")]
        [ValidateNotNullOrEmpty()]
        [string]$Computername = $env:COMPUTERNAME,

        [parameter(ValueFromPipeline, ParameterSetName = "session")]
        [ValidateNotNullOrEmpty()]
        [Microsoft.Management.Infrastructure.CimSession]$CimSession,

        [Parameter(HelpMessage = "Enter a title to use for the GridView")]
        [ValidateNotNullOrEmpty()]
        [string]$Title = "Drive Report",

        [Parameter(HelpMessage = "pass results to the pipeline in addition to the grid view")]
        [switch]$Passthru
    )

    Begin {
        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN  ] Starting $($myinvocation.mycommand)"
        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN  ] Collecting drive information...please wait"

        #initialize a list to hold the results
        $results = [System.Collections.Generic.list[object]]::new()

        #hashtable of Get-CimInstance parameters for splatting
        $splat = @{
            Classname   = "win32_logicaldisk"
            Filter      = "drivetype=3"
            ErrorAction = "Stop"
            Property    = "SystemName", "DeviceID", "Size", "Freespace"
        }
    } #begin

    Process {
        If ($pscmdlet.ParameterSetName -eq 'computer') {
            Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Getting data from computer $($computername.toUpper())"
            $splat["Computername"] = $Computername
            $remote = $Computername.ToUpper()
        }
        else {
            Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Getting data from cimsession $($cimsession.computername.toUpper())"
            $splat["CimSession"] = $cimSession
            $remote = $cimSession.computername.toUpper()
        }

        Try {
            Get-CimInstance @splat | Select-Object -Property @{Name = "Computername"; Expression = { $_.Systemname } },
            @{Name = "Drive"; Expression = { $_.DeviceID } },
            @{Name = "SizeGB"; Expression = { [int]($_.Size / 1GB) } },
            @{Name = "FreeGB"; Expression = { [int]($_.Freespace / 1GB) } },
            @{Name = "UsedGB"; Expression = { [math]::round(($_.size - $_.Freespace) / 1GB, 2) } },
            @{Name = "Free%"; Expression = { [math]::round(($_.Freespace / $_.Size) * 100, 2) } },
            @{Name = "FreeGraph"; Expression = {
            [int]$per = (($_.Freespace / $_.Size) * 100/2)
            "|" * $per }
            } | ForEach-Object { $results.Add($_) }
        } #try
        Catch {
            Write-Warning "Failed to get drive data from $remote. $($_.exception.message)"
        }
    } #process

    End {
        #send the results to Out-Gridview
        Write-Verbose "[$((Get-Date).TimeofDay) END    ] Found $($results.count) total items"
        if ($results.count -gt 1) {
               $Results | Sort-Object -Property Computername | Out-GridView -Title $Title
               if ($passthru) {
                $results
               }
        }
        else {
            Write-Warning "No drive data found to report."
        }
        Write-Verbose "[$((Get-Date).TimeofDay) END    ] Ending $($myinvocation.mycommand)"
    } #end
}

这很好用。

[玩转系统] 生成PowerShell动态参数代码

但是,PowerShell 7 有一个名为 Out-ConsoleGridView 的新命令。从 PowerShell 库安装 Microsoft.PowerShell.ConsoleGuiTools 模块以获取此命令。我想包含一个动态参数来使用此命令而不是 Out-Gridview(如果可用)。这意味着我必须编写 PowerShell 代码来定义此参数。

    [cmdletbinding(DefaultParameterSetName = "computer")]
    [OutputType("none","object")]
    Param(
        [parameter(Position = 0, ValueFromPipeline, ParameterSetName = "computer")]
        [ValidateNotNullOrEmpty()]
        [string]$Computername = $env:COMPUTERNAME,

        [parameter(ValueFromPipeline, ParameterSetName = "session")]
        [ValidateNotNullOrEmpty()]
        [Microsoft.Management.Infrastructure.CimSession]$CimSession,

        [Parameter(HelpMessage = "Enter a title to use for the GridView")]
        [ValidateNotNullOrEmpty()]
        [string]$Title = "Drive Report",

        [Parameter(HelpMessage = "pass results to the pipeline in addition to the grid view")]
        [switch]$Passthru
    )
    DynamicParam {
        #offer to use Out-ConsoleGridView if installed in PowerShell 7
        ...

这是乏味的部分。

新-PSDynamicParameter

相反,我将使用此函数自动生成 PowerShell 代码。

Function New-PSDynamicParameter {
<#
.Synopsis
Create a PowerShell dynamic parameter
.Description
This command will create the code for a dynamic parameter that you can insert into your PowerShell script file.
.Link
about_Functions_Advanced_Parameters

#>

    [cmdletbinding()]
    [alias("ndp")]
    [outputtype([System.String[]])]
    Param(
        [Parameter(Position = 0, Mandatory, HelpMessage = "Enter the name of your dynamic parameter.`nThis is a required value.")]
        [ValidateNotNullOrEmpty()]
        [alias("Name")]
        [string[]]$ParameterName,
        [Parameter(Mandatory, HelpMessage = "Enter an expression that evaluates to True or False.`nThis is code that will go inside an IF statement.`nIf using variables, wrap this in single quotes.`nYou can also enter a placeholder like '`$True' and edit it later.`nThis is a required value.")]
        [ValidateNotNullOrEmpty()]
        [string]$Condition,
        [Parameter(HelpMessage = "Is this dynamic parameter mandatory?")]
        [switch]$Mandatory,
        [Parameter(HelpMessage = "Enter an optional default value.")]
        [object[]]$DefaultValue,
        [Parameter(HelpMessage = "Enter an optional parameter alias.`nSpecify multiple aliases separated by commas.")]
        [string[]]$Alias,
        [Parameter(HelpMessage = "Enter the parameter value type such as String or Int32.`nUse a value like string[] to indicate an array.")]
        [type]$ParameterType = "string",
        [Parameter(HelpMessage = "Enter an optional help message.")]
        [ValidateNotNullOrEmpty()]
        [string]$HelpMessage,
        [Parameter(HelpMessage = "Does this dynamic parameter take pipeline input by property name?")]
        [switch]$ValueFromPipelineByPropertyName,
        [Parameter(HelpMessage = "Enter an optional parameter set name.")]
        [ValidateNotNullOrEmpty()]
        [string]$ParameterSetName,
        [Parameter(HelpMessage = "Enter an optional comment for your dynamic parameter.`nIt will be inserted into your code as a comment.")]
        [ValidateNotNullOrEmpty()]
        [string]$Comment,
        [Parameter(HelpMessage = "Validate that the parameter is not NULL or empty.")]
        [switch]$ValidateNotNullOrEmpty,
        [Parameter(HelpMessage = "Enter a minimum and maximum string length for this parameter value`nas an array of comma-separated set values.")]
        [ValidateNotNullOrEmpty()]
        [int[]]$ValidateLength,
        [Parameter(HelpMessage = "Enter a set of parameter validations values")]
        [ValidateNotNullOrEmpty()]
        [object[]]$ValidateSet,
        [Parameter(HelpMessage = "Enter a set of parameter range validations values as a`ncomma-separated list from minimum to maximum")]
        [ValidateNotNullOrEmpty()]
        [int[]]$ValidateRange,
        [Parameter(HelpMessage = "Enter a set of parameter count validations values as a`ncomma-separated list from minimum to maximum")]
        [ValidateNotNullOrEmpty()]
        [int[]]$ValidateCount,
        [Parameter(HelpMessage = "Enter a parameter validation regular expression pattern")]
        [ValidateNotNullOrEmpty()]
        [string]$ValidatePattern,
        [Parameter(HelpMessage = "Enter a parameter validation scriptblock.`nIf using the form, enter the scriptblock text.")]
        [ValidateNotNullOrEmpty()]
        [scriptblock]$ValidateScript
    )

    Begin {
        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN  ] Starting $($myinvocation.mycommand)"
        $out = @"
    DynamicParam {
    $(If ($comment) { "$([char]35) $comment"})
        If ($Condition) {

        `$paramDictionary = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary

"@

    } #begin

    Process {
        if (-Not $($PSBoundParameters.ContainsKey("ParameterSetName"))) {
            $PSBoundParameters.Add("ParameterSetName", "__AllParameterSets")
        }

        #get validation tests
        $Validations = $PSBoundParameters.GetEnumerator().Where({ $_.key -match "^Validate" })

        #this is structured for future development where you might need to create
        #multiple dynamic parameters. This feature is incomplete at this time
        Foreach ($Name in $ParameterName) {
            Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Defining dynamic parameter $Name [$($parametertype.name)]"
            $out += "`n        # Defining parameter attributes`n"
            $out += "        `$attributeCollection = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute]`n"
            $out += "        `$attributes = New-Object System.Management.Automation.ParameterAttribute`n"
            #add attributes
            $attributeProperties = 'ParameterSetName', 'Mandatory', 'ValueFromPipeline', 'ValueFromPipelineByPropertyName', 'ValueFromRemainingArguments', 'HelpMessage'
            foreach ($item in $attributeProperties) {
                if ($PSBoundParameters.ContainsKey($item)) {
                    Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Defining $item"
                    if ( $PSBoundParameters[$item] -is [string]) {
                        $value = "'$($PSBoundParameters[$item])'"
                    }
                    else {
                        $value = "`$$($PSBoundParameters[$item])"
                    }

                    $out += "        `$attributes.$item = $value`n"
                }
            }

            #add parameter validations
            if ($validations) {
                Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Processing validations"
                foreach ($validation in $Validations) {
                    Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] ... $($validation.key)"
                    $out += "`n        # Adding $($validation.key) parameter validation`n"
                    Switch ($Validation.key) {
                        "ValidateNotNullOrEmpty" {
                            $out += "        `$v = New-Object System.Management.Automation.ValidateNotNullOrEmptyAttribute`n"
                            $out += "        `$AttributeCollection.Add(`$v)`n"
                        }
                        "ValidateLength" {
                            $out += "        `$value = @($($Validation.Value[0]),$($Validation.Value[1]))`n"
                            $out += "        `$v = New-Object System.Management.Automation.ValidateLengthAttribute(`$value)`n"
                            $out += "        `$AttributeCollection.Add(`$v)`n"
                        }
                        "ValidateSet" {
                            $join = "'$($Validation.Value -join "','")'"
                            $out += "        `$value = @($join) `n"
                            $out += "        `$v = New-Object System.Management.Automation.ValidateSetAttribute(`$value)`n"
                            $out += "        `$AttributeCollection.Add(`$v)`n"
                        }
                        "ValidateRange" {
                            $out += "        `$value = @($($Validation.Value[0]),$($Validation.Value[1]))`n"
                            $out += "        `$v = New-Object System.Management.Automation.ValidateRangeAttribute(`$value)`n"
                            $out += "        `$AttributeCollection.Add(`$v)`n"
                        }
                        "ValidatePattern" {
                            $out += "        `$value = '$($Validation.value)'`n"
                            $out += "        `$v = New-Object System.Management.Automation.ValidatePatternAttribute(`$value)`n"
                            $out += "        `$AttributeCollection.Add(`$v)`n"
                        }
                        "ValidateScript" {
                            $out += "        `$value = {$($Validation.value)}`n"
                            $out += "        `$v = New-Object System.Management.Automation.ValidateScriptAttribute(`$value)`n"
                            $out += "        `$AttributeCollection.Add(`$v)`n"
                        }
                        "ValidateCount" {
                            $out += "        `$value = @($($Validation.Value[0]),$($Validation.Value[1]))`n"
                            $out += "        `$v = New-Object System.Management.Automation.ValidateCountAttribute(`$value)`n"
                            $out += "        `$AttributeCollection.Add(`$v)`n"
                        }
                    } #close switch
                } #foreach validation
            } #validations

            $out += "        `$attributeCollection.Add(`$attributes)`n"

            if ($Alias) {
                Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Adding parameter alias $($alias -join ',')"
                Foreach ($item in $alias) {
                    $out += "`n        # Adding a parameter alias`n"
                    $out += "        `$dynalias = New-Object System.Management.Automation.AliasAttribute -ArgumentList '$Item'`n"
                    $out += "        `$attributeCollection.Add(`$dynalias)`n"
                }
            }

            $out += "`n        # Defining the runtime parameter`n"

            #handle the Switch parameter since it uses a slightly different name
            if ($ParameterType.Name -match "Switch") {
                $paramType = "Switch"
            }
            else {
                $paramType = $ParameterType.Name
            }

            $out += "        `$dynParam1 = New-Object -Type System.Management.Automation.RuntimeDefinedParameter('$Name', [$paramType], `$attributeCollection)`n"
            if ($DefaultValue) {
                Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Using default value $($DefaultValue)"
                if ( $DefaultValue[0] -is [string]) {
                    $value = "'$($DefaultValue)'"
                }
                else {
                    $value = "`$$($DefaultValue)"
                }
                $out += "        `$dynParam1.Value = $value`n"
            }
            $Out += @"
        `$paramDictionary.Add('$Name', `$dynParam1)


"@
        } #foreach dynamic parameter name

    }
    End {
        $out += @"
        return `$paramDictionary
    } # end if
} #end DynamicParam
"@
        $out
        Write-Verbose "[$((Get-Date).TimeofDay) END    ] Ending $($myinvocation.mycommand)"
    } #end
}

此函数参数化您可能想要在动态参数中使用的信息。我不会关注该函数如何工作,而是关注它的使用。参数名称和条件是必需的。条件应该是在 IF 语句内运行的代码。如果您知道需要更复杂的东西,请使用像 $True 这样的参数值。您可以稍后编辑生成的代码。

$splat = @{
 ParameterName = "ConsoleGridView"
 Condition = "Get-Command -Name Out-ConsoleGridview -ErrorAction SilentlyContinue"
 Alias = "ocgv"
 HelpMessage = "Use the Out-ConsoleGridView command in PowerShell 7"
 Comment = "Offer to use Out-ConsoleGridView if installed in PowerShell 7"
 ParameterType = "switch"
}
New-PSDynamicParameter @splat | Set-Clipboard

该函数将 PowerShell 代码的此处字符串写入管道。我正在将其复制到剪贴板。

    DynamicParam {
    # Offer to use Out-ConsoleGridView if installed in PowerShell 7
        If (Get-Command -Name Out-ConsoleGridview -ErrorAction SilentlyContinue) {

        $paramDictionary = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary

        # Defining parameter attributes
        $attributeCollection = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute]
        $attributes = New-Object System.Management.Automation.ParameterAttribute
        $attributes.ParameterSetName = '__AllParameterSets'
        $attributes.HelpMessage = 'Use the Out-ConsoleGridView command in PowerShell 7'
        $attributeCollection.Add($attributes)

        # Adding a parameter alias
        $dynalias = New-Object System.Management.Automation.AliasAttribute -ArgumentList 'ocgv'
        $attributeCollection.Add($dynalias)

        # Defining the runtime parameter
        $dynParam1 = New-Object -Type System.Management.Automation.RuntimeDefinedParameter('ConsoleGridView', [Switch], $attributeCollection)
        $paramDictionary.Add('ConsoleGridView', $dynParam1)

        return $paramDictionary
    } # end if
} #end DynamicParam

我可以将其插入到 Param() 部分之后。我不必编写任何代码。当我在 PowerShell 7 中获取函数时,我有一个附加参数。

[玩转系统] 生成PowerShell动态参数代码

因为动态参数不依赖于任何其他参数,而且我让 PowerShell 生成帮助,所以这还不错。但在具有外部帮助的模块中,这变得更难以记录。但即使使用动态参数别名,它也能工作。

Get-DriveGridView -Computername thinkp1 -ocgv

[玩转系统] 生成PowerShell动态参数代码

获取图形用户界面

但由于我可能会使用此函数在脚本编辑器中生成动态参数,因此 GUI 可能会更好。此函数生成第一个函数的 WPF 前端。

Function New-PSDynamicParameterForm {
    [cmdletbinding()]
    [alias("dpf")]
    [Outputtype("None")]
    Param()

    Add-Type -AssemblyName PresentationFramework
    Add-Type -AssemblyName PresentationCore

    $list = [System.Collections.Generic.list[object]]::new()
    [xml]$xaml = @"
    <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:New_DynamicParamForm"
    Title="New Dynamic Parameter" Height="475" Width="650" WindowStartupLocation = "CenterScreen" >
    <Grid HorizontalAlignment="Center" Width="650" Margin="0,0,0,0">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="0*"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Label x:Name="label" Content="Parameter Name*" HorizontalAlignment="Left" Height="25" Margin="9,22,0,0" VerticalAlignment="Top" Width="116" Grid.Column="1"/>
        <TextBox x:Name="ParameterName" Grid.ColumnSpan="2" HorizontalAlignment="Left" Height="20" Margin="120,25,0,0" Text="" TextWrapping="Wrap" VerticalAlignment="Top" Width="153"/>
        <CheckBox x:Name="Mandatory" Content="Mandatory" HorizontalAlignment="Center" Margin="0,27,0,0" VerticalAlignment="Top" Grid.Column="1"/>
        <Label x:Name="label_Copy" Content="Parameter Set Name" HorizontalAlignment="Left" Height="25" Margin="307,44,0,0" VerticalAlignment="Top" Width="121" Grid.Column="1"/>
        <TextBox x:Name="ParameterSetName" Grid.ColumnSpan="2" HorizontalAlignment="Left" Height="20" Margin="443,46,0,0" Text="" TextWrapping="Wrap" VerticalAlignment="Top" Width="153"/>
        <Label x:Name="label1" Content="Comment" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="18,392,0,0" VerticalAlignment="Top"/>
        <TextBox x:Name="Comment" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="87,396,0,0" Text="" TextWrapping="Wrap" VerticalAlignment="Top" Width="345"/>
        <Label x:Name="label2" Content="Parameter Alias" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="11,51,0,0" VerticalAlignment="Top"/>
        <TextBox x:Name="Alias" Grid.ColumnSpan="2" HorizontalAlignment="Left" Height="20" Margin="121,54,0,0" Text="" TextWrapping="Wrap" VerticalAlignment="Top" Width="153"/>
        <Label x:Name="label2_Copy" Content="Default Value" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="11,78,0,0" VerticalAlignment="Top"/>
        <TextBox x:Name="DefaultValue" Grid.ColumnSpan="2" HorizontalAlignment="Left" Height="20" Margin="121,81,0,0" Text="" TextWrapping="Wrap" VerticalAlignment="Top" Width="153"/>
        <Button x:Name="OK" Content="_Create" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="448,394,0,0" VerticalAlignment="Top" Width="75"/>
        <Button x:Name="Quit" Content="Close" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="536,394,0,0" VerticalAlignment="Top" Width="75"/>
        <Label x:Name="label2_Copy1" Content="Help Message" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="281,78,0,0" VerticalAlignment="Top"/>
        <TextBox x:Name="HelpMessage" HorizontalAlignment="Left" Height="20" Margin="376,81,0,0" Text="" TextWrapping="Wrap" VerticalAlignment="Top" Width="222" Grid.Column="1"/>
        <Label x:Name="label2_Copy2" Content="Parameter Type" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="11,105,0,0" VerticalAlignment="Top"/>
        <TextBox x:Name="ParameterType" Grid.ColumnSpan="2" HorizontalAlignment="Left" Height="20" Margin="121,108,0,0" Text="string" TextWrapping="Wrap" VerticalAlignment="Top" Width="153"/>
        <Label x:Name="label3" Content="Condition*" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="13,132,0,0" VerticalAlignment="Top"/>
        <TextBox x:Name="Condition" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="123,137,0,0" Text="" TextWrapping="Wrap" VerticalAlignment="Top" Width="282"/>
        <Border BorderThickness="1" BorderBrush="Black" Grid.ColumnSpan="2" HorizontalAlignment="Left" Height="1" Margin="10,162,0,0" VerticalAlignment="Top" Width="634"/>
        <Label x:Name="label4" Content="Parameter Validations" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="7,166,0,0" VerticalAlignment="Top"/>
        <CheckBox x:Name="ValidateNotNullOrEmpty" Content="ValidateNotNullorEmpty" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="39,196,0,0" VerticalAlignment="Top"/>
        <Label x:Name="label5" Content="ValidateCount" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="35,212,0,0" VerticalAlignment="Top"/>
        <TextBox x:Name="ValidateCount" HorizontalAlignment="Left" Margin="124,216,0,0" Text="" TextWrapping="Wrap" VerticalAlignment="Top" Width="60" Grid.Column="1"/>
        <Label x:Name="label5_Copy" Content="ValidateLength" HorizontalAlignment="Left" Margin="210,212,0,0" VerticalAlignment="Top" Grid.Column="1"/>
        <TextBox x:Name="ValidateLength" HorizontalAlignment="Left" Margin="303,216,0,0" Text="" TextWrapping="Wrap" VerticalAlignment="Top" Width="60" Grid.Column="1"/>
        <CheckBox x:Name="ValueFromPipelineByPropertyName" Content="ValueFromPipelineByPropertyName" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="201,195,0,0" VerticalAlignment="Top"/>
        <Label x:Name="label6" Content="ValidateRange" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="394,212,0,0" VerticalAlignment="Top"/>
        <TextBox x:Name="ValidateRange" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="481,216,0,0" Text="" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
        <Label x:Name="label5_Copy1" Content="ValidatePattern" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="35,242,0,0" VerticalAlignment="Top"/>
        <TextBox x:Name="ValidatePattern" HorizontalAlignment="Left" Margin="125,245,0,0" Text="" TextWrapping="Wrap" VerticalAlignment="Top" Width="150" Grid.Column="1"/>
        <Label x:Name="label5_Copy2" Content="ValidateScript" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="35,270,0,0" VerticalAlignment="Top"/>
        <TextBox x:Name="ValidateScript" AcceptsReturn = "True" VerticalScrollBarVisibility="Auto" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="121,274,0,0" Text="" TextWrapping="Wrap" VerticalAlignment="Top" Width="250" Height="70"/>
        <Label x:Name="label7" Content="ValidateSet" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="34,351,0,0" VerticalAlignment="Top"/>
        <TextBox x:Name="ValidateSet" Grid.ColumnSpan="2" HorizontalAlignment="Left" Height="16" Margin="117,356,0,0" Text="" TextWrapping="Wrap" VerticalAlignment="Top" Width="328"/>
     </Grid>
  </Window>
"@

    $reader = New-Object System.Xml.XmlNodeReader $xaml
    $Window = [Windows.Markup.XamlReader]::Load($reader)

    #get all parameters
    $all = (Get-Command New-PSDynamicParameter).parameters
    #filter out common parameters
    $common = [System.Management.Automation.Cmdlet]::CommonParameters
    $paramList = $all.GetEnumerator().where({$common -notcontains $_.key}).key

    #get controls
    foreach ($item in $paramList) {
    Write-Verbose "Processing control $item"
        Try {
            $tmp = New-Variable -Name $item -Value ($Window.FindName($item)) -ErrorAction Stop -PassThru
            #add a help tool tip
            $tip = $all[$item].attributes.where({$_.typeid.name -eq 'parameterattribute'}).helpMessage
            write-Verbose "Found help $tip"
            $tmp.Value.ToolTip = $tip
            $list.Add((Get-Variable -Name $item))
        }
        Catch {
            Write-Verbose "Skipping $item"
        }
    }

    #hook up code to buttons
    $OK = $Window.FindName("OK")
    $OK.ToolTip = "Create the dynamic parameter code and copy to the clipboard.`nThis will NOT close the form."

    $OK.Add_Click({
        Write-Verbose "Defining dynamic parameter $($ParameterName.text)"

        $splat = @{}
        $list | where-object {$_.value.text} | foreach-object {
        $splat.Add($_.Name,$_.value.Text)
         }
         #add switches
        $list | where-object {$_.value.IsChecked} | foreach-object {
         $splat.Add($_.Name,$True)
        }

        #turn values into arrays as needed
        $Names = "ValidateSet","ValidateCount","ValidateRange","ValidateLength"
        foreach ($n in $names) {
            if ($splat[$n]) {
                $splat[$n] = $splat[$n].split(",")
            }
        }

        #convert ValidateScript text into a scriptblock
        if ($splat["ValidateScript"]) {
            $splat["ValidateScript"] = [scriptblock]::Create($splat["ValidateScript"])
        }
        New-PSDynamicParameter @splat | Set-Clipboard
        Write-Host "Your dynamic parameter code has been copied to the clipboard. Paste it into your script file." -ForegroundColor Green
    })
    $Quit = $Window.FindName("Quit")
    $Quit.Add_Click({$window.close()})

    [void]$window.Activate()
    [void]$window.ShowDialog()
}

在定义这些函数的 .ps1 文件中,如果您在 VS Code 或 PowerShell ISE 中获取文件,我还会使用此代码生成快捷方式。

#add scripting editor shortcuts or you can run the functions in the editor's console window.
if ($host.name -eq 'Visual Studio Code Host') {
    Register-EditorCommand -Name "DynamicParameterForm" -DisplayName "Define a dynamic parameter" -ScriptBlock (Get-Item -path Function:\New-PSDynamicParameterForm).scriptblock -SuppressOutput
}
elseif ($host.name -match "PowerShell ISE") {
    if ($psise.CurrentPowerShellTab.AddOnsMenu.Submenus.DisplayName -notcontains "New Dynamic Parameter") {
        $action = {New-PSDynamicParameterForm}
        [void]($Psise.CurrentPowerShellTab.AddOnsMenu.Submenus.Add("New Dynamic Parameter", $action, "Ctrl+Alt+D"))
    }
}

运行该命令将生成此 WPF 表单。

[玩转系统] 生成PowerShell动态参数代码

我根据 New-PSDynamicParameterForm 的帮助消息动态生成了每个项目的工具提示信息。强制值用星号表示。我发现我可能需要调整“条件”文本框。

无论如何,当您单击“创建”时,该函数会生成动态参数代码并将其复制到剪贴板。然后您可以将其粘贴到您的函数中。该表单将保持打开状态,直到您单击“关闭”。这使您可以微调参数,而无需重新输入所有内容。

顺便说一句,如果您有兴趣,这里是更新的 Get-DriveGridView 函数。

Function Get-DriveGridView {

    [cmdletbinding(DefaultParameterSetName = "computer")]
    [OutputType("none","object")]
    Param(
        [parameter(Position = 0, ValueFromPipeline, ParameterSetName = "computer")]
        [ValidateNotNullOrEmpty()]
        [string]$Computername = $env:COMPUTERNAME,

        [parameter(ValueFromPipeline, ParameterSetName = "session")]
        [ValidateNotNullOrEmpty()]
        [Microsoft.Management.Infrastructure.CimSession]$CimSession,

        [Parameter(HelpMessage = "Enter a title to use for the GridView")]
        [ValidateNotNullOrEmpty()]
        [string]$Title = "Drive Report",

        [Parameter(HelpMessage = "pass results to the pipeline in addition to the grid view")]
        [switch]$Passthru
    )

    DynamicParam {
    # Offer to use Out-ConsoleGridView if installed in PowerShell 7
        If (Get-Command -Name Out-ConsoleGridview -ErrorAction SilentlyContinue) {

        $paramDictionary = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary

        # Defining parameter attributes
        $attributeCollection = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute]
        $attributes = New-Object System.Management.Automation.ParameterAttribute
        $attributes.ParameterSetName = '__AllParameterSets'
        $attributes.HelpMessage = 'Use the Out-ConsoleGridView command in PowerShell 7'
        $attributeCollection.Add($attributes)

        # Adding a parameter alias
        $dynalias = New-Object System.Management.Automation.AliasAttribute -ArgumentList 'ocgv'
        $attributeCollection.Add($dynalias)

        # Defining the runtime parameter
        $dynParam1 = New-Object -Type System.Management.Automation.RuntimeDefinedParameter('ConsoleGridView', [Switch], $attributeCollection)
        $paramDictionary.Add('ConsoleGridView', $dynParam1)

        return $paramDictionary
    } # end if
} #end DynamicParam

    Begin {
        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN  ] Starting $($myinvocation.mycommand)"
        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN  ] Collecting drive information...please wait"

        #initialize a list to hold the results
        $results = [System.Collections.Generic.list[object]]::new()

        #hashtable of Get-CimInstance parameters for splatting
        $splat = @{
            Classname   = "win32_logicaldisk"
            Filter      = "drivetype=3"
            ErrorAction = "Stop"
            Property    = "SystemName", "DeviceID", "Size", "Freespace"
        }

    } #begin

    Process {
        If ($pscmdlet.ParameterSetName -eq 'computer') {
            Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Getting data from computer $($computername.toUpper())"
            $splat["Computername"] = $Computername
            $remote = $Computername.ToUpper()
        }
        else {
            Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Getting data from cimsession $($cimsession.computername.toUpper())"
            $splat["CimSession"] = $cimSession
            $remote = $cimSession.computername.toUpper()
        }

        Try {
            Get-CimInstance @splat | Select-Object -Property @{Name = "Computername"; Expression = { $_.Systemname } },
            @{Name = "Drive"; Expression = { $_.DeviceID } },
            @{Name = "SizeGB"; Expression = { [int]($_.Size / 1GB) } },
            @{Name = "FreeGB"; Expression = { [int]($_.Freespace / 1GB) } },
            @{Name = "UsedGB"; Expression = { [math]::round(($_.size - $_.Freespace) / 1GB, 2) } },
            @{Name = "Free%"; Expression = { [math]::round(($_.Freespace / $_.Size) * 100, 2) } },
            @{Name = "FreeGraph"; Expression = {
            [int]$per = (($_.Freespace / $_.Size) * 100/2)
            "|" * $per }
            } | ForEach-Object { $results.Add($_) }
        } #try
        Catch {
            Write-Warning "Failed to get drive data from $remote. $($_.exception.message)"
        }

    } #process

    End {
        #send the results to Out-Gridview
        Write-Verbose "[$((Get-Date).TimeofDay) END    ] Found $($results.count) total items"
        if ($results.count -gt 1) {
            if ($PSBoundParameters.ContainsKey("ConsoleGridView")) {
                $Results | Sort-Object -Property Computername | Out-ConsoleGridView -Title $Title
            }
            else {
                $Results | Sort-Object -Property Computername | Out-GridView -Title $Title
            }
            if ($Passthru) {
                $Results
            }
        }
        else {
            Write-Warning "No drive data found to report."
        }
        Write-Verbose "[$((Get-Date).TimeofDay) END    ] Ending $($myinvocation.mycommand)"

    } #end
}

尝试一下

这两个函数和脚本编辑器检测代码都位于我点源的单个 .ps1 文件中。我并不经常需要动态参数,但当我需要时,我认为这会节省我很多时间。我希望您能尝试一下,并让我知道您的想法或它对您有何帮助。我正在考虑将其添加到 PSScriptTools 模块中,这似乎是理想的家,但在采取这一步之前我希望得到一些反馈。

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

取消回复欢迎 发表评论:

关灯