A saída de texto de comandos na janela do PowerShell é apenas uma forma de exibir informações de uma maneira compreensível para os seres humanos. Na realidade, o ambiente trabalha com objetos: cmdlets e funções recebem esses objetos como entrada e , enquanto os tipos de variáveis disponíveis no modo interativo e em scripts são baseados em classes .NET. No quarto artigo da série, exploraremos o trabalho com objetos em mais detalhes.
Conteúdo:
Objetos no PowerShell
Lembre-se de que um objeto é um conjunto de campos de dados (propriedades, eventos etc.) e métodos para manipulá-los. Sua estrutura é definida pelo tipo, que geralmente é baseado nas classes utilizadas na plataforma unificada .NET Core. Também é possível trabalhar com objetos COM, CIM (WMI) e ADSI. Propriedades e métodos são necessários para executar diversas ações sobre os dados, além disso, no PowerShell, os objetos podem ser passados como argumentos para funções e cmdlets, atribuindo seus valores a variáveis, e também existe (pipeline). Cada comando no pipeline passa sua saída para o próximo sequencialmente — objeto por objeto. Para processamento, podem ser utilizados cmdlets compilados ou você pode criar suas próprias , para realizar diversas manobras com objetos no pipeline: filtragem, classificação, agrupamento e até mesmo alteração de sua estrutura. A transmissão de dados dessa forma tem uma vantagem significativa: o comando receptor não precisa lidar com a análise sintática do fluxo de bytes (texto), toda a informação necessária é facilmente extraída por meio do acesso às propriedades e métodos correspondentes.
Visualizando a estrutura dos objetos
Por exemplo, vamos executar o cmdlet Get-Process, que permite obter informações sobre os processos em execução no sistema:

Ele exibirá na tela alguns dados de texto formatados, que não fornecem uma visão clara das propriedades dos objetos retornados e seus métodos. Para uma exploração mais detalhada da saída, é necessário aprender a investigar a estrutura dos objetos, e o cmdlet Get-Member nos ajudará com isso:
Get-Process | Get-Member 
Aqui já vemos o tipo e a estrutura, e com parâmetros adicionais podemos, por exemplo, exibir apenas as propriedades do objeto que entrou:
Get-Process | Get-Member -MemberType PropertyEsse conhecimento será útil para resolver tarefas de administração em modo interativo ou para escrever seus próprios scripts: digamos, para obter informações sobre processos congelados pela propriedade Responding.
Filtrando objetos
O PowerShell permite passar objetos através do pipeline que atendem a uma determinada condição:
Where-Object { bloco do script }O resultado da execução do bloco de script entre chaves deve ser um valor lógico. Se for verdadeiro ($true), o objeto que entrou no cmdlet Where-Object será passado adiante no pipeline, caso contrário (valor $false), ele será removido. Por exemplo, vamos exibir uma lista de serviços parados do Windows Server, ou seja, aqueles cujo status tem o valor "Stopped":
Get-Service | Where-Object {$_.Status -eq "Stopped"} 
Aqui novamente vemos uma representação textual, mas se desejarmos, entender o tipo e a estrutura interna dos objetos que passam pelo pipeline não é difícil:
Get-Service | Where-Object {$_.Status -eq "Stopped"} | Get-Member 
Classificando objetos
Ao processar objetos pelo pipeline, frequentemente surge a necessidade de ordená-los. O cmdlet Sort-Object recebe os nomes das propriedades (chaves de ordenação) e retorna os objetos ordenados por seus valores. A saída dos processos em execução pode ser facilmente ordenada pelo tempo de CPU utilizado (propriedade cpu):
Get-Process | Sort-Object –Property cpuO parâmetro -Property ao chamar o cmdlet Sort-Object pode ser omitido — ele é usado por padrão. Para a ordenação inversa, é utilizado o parâmetro -Descending:
Get-Process | Sort-Object cpu -Descending 
Extraindo objetos e suas partes
O cmdlet Select-Object permite destacar um número específico de objetos no início ou no final do pipeline com os parâmetros -First ou -Last. Com ele, é possível escolher objetos individuais ou propriedades específicas, além de criar novos objetos com base neles. Vamos analisar o funcionamento do cmdlet com exemplos simples.
O próximo comando exibe informações sobre 10 processos que consomem a maior quantidade de memória RAM (propriedade WS):
Get-Process | Sort-Object WS -Descending | Select-Object -First 10 
É possível destacar apenas certas propriedades dos objetos que passam pelo pipeline e criar novos com base nelas:
Get-Process | Select-Object ProcessName, Id -First 1Como resultado do pipeline, obteremos um novo objeto cuja estrutura será diferente da estrutura retornada pelo cmdlet Get-Process. Vamos verificar isso com o Get-Member:
Get-Process | Select-Object ProcessName, Id -First 1 | Get-Member 
Observe que o Select-Object retorna um único objeto (-First 1), que possui apenas os dois campos que especificamos: seus valores foram copiados do primeiro objeto passado para o pipeline pelo cmdlet Get-Process. Uma das maneiras de criar objetos em scripts PowerShell é baseada no uso do Select-Object:
$obj = Get-Process | Select-Object ProcessName, Id -First 1
$obj.GetType() 
Com o Select-Object é possível adicionar propriedades calculadas aos objetos que precisam ser apresentadas como . O valor da primeira chave corresponde ao nome da propriedade, enquanto o valor da segunda chave é o valor da propriedade para o elemento atual do pipeline:
Get-Process | Select-Object -Property ProcessName, @{Name="StartTime"; Expression = {$_.StartTime.Minute}} 
Vamos dar uma olhada na estrutura dos objetos que estão passando pelo pipeline:
Get-Process | Select-Object -Property ProcessName, @{Name="StartTime"; Expression = {$_.StartTime.Minute}} | Get-Member 
ForEach-Object, Group-Object e Measure-Object
Existem outros cmdlets para trabalhar com objetos. Para exemplificar, falaremos sobre três dos mais úteis:
ForEach-Object permite executar código na linguagem PowerShell para cada objeto no pipeline:
ForEach-Object { bloco de script }Group-Object agrupa objetos com base no valor de uma propriedade:
Group-Object PropertyNameSe executá-lo com o parâmetro -NoElement, é possível descobrir a contagem dos elementos em cada grupo.
Measure-Object agrega diferentes parâmetros resumos com base nos valores dos campos dos objetos no pipeline (calcula a soma, e também encontra o valor mínimo, máximo ou médio):
Measure-Object -Property PropertyName -Minimum -Maximum -Average -SumNormalmente, os cmdlets discutidos são usados em modo interativo, enquanto em scripts, frequentemente, são criados com blocos Begin, Process e End.
Criando objetos .NET e COM (New-Object)
Existem muitos componentes de software com interfaces .NET Core e COM que são úteis para administradores de sistema. Com a classe System.Diagnostics.EventLog é possível gerenciar os logs do sistema diretamente do Windows PowerShell. Vamos detalhar um exemplo de criação de uma instância dessa classe utilizando o cmdlet New-Object com o parâmetro -TypeName:
New-Object -TypeName System.Diagnostics.EventLog 
Como não especificamos um determinado log de eventos, a instância da classe recebida não contém dados. Para alterar isso, é necessário chamar um método construtor especial durante sua criação usando o parâmetro -ArgumentList. Se quisermos acessar o log de aplicativos, devemos passar a string "Application" como argumento para o construtor:
$AppLog = New-Object -TypeName System.Diagnostics.EventLog -ArgumentList Application
$AppLog 
Observe que a saída do comando foi salva na variável $AppLog. Embora no modo interativo geralmente sejam utilizados pipelines, a escrita de scripts frequentemente requer a сохранение uma referência ao objeto. Além disso, as classes principais do .NET Core estão contidas no namespace System: o PowerShell procura, por padrão, os tipos especificados nele, portanto, é totalmente correto escrever Diagnostics.EventLog em vez de System.Diagnostics.EventLog.
Para trabalhar com o log, podemos chamar os métodos correspondentes:
$AppLog | Get-Member -MemberType Method 
Digamos que ele é limpo pelo método Clear() na presença de permissões de acesso:
$AppLog.Clear()O cmdlet New-Object também é usado para trabalhar com componentes COM. Existem muitos — desde bibliotecas de scripts do Windows entregues com o servidor até aplicativos ActiveX, como o Internet Explorer, por exemplo. Para criar um objeto COM, é necessário especificar o parâmetro -ComObject com o identificador ProgId da classe desejada:
New-Object -ComObject WScript.Shell
New-Object -ComObject WScript.Network
New-Object -ComObject Scripting.Dictionary
New-Object -ComObject Scripting.FileSystemObjectPara criar seus próprios objetos com uma estrutura arbitrária, o uso do New-Object parece excessivamente arcaico e volumoso; esse cmdlet é utilizado para trabalhar com componentes de software externos ao PowerShell. Em artigos futuros, essa questão será explorada com mais detalhes. Além de objetos .NET e COM, também estudaremos objetos CIM (WMI) e ADSI.
Chamando métodos estáticos
Não é possível criar instâncias de algumas classes .NET Core: incluem-se entre elas System.Environment e System.Math. Elas são e contêm apenas propriedades e métodos estáticos. Na verdade, são bibliotecas de referência que são usadas sem a criação de objetos. Para referenciar uma classe estática, pode-se usar o literal, colocando o nome do tipo entre colchetes. Ao fazer isso, se olharmos a estrutura do objeto usando Get-Member, veremos o tipo System.RuntimeType em vez de System.Environment:
[System.Environment] | Get-Member 
Para visualizar apenas elementos estáticos, é necessário chamar Get-Member com o parâmetro -Static (note o tipo do objeto):
[System.Environment] | Get-Member -Static 
Para acessar propriedades e métodos estáticos, utilizam-se dois pontos consecutivos em vez de um ponto após o literal:
[System.Environment]::OSVersionOu
$test=[System.Math]::Sqrt(25)
$test
$test.GetType() 
Tipo PSCustomObject
Entre os inúmeros tipos de dados disponíveis no PowerShell, merece destaque o PSCustomObject, destinado a armazenar objetos com estrutura arbitrária. Criar tal objeto com o cmdlet New-Object é considerado um método clássico, mas volumoso e obsoleto:
$object = New-Object –TypeName PSCustomObject -Property @{Name = 'Ivan Danko';
City = 'Moscow';
Country = 'Russia'}
Vamos observar a estrutura do objeto:
$object | Get-Member 
A partir do PowerShell 3.0, há também uma outra sintaxe disponível:
$object = [PSCustomObject]@{Name = 'Ivan Danko';
City = 'Moscow';
Country = 'Russia'
}
Os dados podem ser acessados de uma das seguintes maneiras equivalentes:
$object.Name
$object.'Name'
$value = 'Name'
$object.$value
Vamos dar um exemplo de conversão para um objeto de uma tabela hash existente:
$hash = @{'Name'='Ivan Danko'; 'City'='Moscow'; 'Country'='Russia'}
$hash.GetType()
$object = [pscustomobject]$hash
$object.GetType()

Uma das desvantagens dos objetos desse tipo é que a ordem de suas propriedades pode mudar. Para evitar isso, é necessário usar o atributo [ordered]:
$object = [PSCustomObject][ordered]@{Name = 'Ivan Danko';
City = 'Moscow';
Country = 'Russia'
}
Existem outras opções para criar um objeto: acima, abordamos o uso do cmdlet . Agora falta entender como adicionar e remover elementos. Fazer isso para o objeto do exemplo anterior é bastante simples:
$object | Add-Member –MemberType NoteProperty –Name Age –Value 33
$object | Get-Member

O cmdlet Add-Member permite adicionar não apenas propriedades, mas também métodos ao objeto $object usando a construção '-MemberType ScriptMethod':
$ScriptBlock = {
# código
}
$object | Add-Member -Name "MyMethod" -MemberType ScriptMethod -Value $ScriptBlock
$object | Get-Member
Observe que, para armazenar o código do novo método, usamos a variável $ScriptBlock do tipo ScriptBlock.

Para remover propriedades, utiliza-se o método correspondente:
$object.psobject.properties.remove('Name')
Criando suas próprias classes
No PowerShell 5.0, a definição de usando a sintaxe característica das linguagens de programação orientadas a objetos. Para isso, a palavra-chave Class é usada, seguida pelo nome da classe e a descrição de seu corpo entre chaves:
class MyClass
{
# corpo da classe
}
Este é um tipo real do .NET Core, cujo corpo descreve suas propriedades, métodos e outros elementos. Vamos considerar um exemplo da definição da classe mais simples:
class MyClass
{
[string]$Name
[string]$City
[string]$Country
}
Para criar um objeto (instância da classe) usamos o cmdlet , ou o literal do tipo [MyClass] e new (construtor padrão):
$object = New-Object -TypeName MyClassou
$object = [MyClass]::new()Vamos analisar a estrutura do objeto:
$object | Get-Member 
Não devemos nos esquecer do escopo: não se pode referir ao nome do tipo como uma string ou usar o literal do tipo fora do script ou módulo onde a classe é definida. Nesse caso, funções podem retornar instâncias da classe (objetos) que estarão disponíveis fora do módulo ou script.
Após a criação do objeto, vamos preencher suas propriedades:
$object.Name = 'Ivan Danko'
$object.City = 'Moscow'
$object.Country = 'Russia'
$object

Vale notar que na descrição da classe definimos não apenas os tipos das propriedades, mas também seus valores padrão:
class Example
{
[string]$Name = 'John Doe'
}
A descrição do método da classe se assemelha à descrição de uma função, mas sem o uso da palavra-chave function. Assim como nas funções, os métodos podem receber parâmetros, se necessário:
class MyClass
{
[string]$Name
[string]$City
[string]$Country
# descrição do método
Smile([bool]$param1)
{
If($param1) {
Write-Host ':)'
}
}
}
Agora, um representante da nossa classe consegue sorrir:
$object = [MyClass]::new()
$object.Smile($true)
Os métodos podem ser sobrecarregados, além disso, a classe pode ter , bem como construtores, cujos nomes coincidem com o nome da própria classe. Uma classe definida em um script ou módulo PowerShell pode servir como base para outra — assim se realiza a herança. Nesse caso, classes existentes do .NET podem ser usadas como classes base:
class MyClass2 : MyClass
{
# corpo da nova classe, que é base para MyClass
}
[MyClass2]::new().Smile($true)
Nossa descrição sobre o trabalho com objetos no PowerShell é difícil de considerar completa. Nas próximas publicações, tentaremos aprofundá-la com exemplos práticos: o quinto artigo da série será dedicado às questões de integração do PowerShell com componentes de software de terceiros. As partes anteriores podem ser encontradas nos links abaixo.
Fonte: habr.com
