Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
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 de composta por uma série de comandos simples.
Por exemplo
Command-1 | Command-2 | Command-3
Neste 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 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 cmdlet Get-Process 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 cmdlet Stop-Process, que interrompe o processo do Notepad. Observe que o comando Stop-Process não tem um parâmetro de Nome ou de 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, pode encaminhar os resultados de um cmdlet Get para outro cmdlet do mesmo tipo.
Por exemplo, você pode canalizar a saída do cmdlet Get-Service para os cmdlets Start-Service 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 Get-Item ou Get-ChildItem dentro do provedor de registro do PowerShell para o cmdlet New-ItemProperty. Este exemplo adiciona uma nova entrada do Registro,
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-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 pela quantidade de manípulos 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 cmdlet Format-List 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á vinculado ao pipeline de entrada do PowerShell. 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. Utilize o comando Get-Help com as opções Full ou Parameter para determinar quais parâmetros de um cmdlet aceitam input do pipeline.
Por exemplo, para determinar qual parâmetro do cmdlet Start-Service aceita entrada de pipeline, digite:
Get-Help Start-Service -Full
ou
Get-Help Start-Service -Parameter *
A ajuda para o cmdlet Start-Service 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 se enviam objetos através do pipeline para 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
Start-Serviceaceita input de 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-Servicepode aceitar objetos que tenham uma propriedade Name. Para listar as propriedades de um objeto, canalize-o paraGet-Member.
Alguns parâmetros podem aceitar objetos, quer por valor, quer por 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 cmdlet Start-Service 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 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 um a um
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 Format-Table para enviar a coleção de objetos.
Por exemplo, podemos guardar o conjunto de serviços numa 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 interface IEnumerable ou sua contraparte genérica. Os itens enumerados são enviados através do pipeline, um de cada vez. O PowerShell também enumera tipos de System.Data.DataTable Rows.
Existem algumas exceções à enumeração automática.
- Você deve chamar o método
GetEnumerator()em tabelas de hash, tipos que implementam a interfaceIDictionaryou sua versão genérica, bem como nos tipos de System.Xml.XmlNode. - A classe System.String
implementa , 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 cmdlet Measure-Object para contar o número de objetos recebidos do pipeline. A matriz tem vários membros e a tabela de dispersão 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 Get-Process para o cmdlet Get-Member, o 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
...
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 for usado o parâmetro InputObject de Get-Member, Get-Member receberá 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 em bruto sob a forma de bytes converte a saída em cadeias de caracteres no formato .NET. Essa conversão pode causar corrupção da saída de dados brutos.
Como solução alternativa, chame os comandos nativos usando cmd.exe /c ou sh -c e use os | operadores e > fornecidos pelo shell nativo.
Investigando erros de canalização
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 cmdlet Get-Item obtém o caminho de destino, que é então canalizado para o cmdlet Move-ItemProperty. O comando Move-ItemProperty 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 cmdlet Trace-Command 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 arquivo debug.txt 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 que os valores estão a ser associados ao cmdlet Get-Item e, em seguida, os valores nomeados estão a ser associados 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, 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 cmdlet Get-Help 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 Destino 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 provenientes 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 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 destino no cmdlet Move-ItemProperty 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 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 canalização é o último token na linha, o analisador do PowerShell une a próxima linha ao comando atual para prosseguir com a construção da canalização.
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, colar código no início de uma linha com pipelines 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.