TÓPICO about_pipelines DESCRIÇÃO RESUMIDA Combinação de comandos em pipelines no Windows PowerShell DESCRIÇÃO LONGA Um pipeline é uma série de comandos conectada por operadores de pipeline (|) (ASCII 124). Cada operador de pipeline envia os resultados do comando anterior ao próximo comando. Você pode usar pipelines para enviar os objetos que são produzidos por um comando para serem usados como entrada para outro comando para processar. E ainda você pode enviar a saída daquele comando para outro comando. O resultado é uma cadeia de comando muito avançada ou "pipeline" que é composto por uma série de comandos simples. Por exemplo, Command-1 | Command-2 | Command-3 Nesse exemplo, os objetos que Command-1 emite são enviados para Command-2. Command-2 processa os objetos e os envia para Command-3. Command-3 processa os objetos e os envia para o pipeline. Como não há mais comandos no pipeline, os resultados são exibidos no console. Em um pipeline, os comandos são processados da esquerda para a direita na ordem que eles aparecem. O processamento é tratado como uma única operação e a saída é exibida da forma que é gerada. Aqui está um exemplo simples. O comando a seguir obtém o processo do Bloco de Notas e, em seguida, para-o. get-process notepad | stop-process O primeiro comando usa o cmdlet Get-Process para obter a um objeto que representa o processo do Bloco de Notas. Ele usa um operador de pipeline (|) para enviar o objeto de processo para o cmdlet Stop-Process, que para o processo do Bloco de Notas. Note que o comando Stop-Process não tem um parâmetro Name ou ID para especificar o processo, porque o processo especificado é enviado pelo pipeline. Aqui está um exemplo prático. Esse pipeline de comando obtém os arquivos de texto no diretório atual, seleciona apenas os arquivos que têm mais que 10.000 bytes, os classifica por comprimento e exibe o nome e comprimento de cada arquivo em uma tabela. Get-ChildItem -path *.txt | Where-Object {$_.length -gt 10000} | Sort-Object -property Length | Format-Table -property name, length Esse pipeline é composto por quatro comandos na ordem especificada. O comando é escrito horizontalmente, mas mostraremos o processo verticalmente no gráfico a seguir. Get-ChildItem -path *.txt | | (FileInfo objects ) | ( .txt ) | V Where-Object {$_.length -gt 10000} | | (FileInfo objects ) | ( .txt ) | ( Length > 10000 ) | V Sort-Object -property Length | | (FileInfo objects ) | ( .txt ) | ( Length > 10000 ) | ( Sorted by length ) | V Format-Table -property name, length | | (FileInfo objects ) | ( .txt ) | ( Length > 10000 ) | ( Sorted by length ) | (Formatted in a table ) | V Name Length ---- ------ tmp1.txt 82920 tmp2.txt 114000 tmp3.txt 114000 USANDO PIPELINES Os cmdlets do Windows PowerShell foram criados para serem usados em pipelines. Por exemplo, você pode enviar normalmente os resultados de um cmdlet Get a um cmdlet de ação (como um cmdlet Set, Start, Stop ou Rename) para o mesmo substantivo. Por exemplo, você pode enviar qualquer serviço do cmdlet Get-Service para o cmdlet Start-Service ou Stop-Service (embora serviços desabilitados não possam ser reiniciados desse modo). Esse pipeline de comando inicia o serviço WMI no computador: get-service wmi | start-service Os cmdlets que obtêm e definem objetos dos provedores do Windows PowerShell, como os cmdlets Item e ItemProperty, também são criados para serem usados em pipelines. Por exemplo, você pode enviar os resultados de um comando Get-Item ou Get-ChildItem no provedor de Registro do Windows PowerShell para o cmdlet New-ItemProperty. Este comando adiciona uma nova entrada do Registro, NoOfEmployees, com o valor de 8124, à chave de Registro MyCompany. get-item -path HKLM:\Software\MyCompany | new-Itemproperty -name NoOfEmployees -value 8124 Muitos dos cmdlets de utilitário, como Get-Member, Where-Object, Sort-Object, Group-Object e Measure-Object são usados quase exclusivamente em pipelines. Você pode enviar qualquer objeto a esses cmdlets. Por exemplo, você pode enviar todos os processos no computador para o comando Sort-Object e tê-los classificado pelo número de manipulações no processo. get-process | sort-object -property handles Além disso, você pode enviar qualquer objeto para os cmdlets de formatação, como Format-List e Format-Table, os cmdlets de exportação, como Export-Clixml e Export-CSV e os cmdlets de saída, como Out-Printer. Por exemplo, você pode enviar o processo Winlogon ao cmdlet Format-List para exibir todas as propriedades do processo em uma lista. get-process winlogon | format-list -property * Com um pouco de prática, você verá que a combinação de comandos simples em pipelines economiza tempo e digitação e torna seu script mais eficiente. COMO OS PIPELINES FUNCIONAM Quando você "envia" objetos aos objetos na saída de um comando para outro comando, o Windows PowerShell tenta associar os objetos enviados a um dos parâmetros do cmdlet de recebimento. Para fazer isso, o componente "vinculação de parâmetro" do Windows PowerShell, que associa objetos de entrada a parâmetros de cmdlet, tenta localizar um parâmetro que atenda aos seguintes critérios: -- O parâmetro deve aceitar entrada de um pipeline (não todos aceitam) -- O parâmetro deve aceitar o tipo de objeto que é enviado ou um tipo para o qual o objeto pode ser convertido. -- O parâmetro ainda não deve ser usado no comando. Por exemplo, o cmdlet Start-Service tem muitos parâmetros, mas apenas dois deles, Name e InputObject, aceitam entrada do pipeline. O parâmetro Name obtém cadeias de caracteres e o parâmetro InputObject obtém objetos de serviço. Portanto, você pode enviar cadeias de caracteres e objetos de serviço (e objetos com propriedades que podem ser convertidas para cadeias de caracteres e objetos de serviço) para Start-Service. Se o componente de vinculação de parâmetro do Windows PowerShell não puder associar os objetos enviados a um parâmetro do cmdlet de recebimento, o comando falhará e o Windows PowerShell solicitará a você os valores de parâmetros não presentes. Você não pode forçar o componente de vinculação de parâmetro para associar os objetos enviados com um parâmetro específico -- você não pode nem mesmo sugerir um parâmetro. Em vez disso, a lógica do componente gerencia o envio do modo mais eficiente possível. PROCESSAMENTO UM DE CADA VEZ O envio de objetos a um comando é como o uso de um parâmetro do comando para enviar os objetos. Por exemplo, o envio de objetos que representam os serviços no computador para um comando Format-Table, como: get-service | format-table -property name, dependentservices É como salvar os objetos de serviço em uma variável e usar o parâmetro InputObject de Format-Table para enviar o objeto de serviço. $services = get-service format-table -inputobject $services -property name, dependentservices ou fixar o comando no valor de parâmetro format-table -inputobject (get-service wmi) -property name, dependentservices Contudo, há uma diferença importante. Quando você envia vários objetos para um comando, o Windows PowerShell envia os objetos ao comando um de cada vez. Quando você usa um parâmetro de comando, os objetos são enviados como um único objeto de matriz. Essa diferença aparentemente técnica pode ter consequências interessante e, às vezes, úteis. Por exemplo, se você enviar vários objetos de processo do cmdlet Get-Process para o cmdlet Get-Member, o Windows PowerShell envia cada objeto de processo, um de cada vez, ao Get-Member. Get-Member exibe a classe .NET (tipo) dos objetos de processo e suas propriedades e métodos. (Get-Member elimina duplicatas; assim, se os objetos forem todos do mesmo tipo, exibirá apenas um tipo de objeto.) Nesse caso, Get-Member exibe as propriedades e métodos de cada objeto de processo, isto é, um objeto System.Diagnostics.Process. get-process | get-member TypeName: System.Diagnostics.Process Name MemberType Definition ---- ---------- ---------- Handles AliasProperty Handles = Handlecount Name AliasProperty Name = ProcessName NPM AliasProperty NPM = NonpagedSystemMemorySize ... Contudo, se você usar o parâmetro InputObject de Get-Member, Get-Member receberá uma matriz do objeto System.Diagnostics.Proc ess como uma única unidade e exibe as propriedades de uma matriz de objetos. (Observe o símbolo de matriz ([]) depois que o System.Object digitem nome.) get-member -inputobject (get-process) TypeName: System.Object[] Name MemberType Definition ---- ---------- ---------- Count AliasProperty Count = Length Address Method System.Object& Address(Int32 ) Clone Method System.Object Clone() ... Esse resultado pode não ser o que você pretendia, mas depois de entendê-lo, pode usá-lo. Por exemplo, uma matriz de objetos de processo tem uma propriedade Count que você pode usar para contar o número de processos no computador. (get-process).count Essa diferença pode ser importante, portanto, lembre-se de que, quando você envia objetos a um cmdlet, eles são entregues um de cada vez. ACEITA ENTRADA DO PIPELINE Para receber objetos em um pipeline, o cmdlet de recebimento deve ter um parâmetro que aceite entrada do pipeline. Você pode usar um comando Get-Help com os parâmetros Full ou Parameter para determinar qual dos parâmetros de um cmdlet, se houver, aceita entrada do pipeline. Na exibição padrão de Get-Help, o item "Aceita entrada do pipeline" é exibido em uma tabela de atributos do parâmetro. Essa tabela é exibida somente quando você usa os parâmetros Full ou Parameter do cmdlet Get-Help. Por exemplo, para determinar qual dos parâmetros do cmdlet Start-Service aceita entrada do pipeline, digite: get-help start-service -full get-help start-service -parameter * Por exemplo, a ajuda para o cmdlet Start-Service mostra que os parâmetros Name e InputObject aceitam entrada do pipeline ("true"). Todos os outros parâmetros têm um valor de "false" na linha "Aceitar entrada do pipeline?". -name <string[]> Especifica os nomes para o serviço a ser iniciado. O nome do parâmetro é opcional. Você pode usar "-Name" ou seu alias, "-ServiceName", ou omitir o nome do parâmetro. Necessário? true Posição? 1 Valor padrão --> Aceitar entrada do pipeline? true (ByValue, ByPropertyName) Aceitar caracteres curinga? true -inputObject <ServiceController[]> Especifica os objetos ServiceController que representam os serviços que devem ser iniciados. Digite uma variável que contenha os objetos ou digite um comando ou expressão que obtenha os objetos. Necessário? false Posição? named Valor padrão --> Aceitar entrada do pipeline? true (ByValue) Aceitar caracteres curinga? false Isso significa que você pode enviar objetos (PsObjects) pelo pipeline para o cmdlet Where-Object e o Windows PowerShell associará o objeto ao parâmetro InputObject. MÉTODOS DE ACEITAÇÃO DE ENTRADA DO PIPELINE Os parâmetros de cmdlets podem aceitar entrada do pipeline de um destes dois modos: -- ByValue: os parâmetros que aceitam entrada "por valor" pode aceitar objetos enviados que têm o mesmo tipo .NET como seu valor de parâmetro ou objetos que podem ser convertidos naquele tipo. Por exemplo, o parâmetro Name de Start-Service aceita entrada do pipeline por valor. Ele pode aceitar objetos de cadeia de caracteres ou objetos que podem ser convertidos em cadeias de caracteres. -- ByPropertyName: os parâmetros que aceitam entrada "por nome de propriedade" podem aceitar objetos enviados somente quando uma propriedade do objeto tiver o mesmo nome que o parâmetro. Por exemplo, o parâmetro Name de Start-Service pode aceitar objetos que têm uma propriedade Name. (Para listar as propriedades de um objeto, envie-o para Get-Member.) Alguns parâmetros podem aceitar objetos por valor ou por nome de propriedade. Esses parâmetros são criados para obter a entrada do pipeline facilmente. INVESTIGANDO ERROS DE PIPELINE Se um comando falhar por causa de um erro de pipeline, você poderá investigar a falha e reescrever o comando. Por exemplo, o comando a seguir tenta mover uma entrada do Registro de uma chave do Registro para outra usando o cmdlet Get-Item para obter o caminho de destino e, em seguida, enviando o caminho para o cmdlet Move-ItemProperty. De modo específico, o comando usa o cmdlet Get-Item para obter o caminho de destino. Ele usa um operador de pipeline para enviar o resultado para o cmdlet Move-ItemProperty. O comando Move-ItemProperty especifica o caminho atual e nome da entrada do Registro a ser movida. get-item -path hklm:\software\mycompany\sales | move-itemproperty -path hklm:\software\mycompany\design -name product O comando falha e o Windows PowerShell exibe a seguinte mensagem de erro: Move-ItemProperty: o objeto de entrada não pode ser associado a nenhum parâmetro para o comando porque o comando não obtém a entrada do pipeline ou a entrada e suas propriedades não correspondem a nenhum dos parâmetros que obtêm entrada do pipeline. At line:1 char:23 + $a | move-itemproperty <<<< -path hklm:\software\mycompany\design -name product Para analisar, use o cmdlet Trace-Command para rastrear o componente de vinculação de parâmetro do Windows PowerShell. O comando a seguir rastreia o componente de vinculação de parâmetro enquanto o comando está sendo processado. Ele usa o parâmetro -pshost para exibir os resultados no console, e o comando -filepath para enviá-los ao arquivo debug.txt para referência futura. trace-command -name parameterbinding -expression {get-item -path hklm:\software\mycompany\sales | move-itemproperty -path hklm:\software\mycompany\ design -name product} -pshost -filepath debug.txt Os resultados do rastreamento são prolongados, mas eles mostram os valores sendo vinculados ao cmdlet Get-Item e, em seguida, os valores nomeados sendo vinculados ao cmdlet Move-ItemProperty. ... BIND NAMED cmd line args [Move-ItemProperty] BIND arg [hklm:\software\mycompany\design] to parameter [Path] ... BIND arg [product] to parameter [Name] .... BIND POSITIONAL cmd line args [Move-ItemProperty] ... Finalmente, ele mostra que a tentativa de vincular o caminho ao parâmetro Destination de Move-ItemProperty falhou. ... BIND PIPELINE object to parameters: [Move-ItemProperty] PIPELINE object TYPE = [Microsoft.Win32.RegistryKey] RESTORING pipeline parameter's original values Parameter [Destination] PIPELINE INPUT ValueFromPipelineByPropertyName NO COERCION Parameter [Credential] PIPELINE INPUT ValueFromPipelineByPropertyName NO COERCION ... Para analisar a falha, use o cmdlet Get-Help para exibir os atributos do parâmetro Destination. O comando a seguir obtém informações detalhadas sobre o parâmetro Destination. get-help move-itemproperty -parameter destination Os resultados mostram que Destination obtém a entrada do pipeline somente "por nome de propriedade". Isto é, o objeto enviado deve ter uma propriedade nomeada Destination. -destination <string> Especifica o caminho até o local de destino. Necessário? true Posição? 2 Valor padrão Aceitar entrada do pipeline? true (ByPropertyName) Aceitar caracteres curinga? true Para ver as propriedades do objeto sendo enviado ao cmdlet Move-ItemProperty, envie-o para o cmdlet Get-Member. O comando a seguir envia os resultados da primeira parte do comando para o cmdlet Get-Member. get-item -path hklm:\software\mycompany\sales | get-member A saída mostra que o item é um Microsoft.Win32.RegistryKey que não tem uma propriedade Destination. Isso explica por que o comando falhou. Para corrigir o comando, devemos especificar o destino no cmdlet Move-ItemProperty. Podemos usar um comando Get-ItemProperty para obter o caminho, mas o nome e o destino devem ser especificados na parte Move-ItemProperty do comando. get-item -path hklm:\software\mycompany\design | move-itemproperty -dest hklm:\software\mycompany\ design -name product Para verificar se o comando funcionou, use um comando Get-ItemProperty: get-itemproperty hklm:\software\mycompany\sales Os resultados mostram que a entrada do Registro de Produto foi movida a chave de Vendas. PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\ software\mycompany\sales PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\ software\mycompany PSChildName : sales PSDrive : HKLM PSProvider : Microsoft.PowerShell.Core\Registry Product : 18 CONSULTE TAMBÉM about_objects about_parameters about_command_syntax about_foreach