No PowerShell, tudo é um objeto, mas muitas vezes precisamos de estruturas de dados que não correspondem exatamente aos tipos nativos. É aí que entra o PSCustomObject, uma ferramenta poderosa para criar objetos personalizados com as propriedades que você definir. Esta aula vai te ensinar a criar, manipular e aplicar esses objetos em situações reais.

Dominar o PSCustomObject é essencial para scripts mais limpos e para a saída de dados estruturados, especialmente quando você precisa combinar informações de diferentes fontes ou criar relatórios personalizados.

Criando objetos customizados

Antes do PowerShell 3.0, criar objetos customizados era mais verboso, usando New-Object com PSObject. Hoje, a maneira mais comum e concisa é usando o acelerador de tipo [PSCustomObject] seguido de uma hashtable. Mas também é possível usar o cmdlet New-Object e depois adicionar propriedades com Add-Member. Vamos focar na abordagem moderna.

Exemplo básico:

$pessoa = [PSCustomObject]@{
    Nome = 'João'
    Idade = 30
    Cidade = 'São Paulo'
}

$pessoa

Isso cria um objeto com três propriedades: Nome, Idade e Cidade. Você pode acessá-las como qualquer propriedade de objeto: $pessoa.Nome.

[PSCustomObject]@{}

Essa sintaxe combina o acelerador de tipo [PSCustomObject] com uma hashtable (@{}). A hashtable define as propriedades e seus valores iniciais. É importante notar que a ordem das propriedades é preservada (a partir do PowerShell 3.0).

Detalhes importantes:

  • A hashtable deve ter chaves como strings (nomes das propriedades).
  • Os valores podem ser de qualquer tipo, inclusive outros objetos ou arrays.
  • Propriedades podem ser adicionadas posteriormente com Add-Member ou simplesmente atribuindo um valor a uma propriedade inexistente (embora isso não seja recomendado para objetos criados com [PSCustomObject] – é mais seguro usar Add-Member).

Exemplo com propriedades de diferentes tipos:

$computador = [PSCustomObject]@{
    Nome = 'SRV01'
    IP = '192.168.1.10'
    Online = $true
    Discos = @('C:', 'D:')
}

$computador

Você também pode criar objetos vazios e depois preenchê-los:

$obj = [PSCustomObject]@{}
$obj | Add-Member -MemberType NoteProperty -Name 'Nome' -Value 'Teste'
$obj | Add-Member -MemberType NoteProperty -Name 'Valor' -Value 100
$obj

Propriedades

Propriedades de um PSCustomObject são como variáveis dentro do objeto. Você pode defini-las na criação ou adicioná-las depois. Além de propriedades simples (NoteProperty), você pode adicionar métodos (ScriptMethod) ou propriedades calculadas.

Exemplo de adição dinâmica com Add-Member:

$carro = [PSCustomObject]@{
    Marca = 'Ford'
    Modelo = 'Focus'
}

$carro | Add-Member -MemberType NoteProperty -Name 'Ano' -Value 2020
$carro | Add-Member -MemberType ScriptMethod -Name 'Ligar' -Value { Write-Host 'Vruum!' }

$carro.Ligar()

Para remover uma propriedade, você pode usar Remove-Variable no objeto, mas isso não é comum. Melhor é criar um novo objeto sem a propriedade indesejada.

Propriedades podem ser acessadas e modificadas diretamente:

$carro.Ano = 2021
$carro.Ano

Casos de uso

PSCustomObject é extremamente útil em várias situações:

  • Saída de dados estruturados: Em vez de strings soltas, você pode emitir objetos que podem ser processados por outros cmdlets.
  • Relatórios: Combinar informações de várias fontes em um único objeto para exportar para CSV, JSON, etc.
  • Simulação de classes: Antes do PowerShell 5.0 com classes, PSCustomObject era a principal forma de criar objetos com propriedades.
  • Conversão de dados: Transformar hashtables ou arrays em objetos para facilitar o acesso.

Exemplo prático: coletar informações de processos e criar um relatório customizado.

$processos = Get-Process | Select-Object -First 5
$relatorio = foreach ($proc in $processos) {
    [PSCustomObject]@{
        Nome      = $proc.Name
        PID       = $proc.Id
        MemoriaMB = [math]::Round($proc.WorkingSet / 1MB, 2)
        Threads   = $proc.Threads.Count
    }
}

$relatorio | Export-Csv -Path 'processos.csv' -NoTypeInformation

Isso gera um CSV limpo com apenas as colunas desejadas.

Boas práticas

  • Use [PSCustomObject]@{} sempre que possível, é mais legível.
  • Evite adicionar propriedades após a criação se puder defini-las todas de uma vez.
  • Para objetos que serão usados como saída, considere usar [PSCustomObject] em vez de hashtables, pois os objetos têm comportamento consistente em pipelines.
  • Se precisar de métodos, use Add-Member com ScriptMethod, mas lembre-se de que isso pode tornar o objeto mais pesado.

Referências

Exercícios

  1. Crie um objeto PSCustomObject chamado $aluno com as propriedades Nome, Idade e Curso. Atribua valores de sua escolha e exiba o objeto.

    ✓ Resposta:
    $aluno = [PSCustomObject]@{
        Nome = 'Maria'
        Idade = 22
        Curso = 'Engenharia'
    }
    $aluno
    
  2. Adicione uma propriedade 'Nota' (tipo double) ao objeto $aluno criado no exercício 1 usando Add-Member. Atribua um valor e exiba a propriedade.

    ✓ Resposta:
    $aluno | Add-Member -MemberType NoteProperty -Name 'Nota' -Value 8.5
    $aluno.Nota
    
  3. Crie uma lista de objetos PSCustomObject representando 3 produtos (Nome, Preco, Quantidade). Calcule e exiba o valor total em estoque (soma de Preco * Quantidade para cada produto).

    ✓ Resposta:
    $produtos = @(
        [PSCustomObject]@{ Nome = 'Caneta'; Preco = 1.50; Quantidade = 100 },
        [PSCustomObject]@{ Nome = 'Caderno'; Preco = 15.00; Quantidade = 50 },
        [PSCustomObject]@{ Nome = 'Borracha'; Preco = 0.75; Quantidade = 200 }
    )
    
    $total = ($produtos | ForEach-Object { $_.Preco * $_.Quantidade } | Measure-Object -Sum).Sum
    Write-Host "Valor total em estoque: R$ $total"
    
  4. Usando Get-Process, crie um objeto PSCustomObject para cada processo com as propriedades: Nome, PID, e MemoriaMB (WorkingSet em MB arredondado). Selecione apenas os 5 primeiros processos. Exiba os objetos.

    ✓ Resposta:
    Get-Process | Select-Object -First 5 | ForEach-Object {
        [PSCustomObject]@{
            Nome      = $_.Name
            PID       = $_.Id
            MemoriaMB = [math]::Round($_.WorkingSet / 1MB, 2)
        }
    }
    
  5. Crie um objeto PSCustomObject vazio, adicione uma propriedade 'Nome' e um método 'Saudacao' que escreve 'Olá, [Nome]!'. Execute o método.

    ✓ Resposta:
    $obj = [PSCustomObject]@{}
    $obj | Add-Member -MemberType NoteProperty -Name 'Nome' -Value 'Ana'
    $obj | Add-Member -MemberType ScriptMethod -Name 'Saudacao' -Value { Write-Host "Olá, $($this.Nome)!" }
    $obj.Saudacao()