Write-CommandOverload.ps1

function Write-CommandOverload
{
    <#
    .Synopsis
        Writes a command overload.
    .Description
        This creates a command that runs another command. It can optionally drop some parameters, or create new ones.
    .Example
        Write-CommandOverload -CommandName dir -NewTypeName myCustomDirView
    .Example
        Write-CommandOverload -CommandName dir -ProcessEachItem { $_ }
    .Link
        creating_command_overloads_with_ezout
    #>

    [CmdletBinding(DefaultParameterSetName='CommandName')]
    param(
    # The command metadata of the command to overload
    [Parameter(Mandatory=$true,ParameterSetName='CommandMetaData')]
    [Management.Automation.CommandMetaData]
    $Command,
    
    # The name of the command to overload
    #|Options Get-Command | ForEach-Object { ($_.ModuleName + $_.PSSnapinName + "\" + $_.Name).TrimStart("\") }
    [Parameter(Mandatory=$true,
        Position=0,
        ValueFromPipelineByPropertyName=$true,
        ParameterSetName='CommandName')]
    [string]
    $CommandName,
    
    # The module the command to overload is in
    [Parameter(Position=1,
        ValueFromPipelineByPropertyName=$true,
        ParameterSetName='CommandName')]
    [Alias('ModuleName', 'PSSnapinName')]
    [string]
    $Module,
    
    # Any additional parameters to add to the command.
    [Parameter(ValueFromPipelineByPropertyName=$true)]
    [Management.Automation.ParameterMetaData[]]
    $AdditionalParameter,
    
    # Any parameters to remove from the command.
    [Parameter(ValueFromPipelineByPropertyName=$true)]    
    [string[]]
    $RemoveParameter,

    # A new type name. This clears the type name table for each output and replaces it with a new name.
    # This allows you to change the default view of a built-in command.
    [string]
    $NewTypeName,
    
    # Any script that will be run on each item of output
    [ScriptBlock]
    $ProcessEachItem,
    
    # The name of the command to create. If no name is specified, the command's name will be used
    [string]
    $Name,
    
    # Any default values to pass to the command
    [Hashtable]
    $DefaultValue   
    )
    
    process {
        if ($psCmdlet.ParameterSetName -eq 'CommandName') {
            $nestedParams = @{} + $psBoundParameters
            if ($psBoundParameters.CommandName -like "*\*") {
                $psBoundParameters.Module = $psBoundParameters.CommandName.Split("\")[0]
                $psBoundParameters.Name = $psBoundParameters.CommandName.Split("\")[1]
            } else {
                $psBoundParameters.Name = $psBoundParameters.CommandName
            }
            
            $null = $nestedParams.Remove('Module'),
                $nestedParams.Remove('CommandName'),
                $psBoundParameters.Remove('CommandName'),
                $psBoundParameters.Remove('DefaultValue'),
                $psBoundParameters.Remove('ProcessEachItem')
            $resolvedCommand = Get-Command @psboundParameters
            
            if (-not $resolvedCommand) { 
                return
            }
            & $myInvocation.MyCommand -Command $resolvedCommand @nestedParams 
        } elseif ($psCmdlet.ParameterSetName -eq 'CommandMetaData') {     
            $MetaData = New-Object Management.Automation.CommandMetaData $Command
            foreach ($rp in $removeParameter) {
                if (-not $rp) { continue }
                $null = $MetaData.Parameters.Remove($rp)
            }
            foreach ($ap in $additionalParameter) {
                if (-not $ap) { continue }
                $null = $MetaData.Parameters.Add($ap.Name, $ap)
            }
            
            
            $beginBlock  = if ($NewTypeName -or $ProcessEachItem){
                [Management.Automation.ProxyCommand]::GetBegin($metaData) -replace '\.Begin\(\$PSCmdlet\)', '.Begin($false)'                
            } else {
                [Management.Automation.ProxyCommand]::GetBegin($metaData)
            }
            
            if (-not $Name) {
                $Name = $command.Name
            }
            
            
            if ($DefaultValue) {
                $defaultValueText = Write-PowerShellHashtable  -InputObject $DefaultValue
                
                $defaultValueText = '$default = ' + $defaultValueText + {
foreach ($kv in $default.GetEnumerator()) {
    $psBoundParameters[$kv.Key] = $kv.Value
}
}
                
                $defaultValueText = $defaultValueText -split ([Environment]::NewLine) -ne "" |
                    ForEach-Object { " " * 8 + $_  + [Environment]::NewLine}                             
                $beginBlock  = $beginBlock -replace '\$scriptCmd =', ($defaultValueText + '$scriptCmd =')
            }
            
            # Indent the block of code. Yes, this is neurotic.
            $beginBlock = $beginBlock -split ([Environment]::NewLine) -ne "" |
                ForEach-Object { " " * 4 + $_  + [Environment]::NewLine}                             
            
            $endBlock = [Management.Automation.ProxyCommand]::GetEnd($metaData) -split ([Environment]::NewLine) -ne "" |
                ForEach-Object { " " * 4 + $_  + [Environment]::NewLine} 
                
                
            $paramBlock = "$([Management.Automation.ProxyCommand]::GetParamBlock($metaData))" -replace '(\$)\{(\w.+)\}', '$1$2'
            
            
            
            $ProcessBlock  = if ($ProcessEachItem){                
                $embedProcessBlock = $ProcessEachItem.ToString() -split ([Environment]::NewLine) -ne "" |
                    ForEach-Object { " " * 16 + $_  + [Environment]::NewLine}                             
                "
    try {
        `$steppablePipeline.Process(`$_) |
            ForEach-Object {
$embedProcessBlock
            }
    } catch {
        throw
    }
                "

            } elseif ($NewTypeName) {
                "
    try {
        `$steppablePipeline.Process(`$_) |
            ForEach-Object {
                `$_.pstypenames.clear()
                `$_.pstypenames.add('$NewTypeName')
                `$_
            }
    } catch {
        throw
    }
                "

            } else {
                [Management.Automation.ProxyCommand]::GetProcess($metaData)
            }
            
            
            $ProcessBlock = $ProcessBlock -split ([Environment]::NewLine) -ne "" |
                ForEach-Object { " " * 4 + $_  + [Environment]::NewLine}

        
"function ${Name} {
    $([Management.Automation.ProxyCommand]::GetCmdletBindingAttribute($MetaData))
    param(
        $paramBlock
    )
     
    begin {
$BeginBlock
    }
     
    process {
$ProcessBlock
    }
     
    end {
$EndBlock
    }
}
"



        }
    }
}