about_Pipelines

Krátký popis

Kombinování příkazů do kanálů v PowerShellu

Dlouhý popis

Kanál je řada příkazů připojených operátory kanálu () (|ASCII 124). Každý operátor kanálu odešle výsledky předchozího příkazu do dalšího příkazu.

Výstup prvního příkazu lze odeslat ke zpracování jako vstup do druhého příkazu. A tento výstup lze odeslat do dalšího příkazu. Výsledkem je složitý řetězec příkazů nebo kanál složený z řady jednoduchých příkazů.

Příklad:

Command-1 | Command-2 | Command-3

V tomto příkladu jsou objekty, které Command-1 generuje, odeslány do Command-2. Command-2 zpracovává objekty a odesílá je do Command-3. Command-3 zpracovává objekty a odesílá je do kanálu. Vzhledem k tomu, že kanál neobsahuje žádné další příkazy, zobrazí se v konzole výsledky.

V kanálu se příkazy zpracovávají v pořadí zleva doprava. Zpracování se zpracovává jako jedna operace a výstup se zobrazí při vygenerování.

Tady je jednoduchý příklad. Následující příkaz získá proces Poznámkový blok a pak ho zastaví.

Příklad:

Get-Process notepad | Stop-Process

První příkaz pomocí rutiny Get-Process získá objekt představující proces Poznámkový blok. K odeslání objektu Stop-Process procesu do rutiny používá operátor kanálu (|), který zastaví proces Poznámkový blok. Všimněte si, že Stop-Process příkaz nemá parametr Name nebo ID k určení procesu, protože zadaný proces se odešle prostřednictvím kanálu.

Tento příklad kanálu získá textové soubory v aktuálním adresáři, vybere pouze soubory, které jsou delší než 10 000 bajtů, seřadí je podle délky a zobrazí název a délku každého souboru v tabulce.

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

Tento kanál se skládá ze čtyř příkazů v zadaném pořadí. Následující obrázek znázorňuje výstup z každého příkazu, který se předá dalšímu příkazu v kanálu.

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

Použití kanálů

Většina rutin PowerShellu je navržená tak, aby podporovala kanály. Ve většině případů můžete výsledky rutiny Get převést na jinou rutinu se stejným podstatným názvem. Výstup rutiny můžete například přeskakovat Get-Service do Start-Service rutin nebo Stop-Service rutin.

Tento ukázkový kanál spustí službu WMI v počítači:

Get-Service wmi | Start-Service

V jiném příkladu můžete do rutiny přeskakovat výstup poskytovatele Get-Item registru PowerShellu nebo Get-ChildItem v rámci tohoto New-ItemProperty poskytovatele. Tento příklad přidá novou položku registru NoOfEmployees s hodnotou 8124 do klíče registru MyCompany.

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

Mnoho rutin utility, například Get-Member, Where-Object, Group-ObjectSort-Objecta Measure-Object jsou používány téměř výhradně v kanálech. Do těchto rutin můžete převést libovolný typ objektu. Tento příklad ukazuje, jak seřadit všechny procesy v počítači podle počtu otevřených popisovačů v každém procesu.

Get-Process | Sort-Object -Property handles

Objekty můžete pipetovat do rutin formátování, exportu a výstupu, například Format-List, Format-Table, Export-Clixml, Export-CSVa Out-File.

Tento příklad ukazuje, jak pomocí rutiny Format-List zobrazit seznam vlastností objektu procesu.

Get-Process winlogon | Format-List -Property *

Výstup nativních příkazů můžete také přeskakovat do rutin PowerShellu. Příklad:

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

Důležité

Datové proudy Success a Error jsou podobné streamům stdin a stderr jiných prostředí. Stdin se ale ke vstupnímu kanálu PowerShellu nepřipojí. Další informace najdete v tématu about_Redirection.

S trochou praxe zjistíte, že kombinování jednoduchých příkazů do kanálů šetří čas a psaní a zefektivňuje skriptování.

Jak fungují kanály

Tato část vysvětluje, jak jsou vstupní objekty svázány s parametry rutiny a zpracovány během provádění kanálu.

Přijímá vstup kanálu.

Aby bylo možné kanálování podporovat, musí mít přijímající rutina parametr, který přijímá vstup kanálu. Get-Help Pomocí příkazu s možnostmi Full nebo Parameter určete, které parametry rutiny přijímají vstup kanálu.

Pokud chcete například určit, které parametry Start-Service rutiny přijímá vstup kanálu, zadejte:

Get-Help Start-Service -Full

nebo

Get-Help Start-Service -Parameter *

Nápověda pro rutinu Start-Service ukazuje, že vstup kanálu přijímají pouze parametry InputObject a Name .

-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

Když odesíláte objekty prostřednictvím kanálu, Start-ServicePowerShell se pokusí přidružit objekty k parametrům InputObject a Name .

Metody přijetí vstupu kanálu

Parametry rutin můžou přijímat vstup kanálu jedním ze dvou různých způsobů:

  • ByValue: Parametr přijímá hodnoty, které odpovídají očekávanému typu .NET nebo které lze převést na tento typ.

    Například parametr Name přijímá Start-Service vstup kanálu podle hodnoty. Může přijímat objekty řetězců nebo objekty, které lze převést na řetězce.

  • ByPropertyName: Parametr přijímá vstup pouze v případech, kdy vstupní objekt má vlastnost se stejným názvem jako parametr.

    Například parametr Start-Service Name může přijímat objekty, které mají vlastnost Name . Chcete-li zobrazit seznam vlastností objektu, nasunout ho na Get-Member.

Některé parametry mohou přijímat objekty podle hodnoty nebo názvu vlastnosti, což usnadňuje příjem vstupu z kanálu.

Vazba parametru

Když objekty předáte z jednoho příkazu do jiného příkazu, PowerShell se pokusí přidružit objekty s kanálem k parametru přijímající rutiny.

Komponenta vazby parametrů PowerShellu přidruží vstupní objekty k parametrům rutiny podle následujících kritérií:

  • Parametr musí přijímat vstupy z kanálu.
  • Parametr musí přijmout typ odesílaného objektu nebo typ, který lze převést na očekávaný typ.
  • Parametr nebyl použit v příkazu.

Například rutina Start-Service má mnoho parametrů, ale pouze dva z nich, Name a InputObject přijímají vstup kanálu. Parametr Name přebírá řetězce a parametr InputObject přebírá objekty služby. Proto můžete řetězce, objekty služby a objekty s vlastnostmi, které lze převést na objekty řetězce nebo služby.

PowerShell spravuje vazbu parametrů co nejefektivněji. PowerShell nemůže navrhnout nebo vynutit vytvoření vazby ke konkrétnímu parametru. Příkaz selže, pokud PowerShell nemůže svázat objekty s kanály.

Další informace o řešení chyb vazby najdete v části Zkoumání chyb kanálu dále v tomto článku.

Jednorázové zpracování

Piping objects to a command is like using a parameter of the command to submit the objects. Podívejme se na příklad kanálu. V tomto příkladu používáme kanál k zobrazení tabulky objektů služby.

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

Funkčně se to podobá použití parametru Format-Table InputObject pro odeslání kolekce objektů.

Kolekci služeb můžeme například uložit do proměnné, která se předává pomocí parametru InputObject .

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

Nebo můžeme příkaz vložit do parametru InputObject .

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

Je ale důležitý rozdíl. Když předáte více objektů do příkazu, PowerShell odešle objekty do příkazu po jednom. Při použití parametru příkazu se objekty odesílají jako jeden maticový objekt. Tento malý rozdíl má významné důsledky.

Při spouštění kanálu PowerShell automaticky vytvoří výčet libovolného typu, který implementuje IEnumerable rozhraní nebo jeho obecný protějšek. Výčtové položky se posílají kanálem po jednom. PowerShell také vyčísluje Rows typy System.Data.DataTable prostřednictvím vlastnosti.

Existuje několik výjimek automatického výčtu.

  • Je nutné volat metodu GetEnumerator() pro hash tabulky, typy, které implementují IDictionary rozhraní nebo jeho obecný protějšek, a System.Xml.XmlNode typy.
  • System.String třída implementuje IEnumerable, ale PowerShell nevyčísluje objekty řetězce.

V následujících příkladech se pole a hashtable předávají rutině Measure-Object , aby se spočítal počet objektů přijatých z kanálu. Pole má více členů a hashovatelná tabulka má několik párů klíč-hodnota. Pouze pole se vyčíslí po jednom.

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

Podobně pokud předáte více procesních objektů z Get-Process rutiny do Get-Member rutiny, PowerShell odešle každý objekt procesu jeden po druhém do Get-Member. Get-Member zobrazí třídu .NET (typ) objektů procesu a jejich vlastnosti a metody.

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

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

Poznámka:

Get-Member eliminuje duplicity, takže pokud jsou všechny objekty stejného typu, zobrazí pouze jeden typ objektu.

Pokud však použijete inputObject parametr Get-Member, pak Get-Member obdrží pole System.Diagnostics.Process objekty jako jednu jednotku. Zobrazí vlastnosti pole objektů. (Poznamenejte si symbol pole ([]) za názvem typu System.Object .)

Příklad:

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

Tento výsledek nemusí být tím, co jste chtěli. Ale až to pochopíte, můžete ho použít. Například všechny objekty pole mají vlastnost Count . Můžete ho použít ke spočítání počtu procesů spuštěných v počítači.

Příklad:

(Get-Process).count

Je důležité si uvědomit, že objekty odeslané kanálem se doručují po jednom.

Použití nativních příkazů v kanálu

PowerShell umožňuje zahrnout do kanálu nativní externí příkazy. Je ale důležité si uvědomit, že kanál PowerShellu je objektově orientovaný a nepodporuje nezpracovaná bajtová data.

Piping or redirecting output from a native program that outputs raw byte data converts the outputs to .NET strings. Tento převod může způsobit poškození nezpracovaného výstupu dat.

PowerShell 7.4 ale přidal PSNativeCommandPreserveBytePipe experimentální funkci, která při přesměrování streamu stdout nativního příkazu na soubor nebo při propojení dat bajtového streamu do datového proudu stdin nativního příkazu zachovala data bajtů.

Například pomocí nativního příkazu curl můžete stáhnout binární soubor a uložit ho na disk pomocí přesměrování.

$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

Data bajtového streamu můžete také směrovat do datového proudu stdin jiného nativního příkazu. Následující příklad stáhne soubor ZIP TAR pomocí curl. Stažená data souboru se streamují do tar příkazu, který extrahuje obsah archivu.

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

Výstup bajtového streamu příkazu PowerShellu můžete také přeskakovat na vstup nativního příkazu. Následující příklady používají Invoke-WebRequest ke stažení stejného souboru TAR jako v předchozím příkladu.

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

Tato funkce nepodporuje data bajtů-stream při přesměrování výstupu stderr na stdout. Při kombinování datových proudů stderr a stdout se kombinované datové proudy považují za řetězcová data.

Zkoumání chyb kanálu

Pokud PowerShell nemůže přidružit objekty s kanálem k parametru přijímající rutiny, příkaz selže.

V následujícím příkladu se pokusíme přesunout položku registru z jednoho klíče registru do jiného. Rutina Get-Item získá cílovou cestu, která se pak předá rutině Move-ItemProperty . Příkaz Move-ItemProperty určuje aktuální cestu a název položky registru, která se má přesunout.

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

Příkaz selže a PowerShell zobrazí následující chybovou zprávu:

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

K prošetření použijte Trace-Command rutinu ke sledování komponenty vazby parametrů PowerShellu. Následující příklad trasuje vazbu parametrů během provádění kanálu. Parametr PSHost zobrazí výsledky trasování v konzole a parametr FilePath odešle výsledky trasování do debug.txt souboru pro pozdější referenci.

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
}

Výsledky trasování jsou zdlouhavé, ale zobrazují hodnoty vázané na rutinu Get-Item a pak pojmenované hodnoty vázané na rutinu 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`]
...

Nakonec ukazuje, že pokus o vytvoření vazby cesty k parametru Destination selhal Move-ItemProperty .

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

Get-Help Pomocí rutiny můžete zobrazit atributy parametru 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

Výsledky ukazují, že cíl přijímá vstup kanálu pouze "podle názvu vlastnosti". Proto musí mít piped objekt vlastnost s názvem Cíl.

Umožňuje Get-Member zobrazit vlastnosti objektu pocházejícího z Get-Item.

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

Výstup ukazuje, že položka je objekt Microsoft.Win32.RegistryKey , který nemá cílovou vlastnost. To vysvětluje, proč příkaz selhal.

Parametr Path přijímá vstup kanálu podle názvu nebo hodnoty.

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

Pokud chcete příkaz opravit, musíme v rutině Move-ItemProperty zadat cíl a použít Get-Item k získání cesty k položce, kterou chceme přesunout.

Příklad:

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

Pokračování vnitřní čáry

Jak už bylo popsáno, kanál je řada příkazů propojených operátory kanálu (|), obvykle napsané na jednom řádku. Kvůli čitelnosti ale PowerShell umožňuje rozdělit kanál mezi více řádků. Pokud je operátor kanálu posledním tokenem na řádku, analyzátor PowerShell připojí další řádek k aktuálnímu příkazu, aby pokračoval ve vytváření kanálu.

Například následující jednořádkový kanál:

Command-1 | Command-2 | Command-3

lze napsat takto:

Command-1 |
    Command-2 |
    Command-3

Úvodní mezery na následujících řádcích nejsou významné. Odsazení zlepšuje čitelnost.

PowerShell 7 přidává podporu pro pokračování kanálů s znakem kanálu na začátku řádku. Následující příklady ukazují, jak můžete tuto novou funkci použít.

# 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

Důležité

Při interaktivní práci v prostředí vložíte kód s kanály na začátku řádku, pouze když k vložení použijete ctrl+V. Operace vložení po kliknutí pravým tlačítkem myši vloží řádky po jednom. Vzhledem k tomu, že řádek nekončí znakem kanálu, PowerShell považuje vstup za dokončený a spustí tento řádek podle zadání.

Viz také