Windows PowerShellA caixa de depósito

Don Jones

Conteúdo

Seja o cmdlet
A função de filtragem
Pense em termos físicos
Aplicações práticas
Reserve algum tempo para "brincar"

Em uma das aulas de Windows PowerShell que ministrei recentemente, alguns alunos tiveram dificuldade para visualizar do que se tratava o pipeline do shell. Admito que o pipeline não é um conceito totalmente intuitivo, e pode ser um tanto difícil de entender para quem busca o aprendizado a partir da visualização. Quando cheguei ao

conceito das funções de filtragem, que funcionam diretamente no pipeline, as coisas realmente se complicaram, e percebi, pela expressão no rosto dos alunos, que estava a ponto de perder alguns deles.

Para tentar ajudá-los, no dia seguinte, eu trouxe uma caixa, algumas etiquetas adesivas com nomes e algumas bolas de pingue-pongue (quem disse que o Windows PowerShell® não podia ser divertido?). Resolvi fazer uma demonstração usando a seguinte linha de comando:

Get-Process | Where { $_.VM –gt 1000 } | Sort VM
–descending | Export-CSV C:\Procs.csv

Seja o cmdlet

Usando as etiquetas adesivas com nomes — você sabe, aqueles adesivos com "Olá, meu nome é…" que você sempre precisa usar em reuniões escolares e outros eventos desagradáveis — atribuí um nome de cmdlet a cada aluno. Expliquei que as bolas de pingue-pongue representavam os objetos de processo do Windows® (mais especificamente, os objetos do tipo System.Diagnostics.Process), e então pedi a eles que dissessem o nome do cmdlet com maior probabilidade para gerar objetos de processo.

Olhando as etiquetas uns dos outros, eles concordaram com a opção óbvia de Get-Process. Isso demonstra um dos principais motivos pelos quais realmente gosto dos nomes de cmdlets usados no Windows PowerShell: em geral, são bastante óbvios.

Então, a aluna com a etiqueta Get-Process pegou todas as bolas de pingue-pongue e depositou-as na caixa de papelão. A caixa representa o pipeline do shell, e a aluna fez algo muito parecido com o que um cmdlet faz. Um cmdlet gera um ou mais objetos e os deposita no pipeline.

Então, o próximo cmdlet no pipeline assumiu o comando. Neste exemplo, o aluno representando o cmdlet Where-Object pegou todas as bolas de pingue-pongue e examinou-as, uma de cada vez, para verificar se a propriedade MV de uma determinada bola era maior do que 1.000. No shell, "MV" significa memória virtual e, para o exercício, escrevi com um marcador as diversas quantidades de memória virtual em cada bola de pingue-pongue.

Cada bola (também conhecida como processo) com uma MV de 1.000 ou mais voltava para a caixa (também denominada pipeline), enquanto as de valor menor eram despejadas em uma lata de lixo, desaparecendo para sempre (o que não é bem verdade, pois eu as resgatei para uso em aulas futuras).

Em seguida, o aluno representando Sort-Object analisou as bolas de pingue-pongue na caixa e colocou-as em ordem, com base na propriedade MV. Tenho de admitir que essa parte do exercício não foi muito bem planejada: foi um problema impedir que as bolas rolassem por toda parte! Acho que, para a próxima aula, precisarei encontrar cubos de pingue-pongue, ou então trazer também algumas caixas de ovos.

Por fim, o aluno representando Export-CSV pegou as bolas e escreveu as informações em um arquivo CSV. Em termos físicos, isso significa que ele escreveu as propriedades dos processos em um flip chart que fingimos ser um arquivo CSV.

A função de filtragem

Com um simples pipeline fora do caminho, resolvemos nos concentrar nas funções de filtragem, um pouco mais complicadas. Propus a função de filtragem e a linha de comando mostradas na Figura 1.

Figura 1 — exemplo de função de filtragem e linha de comando

Function Do-Something {
 BEGIN { }
 PROCESS {
  $obj = New-Object PSObject
  $obj | Add-Member NoteProperty "TimeStamp" (Get-Date)
  $obj | Add-Member NoteProperty "ProcessName" ($_.Name)
  Write-Output $obj
 }
 END {}
}
Get-Process | Do-Something | Format-Table *

Primeiro, quero apresentar uma rápida revisão do que as funções de filtragem se destinam a fazer. A idéia é que a função contém três blocos de scripts chamados BEGIN, PROCESS e END.

Quando a função é usada no pipeline, o bloco de scripts BEGIN é executado primeiro. Esse script pode realizar qualquer tarefa de configuração, como abrir uma conexão com o banco de dados.

Então, o bloco de scripts PROCESS é executado uma vez para cada objeto canalizado na função. No bloco de scripts PROCESS, a variável $_ é preenchida automaticamente com o objeto de pipeline atual. Então, se dez objetos forem canalizados, o bloco de scripts PROCESS será executado dez vezes.

Por fim, depois que todos os objetos canalizados tiverem sido executados, o bloco de scripts END será executado e realizará as tarefas de limpeza necessárias, como fechar uma conexão com o banco de dados.

Em qualquer um desses blocos de scripts, todos os elementos escritos com o uso de Write-Output terminam no pipeline para o próximo cmdlet. Todos os elementos escritos sem o uso de Write-Output são descartados.

Como nosso comando começava com Get-Process, a mesma aluna de antes reuniu todas as bolas de pingue-pongue (saímos para um breve intervalo e, de alguma forma, as bolas foram espalhadas por toda a sala, imagine!) e depositou-as na caixa do pipeline. Quando ela concluiu a tarefa, o aluno representando a função de filtragem "Do-Something" assumiu o controle.

Primeiro, ele executou seu bloco de scripts BEGIN. Como estava vazio, isso não exigiu muito esforço. Em seguida, pegou todos os objetos do pipeline (as bolas de pingue-pongue) e começou a examinar um de cada vez. Isso foi um tanto engraçado, pois havia cerca de uma dúzia de bolas, e ele tentou equilibrar todas em seu colo — mas acho que você precisaria presenciar a cena.

De qualquer forma, ele pegou um objeto de processo por vez e executou seu bloco de scripts PROCESS. Esse bloco de scripts fez com que ele criasse um novo objeto personalizado (usei para isso bolas de pingue-pongue amarelas especiais, e não pense que foi fácil encontrá-las na loja de esportes local). Nessas novas bolas de pingue-pongue, ele escreveu a data e a hora atuais, além do nome do objeto de processo que estava examinando naquele momento.

Esse novo objeto personalizado foi, então, gravado no pipeline, ou seja, o aluno colocou a bola de pingue-pongue amarela na caixa, e o objeto de processo original (a bola de pingue-pongue branca) foi descartado. Ele repetiu essa operação para cada objeto de processo (bola de pingue-pongue) na caixa, totalizando cerca de uma dúzia. Quando terminou, cada bola de pingue-pongue branca havia sido substituída por um objeto personalizado (bola de pingue-pongue amarela). Por fim, ele executou seu bloco de scripts END, que estava vazio e não demorou nem um pouco.

Para concluir, a aluna Format-Table assumiu o controle. Ela pegou todo o conteúdo da caixa — agora, constituído somente por objetos "personalizados" amarelos — e começou a construir uma tabela, usando as duas propriedades escritas em cada bola. O resultado foi uma tabela, escrita em nosso flip chart, com duas colunas — TimeStamp e ProcessName — e cerca de uma dúzia de linhas.

Pense em termos físicos

O exercício tornou o pipeline e as funções de filtragem realmente claros para todos os alunos da turma. As bolas de pingue-pongue funcionaram muito bem representando objetos, algo que tende a tornar-se um pouco abstrato quando você apenas fala sobre o shell.

Todos conseguiram perceber como os cmdlets manipularam os objetos; como os resultados foram colocados no pipeline; e como o cmdlet seguinte coletou os resultados e realizou manipulações adicionais nos objetos. A seqüência de eventos em uma função de filtragem era mais evidente, assim como a técnica do bloco de scripts PROCESS, de trabalhar com um objeto de entrada por vez.

Isso demonstrou também os meios pelos quais um novo objeto personalizado pode ser criado e colocado no pipeline. O sistema ajuda, ainda, a demonstrar as vantagens de produzir objetos personalizados, em oposição ao texto simples — os novos objetos personalizados puderam ser consumidos por outros cmdlets, como Format-Table, oferecendo uma grande flexibilidade na forma como os dados poderiam ser usados e representados.

Aplicações práticas

Logicamente, os alunos perguntaram como é possível usar o pipeline e as funções de filtragem que demonstramos em aplicações práticas. Para mim, foi fácil responder, pois produzi vários exemplos para uma sessão apresentada recentemente em uma conferência (você pode baixar esses exemplos em scriptinganswers.com/essentials/index.php/2008/03/25/techmentor-2008-san-francisco-auditing-examples).

Um dos exemplos consistia na meta de listar várias propriedades da classe Win32_UserAccount na WMI (Instrumentação de Gerenciamento do Windows) de diversos computadores. Supondo que os nomes dos computadores estejam listados em C:\Computers.txt, este comando simples dará conta da tarefa:

Gwmi win32_useraccount –comp (gc c:\computers.txt)

O problema é que a classe Win32_UserAccount não inclui uma propriedade que informe de que computador veio cada instância. Então, a lista resultante será um amontoado inútil de contas de vários computadores. Eu soluciono esse problema criando um novo objeto personalizado que inclui o nome do computador de origem, assim como propriedades de classe selecionadas nas quais tenho interesse. O código desse objeto personalizado é mostrado na Figura 2.

Figura 2 — usando um objeto personalizado para coletar nomes de computadores e selecionar propriedades

function Get-UserInventory {
  PROCESS {
    # $_ is a computer name
    $users = gwmi win32_useraccount -ComputerName $_
    foreach ($user in $users) {
      $obj = New-Object
      $obj | Add-Member NoteProperty Computer $_
      $obj | Add-Member NotePropertyPasswordExpires ($user.PasswordExpires)
      $obj | Add-Member NoteProperty Disabled ($user.Disabled)
      $obj | Add-Member NotePropertyFullName ($user.FullName)
      $obj | Add-Member NoteProperty Lockout ($user.Lockout)
      $obj | Add-Member NoteProperty Name ($user.Name)
      $obj | Add-Member NotePropertyPasswordRequired ($user.PasswordRequired)
      $obj | Add-Member NotePropertyPasswordChangeable ($user.PasswordChangeable)
    }
  }
}

Get-Content c:\computers.txt | 
  Get-UserInventory | 
  where { $_.PasswordRequired -eq $False } | 
  selectComputer,Name | 
  Export-Csv c:\BasUsersBad.csv

A linha de comando final transmite todos os nomes de computadores para a função de filtragem, que produz os objetos personalizados. Em seguida, filtro todos os usuários, exceto aqueles com a propriedade PasswordRequired definida como False (a idéia é produzir um relatório de auditoria das contas problemáticas). Então, simplesmente mantenho as propriedades Computer e Name, de forma que o relatório final seja uma lista de nomes de computadores e nomes de contas que requerem atenção, por terem senhas que não perdem a validade. A função de filtragem torna possível esse relatório sobre vários computadores, pois adiciona o nome do computador de origem à saída, ao mesmo tempo em que reduz as propriedades somente àquelas que tenho interesse em ver.

Embora haja outras formas semelhantes de realizar a tarefa, esta abordagem é provavelmente a mais direta, e serve também para ilustrar conceitos e técnicas importantes.

Reserve algum tempo para "brincar"

Mesmo que você já esteja seguro em relação ao pipeline, há uma lição a ser aprendida aqui. Pensar em termos de objetos físicos pode ajudá-lo a visualizar o que está tentando fazer.

Então, da próxima vez que tiver dificuldades com algum conceito do Windows PowerShell, tente sair um pouco da frente do computador e replicar a tarefa usando objetos do cotidiano como ilustração. Sugiro que você tenha sempre à mão uma sacola com bolas de pingue-pongue (ou cubos, se conseguir encontrá-los) para essa finalidade.

Cmdlet do mês: Out-File

Quantas vezes você já direcionou a saída de um cmdlet para um arquivo usando o símbolo >? Isso significa algo do tipo Get-Process > Processes.txt. Você sabia que, na verdade, está apenas usando o cmdlet Out-File disfarçado? Eis aqui um comando que realiza exatamente a mesma função: Get-Process | Out-File Processes.txt.

Logicamente, isso exige um pouco mais de digitação, então por que ter o trabalho de digitar o cmdlet Out-File completo? Um dos motivos é que a leitura de Out-File é muito mais clara. Digamos que alguém, daqui a seis meses, observe um dos seus scripts e se pergunte o que significa > (que é, afinal de contas, uma espécie de elemento legado).

Por outro lado, Out-File torna bastante óbvio que um arquivo será criado e uma gravação será feita nele. Além disso, Out-File fornece o parâmetro -append (muito semelhante a >>), juntamente com os parâmetros -force e -noClobber, permitindo que você controle se os arquivos existentes serão substituídos. Por fim, digitar Out-File fornece também acesso ao parâmetro -whatif. Esse parâmetro é muito útil e mostra o que Out-File fará, antes que seja feito! É uma ótima forma de testar uma linha de comando complicada, com pouco ou nenhum risco.

Don Jones é co-autor de Windows PowerShell: TFM e dá aulas de Windows PowerShell (www.ScriptingTraining.com).

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