about_Pipelines

Kort beskrivning

Kombinera kommandon till pipelines i PowerShell

Lång beskrivning

En pipeline är en serie kommandon som är anslutna av pipelineoperatorer (|) (ASCII 124). Varje pipelineoperator skickar resultatet från föregående kommando till nästa kommando.

Utdata från det första kommandot kan skickas för bearbetning som indata till det andra kommandot. Och utdata kan skickas till ännu ett kommando. Resultatet är en komplex kommandokedja eller pipeline som består av en serie enkla kommandon.

Exempel:

Command-1 | Command-2 | Command-3

I det här exemplet skickas de objekt som Command-1 avger till Command-2. Command-2 bearbetar objekten och skickar dem till Command-3. Command-3 bearbetar objekten och skickar dem nedåt i pipelinen. Eftersom det inte finns fler kommandon i pipelinen visas resultatet i konsolen.

I en pipeline bearbetas kommandona i ordning från vänster till höger. Bearbetningen hanteras som en enda åtgärd och utdata visas när den genereras.

Här är ett enkelt exempel. Följande kommando hämtar Anteckningar processen och stoppar den sedan.

Exempel:

Get-Process notepad | Stop-Process

Det första kommandot använder cmdleten Get-Process för att hämta ett objekt som representerar Anteckningar process. Den använder en pipelineoperator (|) för att skicka processobjektet till cmdletenStop-Process, vilket stoppar Anteckningar processen. Observera att Stop-Process kommandot inte har någon namn- eller ID-parameter för att ange processen, eftersom den angivna processen skickas via pipelinen.

Det här pipelineexemplet hämtar textfilerna i den aktuella katalogen, väljer endast de filer som är mer än 10 000 byte långa, sorterar dem efter längd och visar namnet och längden på varje fil i en tabell.

Get-ChildItem -Path *.txt |
  Where-Object {$_.length -gt 10000} |
    Sort-Object -Property length |
      Format-Table -Property name, length

Den här pipelinen består av fyra kommandon i den angivna ordningen. Följande bild visar utdata från varje kommando när det skickas till nästa kommando i pipelinen.

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

Använda pipelines

De flesta PowerShell-cmdletar är utformade för att stödja pipelines. I de flesta fall kan du skicka resultatet av en Get-cmdlet till en annan cmdlet med samma substantiv. Du kan till exempel skicka utdata från cmdleten Get-Service till cmdletarna Start-Service eller Stop-Service .

Den här exempelpipelinen startar WMI-tjänsten på datorn:

Get-Service wmi | Start-Service

Du kan till exempel skicka utdata Get-Item från eller Get-ChildItem inom PowerShell-registerprovidern till cmdleten New-ItemProperty . I det här exemplet läggs en ny registerpost, NoOfEmployees, med värdet 8124, till registernyckeln MyCompany .

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

Många av verktygs-cmdletarna, till exempel Get-Member, Where-Object, Sort-Object, Group-Objectoch Measure-Object används nästan uteslutande i pipelines. Du kan skicka alla objekttyper till dessa cmdletar. Det här exemplet visar hur du sorterar alla processer på datorn efter antalet öppna referenser i varje process.

Get-Process | Sort-Object -Property handles

Du kan skicka objekt till cmdletar för formatering, export och utdata, till exempel Format-List, Format-Table, Export-Clixml, Export-CSVoch Out-File.

Det här exemplet visar hur du använder cmdleten Format-List för att visa en lista med egenskaper för ett processobjekt.

Get-Process winlogon | Format-List -Property *

Du kan också skicka utdata från interna kommandon till PowerShell-cmdletar. Till exempel:

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

Viktigt!

Strömmarna Success och Error liknar stdin- och stderr-strömmarna för andra skal. Stdin är dock inte ansluten till PowerShell-pipelinen för indata. Mer information finns i about_Redirection.

Med lite övning kommer du att upptäcka att kombinera enkla kommandon i pipelines sparar tid och skriver, och gör skriptet mer effektivt.

Så här fungerar pipelines

I det här avsnittet beskrivs hur indataobjekt är bundna till cmdlet-parametrar och bearbetas under pipelinekörningen.

Accepterar pipelineindata

För att stödja pipelining måste den mottagande cmdleten ha en parameter som accepterar pipelineindata. Get-Help Använd kommandot med alternativen Fullständig eller Parameter för att avgöra vilka parametrar för en cmdlet som accepterar pipelineindata.

Om du till exempel vill ta reda på vilka av parametrarna i cmdleten Start-Service som accepterar pipelineindata skriver du:

Get-Help Start-Service -Full

eller

Get-Help Start-Service -Parameter *

Hjälpen för cmdleten Start-Service visar att endast parametrarna InputObject och Name accepterar pipelineindata.

-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

När du skickar objekt via pipelinen till Start-Serviceförsöker PowerShell associera objekten med parametrarna InputObject och Name .

Metoder för att acceptera pipelineindata

Cmdletar-parametrar kan acceptera pipelineindata på något av två olika sätt:

  • ByValue: Parametern accepterar värden som matchar den förväntade .NET-typen eller som kan konverteras till den typen.

    Parametern Start-Service Namn för accepterar till exempel pipelineindata efter värde. Den kan acceptera strängobjekt eller -objekt som kan konverteras till strängar.

  • ByPropertyName: Parametern accepterar endast indata när indataobjektet har en egenskap med samma namn som parametern.

    Till exempel kan parametern Start-Service Namn för acceptera objekt som har en namnegenskap . Ange egenskaperna för ett objekt genom att skicka det till Get-Member.

Vissa parametrar kan acceptera objekt med både värde- eller egenskapsnamn, vilket gör det enklare att ta indata från pipelinen.

Parameterbindning

När du dirigerar objekt från ett kommando till ett annat kommando försöker PowerShell associera piped-objekten med en parameter för den mottagande cmdleten.

PowerShells parameterbindningskomponent associerar indataobjekten med cmdlet-parametrar enligt följande villkor:

  • Parametern måste acceptera indata från en pipeline.
  • Parametern måste acceptera vilken typ av objekt som skickas eller en typ som kan konverteras till den förväntade typen.
  • Parametern användes inte i kommandot .

Cmdleten Start-Service har till exempel många parametrar, men endast två av dem, Name och InputObject accepterar pipelineindata. Parametern Namn tar strängar och parametern InputObject tar tjänstobjekt. Därför kan du skicka rörsträngar, tjänstobjekt och objekt med egenskaper som kan konverteras till sträng- eller tjänstobjekt.

PowerShell hanterar parameterbindning så effektivt som möjligt. Du kan inte föreslå eller tvinga PowerShell att binda till en specifik parameter. Kommandot misslyckas om PowerShell inte kan binda piped-objekten.

Mer information om hur du felsöker bindningsfel finns i Undersöka pipelinefel senare i den här artikeln.

Bearbetning en i taget

Att skicka objekt till ett kommando är ungefär som att använda en parameter i kommandot för att skicka objekten. Nu ska vi titta på ett pipelineexempel. I det här exemplet använder vi en pipeline för att visa en tabell med tjänstobjekt.

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

Funktionellt är detta som att använda parametern Format-Table InputObject för för att skicka objektsamlingen.

Vi kan till exempel spara samlingen av tjänster till en variabel som skickas med parametern InputObject .

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

Eller så kan vi bädda in kommandot i parametern InputObject .

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

Det finns dock en viktig skillnad. När du skickar flera objekt till ett kommando skickar PowerShell objekten till kommandot ett i taget. När du använder en kommandoparameter skickas objekten som ett enskilt matrisobjekt. Denna mindre skillnad har betydande konsekvenser.

När du kör en pipeline räknar PowerShell automatiskt upp alla typer som implementerar IEnumerable gränssnittet eller dess generiska motsvarighet. Uppräknade objekt skickas via pipelinen en i taget. PowerShell räknar också upp System.Data.DataTable-typer via egenskapen Rows .

Det finns några undantag för automatisk uppräkning.

  • Du måste anropa GetEnumerator() metoden för hash-tabeller, typer som implementerar IDictionary gränssnittet eller dess generiska motsvarighet och System.Xml.XmlNode-typer .
  • Klassen System.String implementerar IEnumerable, men PowerShell räknar inte upp strängobjekt.

I följande exempel skickas en matris och en hashtable till cmdleten Measure-Object för att räkna antalet objekt som tas emot från pipelinen. Matrisen har flera medlemmar och hashtabellen har flera nyckel/värde-par. Endast matrisen räknas upp en i taget.

@(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 :

Om du skickar flera processobjekt från cmdleten Get-Process till cmdleten Get-Member skickar PowerShell varje processobjekt, ett i taget, till Get-Member. Get-Member visar .NET-klassen (typ) för processobjekten och deras egenskaper och metoder.

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

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

Kommentar

Get-Member eliminerar dubbletter, så om objekten är av samma typ visas bara en objekttyp.

Men om du använder parametern InputObject för Get-Membertar du Get-Member emot en matris med System.Diagnostics.Process-objekt som en enda enhet. Den visar egenskaperna för en matris med objekt. (Observera matrissymbolen ([]) efter namnet på System.Object-typen .)

Exempel:

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

Det här resultatet kanske inte är det du avsåg. Men när du har förstått det kan du använda det. Till exempel har alla matrisobjekt en count-egenskap . Du kan använda det för att räkna antalet processer som körs på datorn.

Exempel:

(Get-Process).count

Det är viktigt att komma ihåg att objekt som skickas ned i pipelinen levereras en i taget.

Använda interna kommandon i pipelinen

Med PowerShell kan du inkludera interna externa kommandon i pipelinen. Det är dock viktigt att observera att PowerShells pipeline är objektorienterad och inte stöder rådata.

Rördragning eller omdirigering av utdata från ett internt program som matar ut rådata konverterar utdata till .NET-strängar. Den här konverteringen kan orsaka skadade rådatautdata.

PowerShell 7.4 lade dock till den PSNativeCommandPreserveBytePipe experimentella funktionen som bevarar byteströmsdata vid omdirigering av stdout-strömmen för ett inbyggt kommando till en fil eller när byteströmdata skickas till stdin-strömmen för ett internt kommando.

Med det interna kommandot curl kan du till exempel ladda ned en binär fil och spara den på disk med hjälp av omdirigering.

$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

Du kan också skicka byteströmdata till stdin-strömmen för ett annat inbyggt kommando. I följande exempel hämtas en zippad TAR-fil med .curl De nedladdade fildata strömmas till tar kommandot för att extrahera innehållet i arkivet.

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

Du kan också skicka byteströmutdata från ett PowerShell-kommando till indata från det interna kommandot. I följande exempel används Invoke-WebRequest för att ladda ned samma TAR-fil som i föregående exempel.

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

Den här funktionen stöder inte byteströmsdata vid omdirigering av stderr-utdata till stdout. När du kombinerar stderr - och stdout-strömmarna behandlas de kombinerade strömmarna som strängdata.

Undersöka pipelinefel

När PowerShell inte kan associera piped-objekten med en parameter för den mottagande cmdleten misslyckas kommandot.

I följande exempel försöker vi flytta en registerpost från en registernyckel till en annan. Cmdleten Get-Item hämtar målsökvägen, som sedan skickas till cmdleten Move-ItemProperty . Kommandot Move-ItemProperty anger den aktuella sökvägen och namnet på registerposten som ska flyttas.

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

Kommandot misslyckas och PowerShell visar följande felmeddelande:

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

Om du vill undersöka det använder du cmdleten Trace-Command för att spåra parameterbindningskomponenten i PowerShell. I följande exempel spåras parameterbindning medan pipelinen körs. Parametern PSHost visar spårningsresultaten i konsolen och parametern FilePath skickar spårningsresultatet debug.txt till filen för senare referens.

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
}

Resultatet av spårningen är långa, men de visar värdena som är bundna till cmdleten Get-Item och sedan de namngivna värden som är bundna till cmdleten 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`]
...

Slutligen visar den att försöket att binda sökvägen till målparameternMove-ItemProperty misslyckades.

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

Använd cmdleten Get-Help för att visa attributen för målparametern.

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

Resultatet visar att Målet endast tar pipelineindata "efter egenskapsnamn". Därför måste piped-objektet ha en egenskap med namnet Destination.

Använd Get-Member för att se egenskaperna för objektet som kommer från Get-Item.

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

Utdata visar att objektet är ett Microsoft.Win32.RegistryKey-objekt som inte har någon målegenskap . Det förklarar varför kommandot misslyckades.

Parametern Path accepterar pipelineindata efter namn eller värde.

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

För att åtgärda kommandot måste vi ange målet i cmdleten Move-ItemProperty och använda Get-Item för att hämta sökvägen för det objekt som vi vill flytta.

Exempel:

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

Fortsättning på inbyggda linjer

Som vi redan nämnt är en pipeline en serie kommandon som är anslutna av pipelineoperatorer (|), vanligtvis skrivna på en enda rad. För läsbarhet kan du dock dela upp pipelinen över flera rader i PowerShell. När en pipe-operator är den sista token på raden ansluter PowerShell-parsern nästa rad till det aktuella kommandot för att fortsätta konstruktionen av pipelinen.

Till exempel följande enradspipeline:

Command-1 | Command-2 | Command-3

kan skrivas som:

Command-1 |
    Command-2 |
    Command-3

De inledande blankstegen på efterföljande linjer är inte signifikanta. Indraget förbättrar läsbarheten.

PowerShell 7 lägger till stöd för fortsättning av pipelines med pipelinetecknet i början av en rad. I följande exempel visas hur du kan använda den här nya funktionen.

# 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

Viktigt!

När du arbetar interaktivt i gränssnittet klistrar du in kod med pipelines i början av en rad endast när du använder Ctrl+V för att klistra in. Högerklicka på inklistringsåtgärder för att infoga raderna en i taget. Eftersom raden inte slutar med ett pipelinetecken anser PowerShell att indata är fullständiga och kör raden som angiven.

Se även