3

我正在尝试重命名我的一些 cmdlet,并希望在不破坏现有脚本的情况下这样做。我想在不使用 Set-Alias/New-Alias 的情况下执行此操作,因为我不希望在我们从 powershell 提示符下执行 Get-Command 时显示别名,并且我认为可以使用导出的函数来实现相同的目的别名 cmdlet 就可以了。

这是我想做的一个例子

Old cmdlet - Add-Foo
Renamed cmdlet - Add-FooBar
Expectation - Scripts using Add-Foo should continue to work the same way as it used to

我正在考虑引入以下功能

function Add-Foo()
{
   # Delegate parameter inputs to cmdlet Add-FooBar
}

我有一个简单的版本,但我不确定它是否适用于更复杂的情况。

function Add-Foo()
{
   $cmd = "Add-FooBar"
   if ($arguments.Length -eq 0){
     Invoke-Expression $cmd;
   }
   else{
     # Concatentate cmdlet and arguments into an expression
     $expr = "$($cmd)  $($args)";
     Write-Debug $expr;
     Invoke-Expression $expr;
   }
}

我不确定我的功能是否会 100% 与现有用法兼容。可以使函数 Add-Foo 与参数属性(管道绑定)和任何其他可能的用法一起表现良好吗?本质上,我希望函数按原样获取参数并将其传递给底层重命名的 cmdlet。

任何帮助表示赞赏。

谢谢

4

2 回答 2

3

PowerShell has a built-in feature for this: Proxy commands.

The [System.Management.Automation.ProxyCommand] class has several static methods to help out with this. Below is a template you can use to generate a proxy command and add a condition choosing whether or not to call the original command.

function New-ProxyCommand($command)
{
    $cmd = Get-Command $command
    $blocks = @{
        CmdletBinding = [System.Management.Automation.ProxyCommand]::GetCmdletBindingAttribute($cmd)
        Params        = [System.Management.Automation.ProxyCommand]::GetParamBlock($cmd)
        Begin         = [System.Management.Automation.ProxyCommand]::GetBegin($cmd)
        Process       = [System.Management.Automation.ProxyCommand]::GetProcess($cmd)
        End           = [System.Management.Automation.ProxyCommand]::GetEnd($cmd)
    }

    # Indent
    filter Indent($indent='    ') { $_ | foreach { ($_ -split "`r`n" | foreach { "${indent}$_" }) -join "`r`n" } }
    [array]$blocks.Keys | foreach { $blocks[$_] = $blocks[$_] | Indent }

    @"
function $command
{
$($blocks.CmdletBinding)
    param
    ($($blocks.Params)
    )

    begin
    {
        `$Reroute = `$false  ### Put your conditions here ###

        if (`$Reroute) { return }
    $($blocks.Begin)}

    process
    {
        if (`$Reroute) { return }
    $($blocks.Process)}

    end
    {
        if (`$Reroute) { return }
    $($blocks.End)}
}
"@
}

Example:

PS> New-ProxyCommand Get-Item

function Get-Item
{
    [CmdletBinding(DefaultParameterSetName='Path', SupportsTransactions=$true, HelpUri='http://go.microsoft.com/fwlink/?LinkID=113319')]
    param
    (    
        [Parameter(ParameterSetName='Path', Mandatory=$true, Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
        [string[]]
        ${Path},

        [Parameter(ParameterSetName='LiteralPath', Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
        [Alias('PSPath')]
        [string[]]
        ${LiteralPath},

        [string]
        ${Filter},

        [string[]]
        ${Include},

        [string[]]
        ${Exclude},

        [switch]
        ${Force},

        [Parameter(ValueFromPipelineByPropertyName=$true)]
        [pscredential]
        [System.Management.Automation.CredentialAttribute()]
        ${Credential}
    )

    begin
    {
        $Reroute = $false  ### Put your conditions here ###

        if ($Reroute) { return }

        try {
            $outBuffer = $null
            if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
            {
                $PSBoundParameters['OutBuffer'] = 1
            }
            $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Get-Item', [System.Management.Automation.CommandTypes]::Cmdlet)
            $scriptCmd = {& $wrappedCmd @PSBoundParameters }
            $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
            $steppablePipeline.Begin($PSCmdlet)
        } catch {
            throw
        }
    }

    process
    {
        if ($Reroute) { return }

        try {
            $steppablePipeline.Process($_)
        } catch {
            throw
        }
    }

    end
    {
        if ($Reroute) { return }

        try {
            $steppablePipeline.End()
        } catch {
            throw
        }
    }
}
于 2014-04-09T01:34:06.020 回答
0

一种选择是使用私有函数:

function Private:Add-Foo
{
  Add-Foobar $args
}

Add-Foo 只会在当前范围内调用这个函数。该函数在任何子范围内都不可见(如被调用的脚本),它们将使用 Add-Foo cmdlet。

于 2014-04-09T00:21:26.170 回答