Funções avançadas (cmdlet-style)
Esta aula ensina a criar funções avançadas no PowerShell que se comportam como cmdlets nativos, utilizando o atributo [CmdletBinding()] para habilitar funcionalidades como parâmetros comuns e tratamento de pipeline. Você aprenderá a usar parâmetros como ValueFromPipeline para receber objetos do pipeline e a estrutura Begin/Process/End para processar dados em lote.
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
- Documentação Microsoft: About Functions Advanced
- About Functions Advanced Parameters
- PowerShell 101: Functions
- About Pipelines
- Adding Parameters That Process Pipeline Input
Exercícios
- Crie uma função avançada chamada
Get-Quadradoque receba um número do pipeline (ValueFromPipeline) e retorne seu quadrado. Teste com1..5 | Get-Quadrado. - Modifique a função
Get-Dobropara usar o blocobeginpara exibir uma mensagem "Iniciando processamento" e o blocoendpara exibir "Processamento concluído". - Crie uma função
Get-Statusque receba objetos do pipeline por propriedade (ValueFromPipelineByPropertyName) com um parâmetro$Namee retorne "Processando: [nome]". Teste comGet-Process | Get-Status. - Escreva uma função
Get-Mediaque receba números do pipeline, calcule a média no blocoende a exiba. Usebeginpara inicializar uma lista. - Adicione suporte a ShouldProcess em uma função
Remove-ItemSeguroque simula a exclusão de um arquivo (apenas exibe mensagem), usando[CmdletBinding(SupportsShouldProcess)]e$PSCmdlet.ShouldProcess().
function Get-Quadrado {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline)]
[int]$Numero
)
process {
Write-Output ($Numero * $Numero)
}
}
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"
}
}
function Get-Status {
[CmdletBinding()]
param(
[Parameter(ValueFromPipelineByPropertyName)]
[string]$Name
)
process {
Write-Output "Processando: $Name"
}
}
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"
}
}
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)"
}
}
}