从内部将功能从一个功能传递到另一个功能

时间:2018-08-14 17:49:17

标签: powershell

我有一个具有以下两个功能的模块,它们几乎是相同的:

<#
    .SYNOPSIS
    Retrieves a VApp from VCloud.
#>
Function Get-VApp
{
    [CmdletBinding()]
    [OutputType([System.Xml.XmlElement])]
    Param(
        [Parameter(Mandatory = $true)]
        [System.Xml.XmlElement] $Session,
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [string[]] $VAppName
    )
    Begin {
        [System.Xml.XmlElement] $queryList = $Session.GetQueryList();
        [System.Xml.XmlElement[]] $vAppRecords = $queryList.GetVAppsByRecords().VAppRecord;
    }
    Process {
        ForEach ($VAN in $VAppName)
        {
            $vAppRecords |
            Where-Object { $_.name -eq $VAN } |
            ForEach-Object { $_.Get(); }
        }
    }
    End
    {
        #
    }
}

<#
    .SYNOPSIS
    Retrieves a VAppRecord from VCloud.
#>
Function Get-VAppRecord
{
    [CmdletBinding()]
    [OutputType([System.Xml.XmlElement])]
    Param(
        [Parameter(Mandatory = $true)]
        [System.Xml.XmlElement] $Session,
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [string[]] $VAppName
    )
    Begin {
        [System.Xml.XmlElement] $queryList = $Session.GetQueryList();
        [System.Xml.XmlElement[]] $vAppRecords = $queryList.GetVAppsByRecords().VAppRecord;
    }
    Process {
        ForEach ($VAN in $VAppName)
        {
            $vAppRecords |
            Where-Object { $_.name -eq $VAN } |
            ForEach-Object { $_; }
        }
    }
    End
    {
        #
    }
}

本质上,Get-VApp类似于Get-VAppRecord,除了前者在返回的对象上调用Get()方法。这似乎很浪费。如果我不打扰管道,那将很容易:

Function Get-VApp
{
        [CmdletBinding()]
        [OutputType([System.Xml.XmlElement])]
        Param(
            [Parameter(Mandatory = $true)]
            [System.Xml.XmlElement] $Session,
            [Parameter(Mandatory = $true)]
            [string[]] $VAppName
        )
    Get-VAppRecord $Session $VAppName |
    ForEach-Object {
        $_.Get();
    }
}

但是很明显,管道把事情搞砸了。为了提高效率,我没有多次调用Begin块中的代码,我想找到一种方法来“与管道打好”而不必分批记录。

1 个答案:

答案 0 :(得分:2)

SteppablePipeline类旨在包装启用了管道的命令,而不会弄乱它们对管道的支持。

您甚至不需要知道如何设置它,ProxyCommand.Create()会为其生成支架!

因此,我们首先为Get-VAppRecord创建代理功能:

$GetVAppRecordCommand = Get-Command Get-VAppRecord
$GetVAppRecordCommandMetadata = [System.Management.Automation.CommandMetadata]::new($GetVAppRecordCommand)

# returns the body of the new proxy functions
[System.Management.Automation.ProxyCommand]::Create($GetVAppRecordCommandMetadata)

...,然后我们只需要在其Get()块中添加process调用:

function Get-VApp {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true, Position=0)]
        [System.Xml.XmlElement]
        ${Session},

        [Parameter(Mandatory=$true, Position=1, ValueFromPipeline=$true)]
        [string[]]
        ${VAppName})

    begin
    {
        try {
            $outBuffer = $null
            if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
            {
                $PSBoundParameters['OutBuffer'] = 1
            }
            $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Get-VAppRecord', [System.Management.Automation.CommandTypes]::Function)
            $scriptCmd = {& $wrappedCmd @PSBoundParameters }
            $steppablePipeline = $scriptCmd.GetSteppablePipeline()
            $steppablePipeline.Begin($MyInvocation.ExpectingInput) # Many examples use $PSCmdlet; however setting this ensures that $steppablePipeline.Process() returns the output of the inner function.
        } catch {
            throw
        }
    }

    process
    {
        try {
            $steppablePipeline.Process($_) |ForEach-Object {
                # call Get() on the record
                $_.Get()
            }
        } catch {
            throw
        }
    }

    end
    {
        try {
            $steppablePipeline.End()
        } catch {
            throw
        }
    }
}