about_Pipelines

Descrição breve

Combinando comandos em pipelines no PowerShell

Descrição longa

Um pipeline é uma série de comandos conectados por operadores de dutos (|) (ASCII 124). Cada operador de pipeline envia os resultados do comando anterior para o próximo comando.

A saída do primeiro comando pode ser enviada para processamento como entrada para o segundo comando. E essa saída pode ser enviada para outro comando. O resultado é uma complexa cadeia de comandos ou pipeline que é composto por uma série de comandos simples.

Por exemplo,

Command-1 | Command-2 | Command-3

Neste exemplo, os objetos emitidos Command-1 são enviados para Command-2o . Command-2 processa os objetos e os envia para o Command-3. Command-3 processa os objetos e os envia pelo pipeline. Como não há mais comandos no pipeline, os resultados são exibidos no console.

Em um pipeline, os comandos são processados em ordem da esquerda para a direita. O processamento é tratado como uma única operação e a saída é exibida à medida que é gerada.

Veja um exemplo simples. O comando a seguir obtém o processo do bloco de notas e, em seguida, pára-lo.

Por exemplo,

Get-Process notepad | Stop-Process

O primeiro comando usa o Get-Process cmdlet para obter um objeto que representa o processo do Bloco de Notas. Ele usa um operador de pipeline (|) para enviar o objeto de processo para o Stop-Process cmdlet, que interrompe o processo do Bloco de Notas. Observe que o Stop-Process comando não tem um parâmetro Name ou ID para especificar o processo, porque o processo especificado é enviado por meio do pipeline.

Este exemplo de pipeline obtém os arquivos de texto no diretório atual, seleciona apenas os arquivos com mais de 10.000 bytes, os classifica por comprimento e exibe o nome e o 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 consiste em quatro comandos na ordem especificada. A ilustração a seguir mostra a saída de cada comando à medida que ele é passado para o próximo comando no pipeline.

Get-ChildItem -Path *.txt
| (FileInfo objects for *.txt)
V
Where-Object {$_.length -gt 10000}
| (FileInfo objects for *.txt)
| (      Length > 10000      )
V
Sort-Object -Property Length
| (FileInfo objects for *.txt)
| (      Length > 10000      )
| (     Sorted by length     )
V
Format-Table -Property name, length
| (FileInfo objects for *.txt)
| (      Length > 10000      )
| (     Sorted by length     )
| (   Formatted in a table   )
V

Name                       Length
----                       ------
tmp1.txt                    82920
tmp2.txt                   114000
tmp3.txt                   114000

Usando pipelines

A maioria dos cmdlets do PowerShell foi projetada para oferecer suporte a pipelines. Na maioria dos casos, você pode canalizar os resultados de um cmdlet Get para outro cmdlet do mesmo substantivo. Por exemplo, você pode canalizar a saída do Get-Service cmdlet para os Start-Service cmdlets ou Stop-Service .

Este pipeline de exemplo inicia o serviço WMI no computador:

Get-Service wmi | Start-Service

Para outro exemplo, você pode canalizar a saída de ou Get-ChildItem dentro do provedor de Get-Item registro do PowerShell para o New-ItemProperty cmdlet. Este exemplo adiciona uma nova entrada do Registro, NoOfEmployees, com um valor de 8124, à chave do Registro MyCompany.

Get-Item -Path HKLM:\Software\MyCompany |
  New-ItemProperty -Name NoOfEmployees -Value 8124

Muitos dos cmdlets do utilitário, como Get-Member, Where-Object, Sort-Object, Group-Objecte Measure-Object são usados quase exclusivamente em pipelines. Você pode canalizar qualquer tipo de objeto para esses cmdlets. Este exemplo mostra como classificar todos os processos no computador pelo número de identificadores abertos em cada processo.

Get-Process | Sort-Object -Property handles

Você pode canalizar objetos para os cmdlets de formatação, exportação e saída, como Format-List, Format-Table, Export-Clixml, Export-CSVe Out-File.

Este exemplo mostra como usar o Format-List cmdlet para exibir uma lista de propriedades de um objeto de processo.

Get-Process winlogon | Format-List -Property *

Você também pode canalizar a saída de comandos nativos para cmdlets do PowerShell. Por exemplo:

PS> ipconfig.exe | Select-String -Pattern 'IPv4'

   IPv4 Address. . . . . . . . . . . : 172.24.80.1
   IPv4 Address. . . . . . . . . . . : 192.168.1.45
   IPv4 Address. . . . . . . . . . . : 100.64.108.37

Importante

Os fluxos Success e Error são semelhantes aos fluxos stdin e stderr de outros shells. No entanto, o stdin não está conectado ao pipeline do PowerShell para entrada. Para obter mais informações, consulte about_Redirection.

Com um pouco de prática, você descobrirá que combinar comandos simples em pipelines economiza tempo e digitação, além de tornar seus scripts mais eficientes.

Como funcionam os pipelines

Esta seção explica como os objetos de entrada são vinculados aos parâmetros do cmdlet e processados durante a execução do pipeline.

Aceita entrada de pipeline

Para dar suporte ao pipelining, o cmdlet de recebimento deve ter um parâmetro que aceite a entrada do pipeline. Use o Get-Help comando com as opções Full ou Parameter para determinar quais parâmetros de um cmdlet aceitam entrada de pipeline.

Por exemplo, para determinar qual dos parâmetros do Start-Service cmdlet aceita entrada de pipeline, digite:

Get-Help Start-Service -Full

or

Get-Help Start-Service -Parameter *

A ajuda para o Start-Service cmdlet mostra que somente os parâmetros InputObject e Name aceitam a entrada de pipeline.

-InputObject <ServiceController[]>
Specifies ServiceController objects representing the services to be started.
Enter a variable that contains the objects, or type a command or expression
that gets the objects.

Required?                    true
Position?                    0
Default value                None
Accept pipeline input?       True (ByValue)
Accept wildcard characters?  false

-Name <String[]>
Specifies the service names for the service to be started.

The parameter name is optional. You can use Name or its alias, ServiceName,
or you can omit the parameter name.

Required?                    true
Position?                    0
Default value                None
Accept pipeline input?       True (ByPropertyName, ByValue)
Accept wildcard characters?  false

Quando você envia objetos pelo pipeline para Start-Serviceo , o PowerShell tenta associar os objetos aos parâmetros InputObject e Name .

Métodos de aceitação de entrada de pipeline

Os parâmetros de cmdlets podem aceitar a entrada de pipeline de duas maneiras diferentes:

  • ByValue: O parâmetro aceita valores que correspondem ao tipo .NET esperado ou que podem ser convertidos para esse tipo.

    Por exemplo, o parâmetro Name de aceita entrada de Start-Service pipeline por valor. Ele pode aceitar objetos de cadeia de caracteres ou objetos que podem ser convertidos em cadeias de caracteres.

  • ByPropertyName: O parâmetro aceita entrada somente quando o objeto de entrada tem uma propriedade do mesmo nome que o parâmetro.

    Por exemplo, o parâmetro Name de Start-Service pode aceitar objetos que tenham uma propriedade Name . Para listar as propriedades de um objeto, canalize-o para Get-Member.

Alguns parâmetros podem aceitar objetos por valor ou nome de propriedade, facilitando a entrada do pipeline.

Associação de parâmetro

Quando você canaliza objetos de um comando para outro, o PowerShell tenta associar os objetos canalizados a um parâmetro do cmdlet de recebimento.

O componente de vinculação de parâmetros do PowerShell associa os objetos de entrada aos parâmetros do cmdlet de acordo com os seguintes critérios:

  • O parâmetro deve aceitar entrada de um pipeline.
  • O parâmetro deve aceitar o tipo de objeto que está sendo enviado ou um tipo que pode ser convertido para o tipo esperado.
  • O parâmetro não foi usado no comando.

Por exemplo, o Start-Service cmdlet tem muitos parâmetros, mas apenas dois deles, Name e InputObject, aceitam entrada de pipeline. O parâmetro Name usa cadeias de caracteres e o parâmetro InputObject usa objetos de serviço. Portanto, você pode canalizar cadeias de caracteres, objetos de serviço e objetos com propriedades que podem ser convertidas em objetos de cadeia de caracteres ou de serviço.

O PowerShell gerencia a vinculação de parâmetros da forma mais eficiente possível. Você não pode sugerir ou forçar o PowerShell a se vincular a um parâmetro específico. O comando falhará se o PowerShell não puder vincular os objetos canalizados.

Para obter mais informações sobre como solucionar erros de vinculação, consulte Investigando erros de pipeline posteriormente neste artigo.

Processamento único

Canalizar objetos para um comando é muito parecido com usar um parâmetro do comando para enviar os objetos. Vejamos um exemplo de pipeline. Neste exemplo, usamos um pipeline para exibir uma tabela de objetos de serviço.

Get-Service | Format-Table -Property Name, DependentServices

Funcionalmente, isso é como usar o parâmetro InputObject de para enviar a coleção de Format-Table objetos.

Por exemplo, podemos salvar a coleção de serviços em uma variável que é passada usando o parâmetro InputObject .

$services = Get-Service
Format-Table -InputObject $services -Property Name, DependentServices

Ou podemos incorporar o comando no parâmetro InputObject .

Format-Table -InputObject (Get-Service) -Property Name, DependentServices

No entanto, há uma diferença importante. Quando você canaliza vários objetos para um comando, o PowerShell envia os objetos para o 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 pequena diferença tem consequências significativas.

Ao executar um pipeline, o PowerShell enumera automaticamente qualquer tipo que implemente a IEnumerable interface ou sua contraparte genérica. Os itens enumerados são enviados pelo pipeline, um de cada vez. O PowerShell também enumera tipos System.Data.DataTable por meio da Rows propriedade.

Há algumas exceções à enumeração automática.

  • Você deve chamar o GetEnumerator() método para tabelas de hash, tipos que implementam a IDictionary interface ou sua contraparte genérica e tipos System.Xml.XmlNode.
  • A classe System.String implementa IEnumerable, no entanto, o PowerShell não enumera objetos de cadeia de caracteres.

Nos exemplos a seguir, uma matriz e uma hashtable são canalizadas para o Measure-Object cmdlet para contar o número de objetos recebidos do pipeline. A matriz tem vários membros e a hashtable tem vários pares chave-valor. Somente a matriz é enumerada uma de cada vez.

@(1,2,3) | Measure-Object
Count    : 3
Average  :
Sum      :
Maximum  :
Minimum  :
Property :
@{"One"=1;"Two"=2} | Measure-Object
Count    : 1
Average  :
Sum      :
Maximum  :
Minimum  :
Property :

Da mesma forma, se você canalizar vários objetos de processo do cmdlet para o cmdlet, o Get-Member PowerShell enviará cada objeto de Get-Process processo, um de cada vez, para Get-Membero . Get-Member exibe a classe .NET (tipo) dos objetos de processo e suas propriedades e métodos.

Get-Process | Get-Member
TypeName: System.Diagnostics.Process

Name      MemberType     Definition
----      ----------     ----------
Handles   AliasProperty  Handles = Handlecount
Name      AliasProperty  Name = ProcessName
NPM       AliasProperty  NPM = NonpagedSystemMemorySize
...

Observação

Get-Member Elimina duplicatas, portanto, se os objetos forem todos do mesmo tipo, ele exibirá apenas um tipo de objeto.

No entanto, se você usar o parâmetro InputObject do Get-Member, receberá Get-Member uma matriz de objetos System.Diagnostics.Process como uma única unidade. Ele exibe as propriedades de uma matriz de objetos. (Observe o símbolo da matriz ([]) após o nome do tipo System.Object .)

Por exemplo,

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 pretendido. Mas depois de entendê-lo, você pode usá-lo. Por exemplo, todos os objetos de matriz têm uma propriedade Count . Você pode usar isso para contar o número de processos em execução no computador.

Por exemplo,

(Get-Process).count

É importante lembrar que os objetos enviados pelo pipeline são entregues um de cada vez.

Usando comandos nativos no pipeline

O PowerShell permite que você inclua comandos externos nativos no pipeline. No entanto, é importante observar que o pipeline do PowerShell é orientado a objetos e não oferece suporte a dados de bytes brutos.

Canalizar ou redirecionar a saída de um programa nativo que produz dados de bytes brutos converte a saída em cadeias de caracteres .NET. Essa conversão pode causar corrupção da saída de dados brutos.

No entanto, o PowerShell 7.4 adicionou o recurso experimental que preserva dados PSNativeCommandPreserveBytePipe de fluxo de bytes ao redirecionar o fluxo stdout de um comando nativo para um arquivo ou ao canalizar dados de fluxo de bytes para o fluxo stdin de um comando nativo.

Por exemplo, usando o comando curl nativo, você pode baixar um arquivo binário e salvá-lo em disco usando o redirecionamento.

$uri = 'https://github.com/PowerShell/PowerShell/releases/download/v7.3.4/powershell-7.3.4-linux-arm64.tar.gz'

# native command redirected to a file
curl -s -L $uri > powershell.tar.gz

Você também pode redirecionar os dados de fluxo de bytes para o fluxo stdin de outro comando nativo. O exemplo a seguir baixa um arquivo TAR compactado usando curl. Os dados do arquivo baixado são transmitidos ao comando tar para extrair o conteúdo do arquivo morto.

# native command output piped to a native command
curl -s -L $uri | tar -xzvf - -C .

Você também pode redirecionar a saída de fluxo de bytes de um comando do PowerShell para a entrada do comando nativo. Os exemplos a seguir usam Invoke-WebRequest para baixar o mesmo arquivo TAR do exemplo anterior.

# byte stream piped to a native command
(Invoke-WebRequest $uri).Content | tar -xzvf - -C .

# bytes piped to a native command (all at once as byte[])
,(Invoke-WebRequest $uri).Content | tar -xzvf - -C .

Esse recurso não dá suporte a dados de fluxo de bytes ao redirecionar a saída stderr para stdout. Quando você combina os fluxos stderr e stdout, os fluxos combinados são tratados como dados de cadeia de caracteres.

Investigando erros de pipeline

Quando o PowerShell não consegue associar os objetos canalizados a um parâmetro do cmdlet de recebimento, o comando falha.

No exemplo a seguir, tentamos mover uma entrada do Registro de uma chave do Registro para outra. O Get-Item cmdlet obtém o caminho de destino, que é então canalizado para o Move-ItemProperty cmdlet. O Move-ItemProperty comando especifica o caminho atual e o 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 PowerShell exibe a seguinte mensagem de erro:

Move-ItemProperty : The input object can't be bound to any parameters for
the command either because the command doesn't take pipeline input or the
input and its properties do not match any of the parameters that take
pipeline input.
At line:1 char:23
+ $a | Move-ItemProperty <<<<  -Path HKLM:\Software\MyCompany\design -Name p

Para investigar, use o Trace-Command cmdlet para rastrear o componente de vinculação de parâmetros do PowerShell. O exemplo a seguir rastreia a vinculação de parâmetros enquanto o pipeline está em execução. O parâmetro PSHost exibe os resultados do rastreamento no console e o parâmetro FilePath envia os resultados do rastreamento para o debug.txt arquivo para referência posterior.

Trace-Command -Name ParameterBinding -PSHost -FilePath debug.txt -Expression {
  Get-Item -Path HKLM:\Software\MyCompany\sales |
    Move-ItemProperty -Path HKLM:\Software\MyCompany\design -Name product
}

Os resultados do rastreamento são longos, mas mostram os valores que estão sendo vinculados ao Get-Item cmdlet e, em seguida, os valores nomeados que estão sendo vinculados ao Move-ItemProperty cmdlet.

...
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
...

Use o Get-Help cmdlet para exibir os atributos do parâmetro Destination .

Get-Help Move-ItemProperty -Parameter Destination

-Destination <String>
    Specifies the path to the destination location.

    Required?                    true
    Position?                    1
    Default value                None
    Accept pipeline input?       True (ByPropertyName)
    Accept wildcard characters?  false

Os resultados mostram que Destination recebe a entrada do pipeline apenas "pelo nome da propriedade". Portanto, o objeto canalizado deve ter uma propriedade chamada Destination.

Use Get-Member para ver as propriedades do objeto proveniente do Get-Item.

Get-Item -Path HKLM:\Software\MyCompany\sales | Get-Member

A saída mostra que o item é um objeto Microsoft.Win32.RegistryKey que não tem uma propriedade Destination . Isso explica por que o comando falhou.

O parâmetro Path aceita entrada de pipeline por nome ou por valor.

Get-Help Move-ItemProperty -Parameter Path

-Path <String[]>
    Specifies the path to the current location of the property. Wildcard
    characters are permitted.

    Required?                    true
    Position?                    0
    Default value                None
    Accept pipeline input?       True (ByPropertyName, ByValue)
    Accept wildcard characters?  true

Para corrigir o comando, devemos especificar o Move-ItemProperty destino no cmdlet e usar Get-Item para obter o Caminho do item que queremos mover.

Por exemplo,

Get-Item -Path HKLM:\Software\MyCompany\design |
Move-ItemProperty -Destination HKLM:\Software\MyCompany\sales -Name product

Continuação da linha intrínseca

Como já discutido, um pipeline é uma série de comandos conectados por operadores de pipeline (|), geralmente escritos em uma única linha. No entanto, para facilitar a leitura, o PowerShell permite dividir o pipeline em várias linhas. Quando um operador de pipe é o último token na linha, o analisador do PowerShell une a próxima linha ao comando atual para continuar a construção do pipeline.

Por exemplo, o seguinte pipeline de linha única:

Command-1 | Command-2 | Command-3

pode ser escrito como:

Command-1 |
    Command-2 |
    Command-3

Os espaços principais nas linhas subsequentes não são significativos. O recuo melhora a legibilidade.

O PowerShell 7 adiciona suporte para continuação de pipelines com o caractere de pipeline no início de uma linha. Os exemplos a seguir mostram como você pode usar essa nova funcionalidade.

# Wrapping with a pipe at the beginning of a line (no backtick required)
Get-Process | Where-Object CPU | Where-Object Path
    | Get-Item | Where-Object FullName -match "AppData"
    | Sort-Object FullName -Unique

# Wrapping with a pipe on a line by itself
Get-Process | Where-Object CPU | Where-Object Path
    |
    Get-Item | Where-Object FullName -match "AppData"
    |
    Sort-Object FullName -Unique

Importante

Ao trabalhar interativamente no shell, cole código com pipelines no início de uma linha somente ao usar Ctrl+V para colar. Clique com o botão direito do mouse em operações de colagem, insira as linhas, uma de cada vez. Como a linha não termina com um caractere de pipeline, o PowerShell considera a entrada como concluída e executa essa linha conforme inserida.

Confira também