Windows PowerShellUma boa aparência

Don Jones

Não é incomum querer que seus scripts produzam uma saída com uma boa aparência, e muitas vezes me perguntam sobre a melhor maneira de produzir coisas como tabelas. Existem realmente duas formas de fazer isso. A primeira etapa é escrever um script que coloque os dados — isto é, quaisquer dados que eu queira exibir de um modo formatado — em variáveis chamadas

$data1, $data2 e $data3. Agora posso falar da formatação dos dados.

O método baseado em texto

Na Web

Você está procurando uma demonstração rápida das técnicas discutidas na coluna do Windows PowerShell deste mês? Vá até technetmagazine.com/video para ver Don Jones colocar esses cmdlets em ação.

A maneira mais comum com que as pessoas tendem a abordar essa tarefa é a mesma forma que com tratariam dela se estivessem trabalhando em uma linguagem como VBScript, Perl, KiXtart ou outra linguagem orientada por texto. Eu poderia começar escrevendo um conjunto de cabeçalhos de coluna na tela:

Write-Host "ColumnA'tColumnB'tColumnC"

Observe que o`t é uma seqüência de escape especial no Windows PowerShellTM que insere um caractere de tabulação. Para escrever cada linha da minha tabela — lembrando que meus dados estão em $data1, $data2 e $data3 — possuo algumas opções. Uma é simplesmente escrever as variáveis, confiando na capacidade do shell de substituir variáveis pelo seu conteúdo entre aspas duplas:

Write-Host "$data1't$data2't$data3"

Outra maneira ligeiramente mais atraente para atingir esse objetivo usa o operador –f. Essa técnica separa a formatação dos dados e faz o que eu considero uma linha de código mais fácil de ler e de manutenção mais fácil:

Write-Host "{0}'t{1}'t{3}" –f $data1,$data2,$data3

Toda essa abordagem possui problemas significativos, no entanto. Primeiro, confiando nas paradas de tabulação fixas na janela do console, estou me preparando para uma formatação estranha. Por exemplo, se uma linha da tabela particular possuir um valor de 20 caracteres na primeira coluna, eu lanço toda a formatação dessa linha fora do lugar. Além disso, como estou enviando esse texto usando o Write-Host, estou bem limitado a uma exibição de console.

Não há maneira fácil de enviar essa saída a um arquivo ou colocá-la em outros formatos, se eu alguma vez desejar fazer isso. Mais importante, essa abordagem baseada em texto ignora completamente o shell inerentemente baseado em objeto no qual estou trabalhando, falhando em se beneficiar de todas as técnicas e recursos incríveis que o Windows PowerShell oferece.

O método baseado no Windows PowerShell

O Windows PowerShell é um shell orientado a objeto. Isso significa, idealmente, que tudo com que você trabalha deve estar em objetos, permitindo que o shell transforme as coisas em exibições de texto quando necessário. Mas como você cria objetos para partes arbitrárias de dados?

Continuando meu exemplo, eu começo criando um objeto personalizado em branco e armazenando-o em uma variável:

 $obj = New-Object PSObject

Isso me fornece um objeto vazio e novo com o qual trabalhar. Em seguida, adiciono meus dados ao objeto na forma de propriedades. Para isso, simplesmente canalizo meu objeto para o cmdlet Add-Member. Adiciono algo chamado de NoteProperty, dou à propriedade um nome (uma boa idéia é usar os cabeçalhos da coluna como nomes de propriedade) e insiro os dados como os valores para as propriedades:

 $obj | Add-Member NotePropertyColumnA $data1
$obj | Add-Member NotePropertyColumnB $data2
$obj | Add-Member NotePropertyColumnC $data3

Agora, simplesmente envio esse objeto — não ao console, mas ao pipeline:

Write-Output $obj

Posso então repetir essas etapas para cada linha da tabela que preciso enviar. A seguir, uma função curta que aceita uma cadeia de caracteres e envia uma versão em maiúscula e minúscula, juntamente com a cadeia de caracteres original:

functionStringVersions {
param([string]$inputString)
  $obj = New-Object PSObject
  $obj | Add-Member NoteProperty Original($inputString)
  $obj | Add-Member NoteProperty Uppercase($inputString.ToUpper())
  $obj | Add-Member NoteProperty Lowercase($inputString.ToLower())
  Write-Output $obj
}  
$strings = @("one","two","three")
foreach ($item in $strings) {
StringVersions $item
}

Começa com uma matriz de variáveis de cadeias de caracteres, passando por cada uma delas por vez e enviando-as para a função. E a saída da função é escrita no pipeline.

Você deve observar que existem maneiras mais curtas de escrever esse código, mas escolhi essa técnica porque ela claramente ilustra o ponto que desejo fazer. O resultado, como mostra a Figura 1, é uma tabela perfeitamente formatada. Isso porque o shell já sabe como formatar objetos em uma tabela.

Figura 1 Saída do Windows PowerShell exibida em uma tabela

Figura 1** Saída do Windows PowerShell exibida em uma tabela **(Clique na imagem para uma visão ampliada)

Sempre que um objeto possuir quatro ou menos propriedades, o Windows PowerShell escolhe uma tabela automaticamente. Então, sem nenhum trabalho qualquer da minha parte, agora possuo uma tabela com uma ótima aparência.

Mas, espere; tem mais!

Cmdlet do mês: Get-Command

Tenho certeza que você usou o Get-Command, ou seu alias útil (gcm), uma ou duas vezes para rever a lista de cmdlets disponíveis do Windows PowerShell. Mas você pode não saber como o gcm realmente é flexível. Por exemplo, se você deseja ver tudo o que o Windows PowerShell pode fazer com um serviço, execute o -noun service do gcm. Ou, se deseja ver todas as opções de exportação do Windows PowerShell, experimente o -verb export do gcm. Se apenas precisa ver quais cmdlets foram adicionados por um snap-in particular, como as Extensões de Comunidade do PowerShell, experimente o -pssnapin pscx do gcm. (Você pode substituir "pscx" por qualquer nome de snap-in para ver os cmdlets desse snap-in.)

Como você pode ver, o Get-Command desempenha um papel fundamental na capacidade de detecção do Windows PowerShell. Ele permite que você aprenda qual funcionalidade está disponível sem mesmo ter que ler um manual. E note que o gcm é uma maneira mais precisa de detectar a funcionalidade do que usar um comando como Help *. A função de Ajuda lista apenas tópicos de ajuda disponíveis. Qualquer cmdlet não enviado com a ajuda não é exibido na listagem da ajuda — embora os cmdlets estejam lá, se precisar deles.

O benefício dessa técnica vai bem além das tabelas. Quando você está trabalhando com objetos, o Windows PowerShell sabe como fazer uma enorme variedade de coisas. Quer seus dados em um arquivo CSV? Use o Export-CSV. Prefere uma tabela HTML? Canalize seus objetos para ConvertTo-HTML. Precisa de um formato de lista? Canalize-os para Format-List. Usando objetos, você pode utilizar todas essas coisas que o shell já sabe como fazer. Veja um exemplo revisado:

functionStringVersions {
  PROCESS {
   $obj = New-Object PSObject
   $obj | Add-Member NoteProperty Original($_)
   $obj | Add-Member NoteProperty Uppercase($_.ToUpper())
   $obj | Add-Member NoteProperty Lowercase($_.ToLower())
   Write-Output $obj
}
}

Desta vez, modifiquei a função para aceitar a entrada de pipeline — essa sempre é uma idéia melhor quando você está trabalhando com objetos — e para enviar ao pipeline.

Essa função agora possui seu código dentro de um scriptblock PROCESS. Isso significa que a função aceitará a entrada do pipeline e executará o scriptblock PROCESS uma vez para cada objeto canalizado.

No scriptblock PROCESS, a variável especial $_ é uma referência ao objeto do pipeline atual que está sendo processado. O resultado prático disso é que agora eu simplesmente posso canalizar em uma matriz de cadeias de caracteres:

@("one","two","three") | StringVersions

Eu obtenho uma tabela porque a função está colocando sua saída no pipeline. No final do pipeline, o shell sabe chamar seu subsistema de formatação, que toma a decisão de usar uma tabela porque os objetos no pipeline possuem menos de cinco propriedades (para mais propriedades, o shell usará uma lista por padrão).

Mas eu não preciso confiar no comportamento padrão. Posso simplesmente canalizar esses objetos em outro cmdlet para fazer algo diferente com eles:

@("one","two","three") | StringVersions | Format-List
@("one","two","three") | StringVersions | ConvertTo-HTML | Out-File "strings.html"
@("one","two","three") | StringVersions | Export-CSV "strings.csv"
@("one","two","three") | StringVersions | Select Uppercase,Lowercase -unique

A Figura 2 mostra o HTML resultante do segundo exemplo exibido em um navegador da Web. Simplesmente representando meus dados de saída em objetos, em vez de representá-los como texto simples, agora, de repente, eu possuo acesso completo a uma riqueza de funcionalidades incorporadas no shell — uma variedade de layouts de formatação; conversão HTML; opções de exportação; a capacidade de classificar, filtrar e agrupar e mais.

Figura 2 Saída de dados do Windows PowerShell em formato HTML

Figura 2** Saída de dados do Windows PowerShell em formato HTML **(Clique na imagem para uma visão ampliada)

Isso é algo muito poderoso. Na verdade, pararei aqui para sugerir que todos os scripts escritos devem produzir um objeto como sua saída, de forma que você possa usar essa saída de várias maneiras diferentes quantas forem possíveis — tudo sem escrever uma única linha de código adicional.

Don Jones é especialista em automação administrativa em Windows e criou livros como Windows PowerShell: TFM e VBScript, WMI, and ADSI Unleashed (Liberando o potencial de VBScript, WMI e ADSI). Entre em contato com ele pelos fóruns em ScriptingAnswers.com.

© 2008 Microsoft Corporation e CMP Media, LLC. Todos os direitos reservados; é proibida a reprodução parcial ou completa sem autorização..