Funções avançadas no PowerShell permitem criar comandos que se comportam exatamente como cmdlets nativos, suportando parâmetros comuns (como -Verbose, -Debug, -ErrorAction), pipeline e validação de parâmetros. O segredo está no uso do atributo [CmdletBinding()] e na definição correta dos parâmetros. Nesta aula, você dominará esses conceitos e poderá criar funções profissionais e reutilizáveis.

CmdletBinding()

O atributo [CmdletBinding()] é colocado antes do bloco param() em uma função para transformá-la em uma função avançada. Sem ele, a função é considerada simples e não possui acesso a parâmetros comuns do PowerShell, como -Verbose, -Debug, -ErrorAction, -WarningAction, etc.

Além de habilitar esses parâmetros, [CmdletBinding()] permite configurar comportamentos como suporte a ShouldProcess (para operações que modificam o sistema), confirmação automática e tratamento de erros consistente. A sintaxe básica é:

function Get-Exemplo {
    [CmdletBinding()]
    param(
        [string]$Nome
    )
    process {
        Write-Output "Olá, $Nome"
    }
}

Com isso, a função já aceita -Verbose, -Debug, etc. Você pode testar executando Get-Exemplo -Nome Mundo -Verbose e verá mensagens de verbose.

Parâmetros do pipeline

Uma das grandes vantagens das funções avançadas é a capacidade de receber dados diretamente do pipeline. Para isso, você precisa decorar os parâmetros com atributos como [Parameter(ValueFromPipeline)] ou [Parameter(ValueFromPipelineByPropertyName)]. O pipeline envia objetos para a função, e ela pode processá-los um a um.

Exemplo: uma função que recebe números do pipeline e retorna o dobro:

function Get-Dobro {
    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipeline)]
        [int]$Numero
    )
    process {
        Write-Output ($Numero * 2)
    }
}

Agora você pode executar 1..5 | Get-Dobro e obterá 2, 4, 6, 8, 10. O pipeline envia cada número inteiro para o parâmetro $Numero.

ValueFromPipeline

O atributo [Parameter(ValueFromPipeline)] indica que o parâmetro aceita valores diretamente do pipeline. Se o objeto enviado for do mesmo tipo do parâmetro, ele é automaticamente convertido. Caso contrário, ocorrerá um erro. É possível usar ValueFromPipelineByPropertyName para mapear propriedades do objeto para parâmetros com o mesmo nome.

Exemplo com ValueFromPipelineByPropertyName:

function Get-Propriedade {
    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipelineByPropertyName)]
        [string]$Name
    )
    process {
        Write-Output "Propriedade Name: $Name"
    }
}

Se você enviar objetos que tenham uma propriedade chamada Name, como Get-Process | Get-Propriedade, o valor da propriedade Name de cada processo será passado para o parâmetro $Name.

Begin/Process/End

Funções avançadas podem ter três blocos especiais: begin, process e end. Eles controlam o fluxo de execução quando a função recebe dados do pipeline. O bloco begin executa uma vez antes do primeiro objeto ser processado, ideal para inicializar variáveis. O bloco process executa para cada objeto recebido do pipeline. O bloco end executa uma vez após todos os objetos serem processados, útil para finalização.

Exemplo completo:

function Soma-Acumulada {
    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipeline)]
        [int]$Numero
    )
    begin {
        $soma = 0
        Write-Verbose "Iniciando soma"
    }
    process {
        $soma += $Numero
        Write-Output "Soma parcial: $soma"
    }
    end {
        Write-Output "Soma total: $soma"
    }
}

Teste com 1..5 | Soma-Acumulada -Verbose. Você verá as mensagens de verbose no início e os resultados parciais e total.

Boas Práticas e Observações Finais

Sempre use [CmdletBinding()] em funções que serão compartilhadas ou usadas em scripts complexos. Isso garante consistência com cmdlets nativos e facilita o debugging. Use Write-Verbose, Write-Debug e Write-Warning para fornecer feedback adequado. Prefira ValueFromPipelineByPropertyName quando os objetos do pipeline tiverem propriedades com nomes correspondentes aos parâmetros. Lembre-se de que o bloco process é obrigatório se você usar ValueFromPipeline; caso contrário, a função não processará os itens do pipeline corretamente.

Referências

Exercícios

  1. Crie uma função avançada chamada Get-Quadrado que receba um número do pipeline (ValueFromPipeline) e retorne seu quadrado. Teste com 1..5 | Get-Quadrado.
  2. ✓ Resposta:
    function Get-Quadrado {
        [CmdletBinding()]
        param(
            [Parameter(ValueFromPipeline)]
            [int]$Numero
        )
        process {
            Write-Output ($Numero * $Numero)
        }
    }
  3. Modifique a função Get-Dobro para usar o bloco begin para exibir uma mensagem "Iniciando processamento" e o bloco end para exibir "Processamento concluído".
  4. ✓ Resposta:
    function Get-Dobro {
        [CmdletBinding()]
        param(
            [Parameter(ValueFromPipeline)]
            [int]$Numero
        )
        begin {
            Write-Host "Iniciando processamento"
        }
        process {
            Write-Output ($Numero * 2)
        }
        end {
            Write-Host "Processamento concluído"
        }
    }
  5. Crie uma função Get-Status que receba objetos do pipeline por propriedade (ValueFromPipelineByPropertyName) com um parâmetro $Name e retorne "Processando: [nome]". Teste com Get-Process | Get-Status.
  6. ✓ Resposta:
    function Get-Status {
        [CmdletBinding()]
        param(
            [Parameter(ValueFromPipelineByPropertyName)]
            [string]$Name
        )
        process {
            Write-Output "Processando: $Name"
        }
    }
  7. Escreva uma função Get-Media que receba números do pipeline, calcule a média no bloco end e a exiba. Use begin para inicializar uma lista.
  8. ✓ Resposta:
    function Get-Media {
        [CmdletBinding()]
        param(
            [Parameter(ValueFromPipeline)]
            [int]$Numero
        )
        begin {
            $lista = @()
        }
        process {
            $lista += $Numero
        }
        end {
            $media = ($lista | Measure-Object -Average).Average
            Write-Output "Média: $media"
        }
    }
  9. Adicione suporte a ShouldProcess em uma função Remove-ItemSeguro que simula a exclusão de um arquivo (apenas exibe mensagem), usando [CmdletBinding(SupportsShouldProcess)] e $PSCmdlet.ShouldProcess().
  10. ✓ Resposta:
    function Remove-ItemSeguro {
        [CmdletBinding(SupportsShouldProcess)]
        param(
            [Parameter(Mandatory)]
            [string]$Path
        )
        process {
            if ($PSCmdlet.ShouldProcess($Path, "Excluir arquivo")) {
                Write-Host "Arquivo $Path excluído (simulação)"
            }
        }
    }