Partilhar via


about_Pipelines

Breve descrição

Combinando comandos em pipelines no PowerShell

Descrição longa

Um pipeline é uma série de comandos conectados por operadores de pipeline (|) (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 cadeia de comando complexa ou pipeline 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-2. Command-2 processa os objetos e os envia para Command-3. Command-3 processa os objetos e envia-os 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.

Aqui está um exemplo simples. O comando a seguir obtém o processo do Bloco de Notas e o interrompe.

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 através 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 de comprimento, classifica-os 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 é passada 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 dar 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 Get-Service saída do 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-Object, e 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 de sucesso e erro 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 e torna seu script mais eficiente.

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 a entrada do pipeline.

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

Get-Help Start-Service -Full

ou

Get-Help Start-Service -Parameter *

A ajuda para o Start-Service cmdlet mostra que somente os parâmetros InputObject e Name aceitam a entrada do 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 através do pipeline para o Start-Service, o PowerShell tenta associar os objetos aos parâmetros InputObject e Name.

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

Os parâmetros dos cmdlets podem aceitar a entrada do 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.

Vinculação de parâmetros

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 a 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 a entrada do 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 cadeia de caracteres ou objetos 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 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 mais adiante neste artigo.

Processamento individual

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. Esta 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 através do pipeline, um de cada vez. O PowerShell também enumera os tipos System.Data.DataTable por meio da Rows propriedade.

Existem 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 seguintes, uma matriz e uma tabela hash são direcionadas para o cmdlet Measure-Object para contar o número de objetos recebidos da pipeline. A matriz tem vários membros e a tabela hash 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 Get-Process cmdlet, o Get-Member PowerShell enviará cada objeto de processo, um de cada vez, para Get-Member. 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
...

Nota

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 de 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()
...

Este 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 incluir comandos externos nativos no pipeline. No entanto, é importante observar que o pipeline do PowerShell é orientado a objetos e não oferece suporte a dados brutos de bytes.

Canalizar ou redirecionar a saída de um programa nativo que produz dados brutos de bytes 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 os PSNativeCommandPreserveBytePipe dados 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 no 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 canalizar os dados de fluxo de bytes para o fluxo stdin de outro comando nativo. O exemplo a seguir baixa um arquivo TAR compactado usando curlo . Os dados do arquivo baixado são transmitidos para o tar comando para extrair o conteúdo do arquivo.

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

Você também pode canalizar 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 suporta 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 pode 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 sendo vinculados ao Get-Item cmdlet e, em seguida, os valores nomeados 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, 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 o 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 de 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 Path 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 intrínseca da linha

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 a 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 completa e executa essa linha conforme inserida.

Consulte também