about_Pipelines

Краткое описание

Объединение команд в конвейеры в PowerShell

Подробное описание

Конвейер — это ряд команд, подключенных операторами конвейера () (|ASCII 124). Каждый оператор конвейера отправляет результаты предыдущей команды в следующую команду.

Выходные данные первой команды можно отправить для обработки в качестве входных данных во вторую команду. И эти выходные данные можно отправить в еще одну команду. Результатом является сложная цепочка команд или конвейер , состоящий из ряда простых команд.

Например,

Command-1 | Command-2 | Command-3

В этом примере объекты, которые Command-1 выдаются, отправляются Command-2в . Command-2 обрабатывает объекты и отправляет их Command-3в . Command-3 обрабатывает объекты и отправляет их по конвейеру. Так как в конвейере больше команд нет, результаты отображаются в консоли.

В конвейере команды обрабатываются слева направо. Обработка обрабатывается как одна операция, и выходные данные отображаются при создании.

Вот простой пример. Следующая команда получает процесс Блокнот, а затем останавливает его.

Например,

Get-Process notepad | Stop-Process

Первая команда использует Get-Process командлет для получения объекта, представляющего процесс Блокнот. Он использует оператор конвейера (|) для отправки объекта Stop-Process процесса командлету, который останавливает процесс Блокнот. Обратите внимание, что команда Stop-Process не имеет параметра Name или ID для указания процесса, так как указанный процесс отправляется через конвейер.

Этот пример конвейера получает текстовые файлы в текущем каталоге, выбирает только те файлы, которые имеют длину более 10 000 байтов, сортируют их по длине и отображают имя и длину каждого файла в таблице.

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

Этот конвейер состоит из четырех команд в указанном порядке. На следующем рисунке показаны выходные данные каждой команды, передаваемой в следующую команду в конвейере.

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

Использование конвейеров

Большинство командлетов PowerShell предназначены для поддержки конвейеров. В большинстве случаев можно передать результаты командлета Get другому командлету того же существительного. Например, можно передать выходные данные командлета Get-ServiceStart-Service в командлеты или Stop-Service командлеты.

В этом примере конвейер запускает службу WMI на компьютере:

Get-Service wmi | Start-Service

Например, можно передать выходные данные Get-Item или Get-ChildItem в поставщик реестра PowerShell в New-ItemProperty командлет. В этом примере в раздел реестра MyCompany добавляется новая запись реестра NoOfEmployees с значением 8124.

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

Многие командлеты служебной программы, такие как Get-Member, Where-Object, Sort-ObjectGroup-Objectи Measure-Object используются почти исключительно в конвейерах. Вы можете передать любой тип объекта в эти командлеты. В этом примере показано, как сортировать все процессы на компьютере по количеству открытых дескрипторов в каждом процессе.

Get-Process | Sort-Object -Property handles

Объекты можно передать в командлеты форматирования, экспорта и вывода, например Format-List, , Export-CSVFormat-TableExport-Clixmlи .Out-File

В этом примере показано, как использовать Format-List командлет для отображения списка свойств объекта процесса.

Get-Process winlogon | Format-List -Property *

Вы также можете передать выходные данные собственных команд в командлеты PowerShell. Например:

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

Важно!

Потоки успешности и ошибок похожи на потоки stdin и stderr других оболочк. Однако stdin не подключен к конвейеру PowerShell для ввода. Дополнительные сведения см. в about_Redirection.

С помощью немного практики вы найдете, что объединение простых команд в конвейеры экономит время и ввод, а также делает скрипт более эффективным.

Как работают конвейеры

В этом разделе объясняется, как входные объекты привязаны к параметрам командлета и обрабатываются во время выполнения конвейера.

Принимает входные данные конвейера

Для поддержки конвейерной настройки командлет-получатель должен иметь параметр, принимающий входные данные конвейера. Get-Help Используйте команду с параметрами "Полный" или "Параметр", чтобы определить, какие параметры командлета принимают входные данные конвейера.

Например, чтобы определить, какие из параметров Start-Service командлета принимают входные данные конвейера, введите:

Get-Help Start-Service -Full

or

Get-Help Start-Service -Parameter *

В справке Start-Service командлета показано, что только параметры InputObject и 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

При отправке объектов через конвейер Start-Serviceв PowerShell пытается связать объекты с параметрами InputObject и Name .

Методы приема входных данных конвейера

Параметры командлетов могут принимать входные данные конвейера одним из двух способов:

  • ByValue: параметр принимает значения, соответствующие ожидаемому типу .NET или которые можно преобразовать в этот тип.

    Например, параметр Name принимает входные данные конвейера Start-Service по значению. Он может принимать строковые объекты или объекты, которые можно преобразовать в строки.

  • ByPropertyName: параметр принимает входные данные только в том случае, если входной объект имеет свойство того же имени, что и параметр.

    Например, параметр Start-Service Name может принимать объекты с свойством Name . Чтобы перечислить свойства объекта, передайте Get-Memberего в .

Некоторые параметры могут принимать объекты по имени значения или свойства, что упрощает прием входных данных из конвейера.

Привязка параметра

При канале объектов из одной команды в другую PowerShell пытается связать объекты с параметром принимающего командлета.

Компонент привязки параметров PowerShell связывает входные объекты с параметрами командлета в соответствии со следующими критериями:

  • Параметр должен принимать входные данные из конвейера.
  • Параметр должен принять тип отправляемого объекта или тип, который можно преобразовать в ожидаемый тип.
  • Параметр не использовался в команде.

Например, Start-Service командлет имеет множество параметров, но только два из них, Name и InputObject принимают входные данные конвейера. Параметр Name принимает строки, а параметр InputObject принимает объекты службы. Таким образом, можно передать строки, объекты службы и объекты со свойствами, которые можно преобразовать в строковые или служебные объекты.

PowerShell управляет привязкой параметров максимально эффективно. Вы не можете предложить или принудительно привязать PowerShell к конкретному параметру. Команда завершается ошибкой, если PowerShell не может привязать объекты с конвейером.

Дополнительные сведения об устранении ошибок привязки см . в разделе "Исследование ошибок конвейера" далее в этой статье.

Однократная обработка

Пилинг объектов к команде очень похож на использование параметра команды для отправки объектов. Рассмотрим пример конвейера. В этом примере конвейер используется для отображения таблицы объектов службы.

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

Функционально это похоже на использование параметра Format-Table InputObject для отправки коллекции объектов.

Например, можно сохранить коллекцию служб в переменную, передаваемую с помощью параметра InputObject .

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

Или можно внедрить команду в параметр InputObject .

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

Однако есть важное различие. При отправке нескольких объектов в команду PowerShell отправляет объекты в команду одновременно. При использовании параметра команды объекты отправляются в виде одного объекта массива. Это незначительное различие имеет значительные последствия.

При выполнении конвейера PowerShell автоматически перечисляет любой тип, реализующий IEnumerable интерфейс или его универсальный аналог. Перечисляемые элементы отправляются по конвейеру по одному за раз. PowerShell также перечисляет типы System.Data.DataTable через Rows свойство.

Существует несколько исключений для автоматического перечисления.

  • Необходимо вызвать GetEnumerator() метод для хэш-таблиц, типов, реализующих IDictionary интерфейс или его универсальный тип, и типы System.Xml.XmlNode .
  • Класс System.String реализует IEnumerable, однако PowerShell не перечисляет строковые объекты.

В следующих примерах массив и хэш-файл передаются Measure-Object командлету для подсчета количества объектов, полученных из конвейера. Массив содержит несколько элементов, а хэш-файл содержит несколько пар "ключ-значение". Перечисляется только один массив одновременно.

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

Аналогичным образом, если передать несколько объектов процесса из командлета Get-ProcessGet-Member в командлет, PowerShell отправляет каждый объект процесса по одному за раз Get-Member. Get-Member отображает класс .NET (тип) объектов процесса и их свойства и методы.

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

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

Примечание.

Get-Member устраняет дубликаты, поэтому если объекты имеют одинаковый тип, он отображает только один тип объекта.

Однако если вы используете параметр Get-MemberInputObject, то Get-Member получает массив объектов System.Diagnostics.Process в виде одной единицы. В нем отображаются свойства массива объектов. (Обратите внимание на символ массива ([]) после имени типа System.Object.)

Например,

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

Это может быть не то, что вы намеревались. Но после того как вы понимаете его, вы можете использовать его. Например, все объекты массива имеют свойство Count . Это можно использовать для подсчета количества процессов, выполняемых на компьютере.

Например,

(Get-Process).count

Важно помнить, что объекты, отправляемые по конвейеру, доставляются по одному за раз.

Использование собственных команд в конвейере

PowerShell позволяет включать собственные внешние команды в конвейер. Однако важно отметить, что конвейер PowerShell является объектно-ориентированным и не поддерживает необработанные данные байтов.

Пилинг или перенаправление выходных данных из собственной программы, которая выводит необработанные данные байтов, преобразует выходные данные в строки .NET. Это преобразование может привести к повреждению выходных данных необработанных данных.

Однако PowerShell 7.4 добавил экспериментальную PSNativeCommandPreserveBytePipe функцию, которая сохраняет данные байтового потока при перенаправлении потока stdout собственной команды в файл или при передаче данных байт-потока в поток stdin собственной команды.

Например, с помощью собственной команды curl можно скачать двоичный файл и сохранить его на диск с помощью перенаправления.

$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

Вы также можете передать данные байт-потока в поток stdin другой собственной команды. В следующем примере скачивает zippped TAR-файл с помощью curl. Скачанные данные файла передаются в tar команду, чтобы извлечь содержимое архива.

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

Вы также можете передать выходные данные байтового потока команды PowerShell в входные данные собственной команды. В следующих примерах используется Invoke-WebRequest для скачивания того же TAR-файла, что и в предыдущем примере.

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

Эта функция не поддерживает данные байтового потока при перенаправлении выходных данных stderr на stdout. При объединении потоков stderr и stdout объединенные потоки обрабатываются как строковые данные.

Изучение ошибок конвейера

Если PowerShell не может связать объекты с параметром принимающего командлета, команда завершается ошибкой.

В следующем примере мы пытаемся переместить запись реестра из одного раздела реестра в другой. Командлет Get-Item получает путь назначения, который затем будет передан командлету Move-ItemProperty . Команда Move-ItemProperty указывает текущий путь и имя перемещаемой записи реестра.

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

Команда завершается ошибкой, и PowerShell отображает следующее сообщение об ошибке:

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

Для изучения используйте Trace-Command командлет для трассировки компонента привязки параметров PowerShell. В следующем примере выполняется привязка параметров трассировки во время выполнения конвейера. Параметр PSHost отображает результаты трассировки в консоли, а параметр FilePath отправляет результаты трассировки в файл для последующей debug.txt ссылки.

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
}

Результаты трассировки являются длинными, но они показывают значения, привязанные к Get-Item командлету, а затем именованные значения, привязанные к командлету 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`]
...

Наконец, показано, что попытка привязать путь к параметру 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 Используйте командлет для просмотра атрибутов параметра 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

Результаты показывают, что назначение принимает входные данные конвейера только по имени свойства. Таким образом, объект с каналом должен иметь свойство с именем Destination.

Используется Get-Member для просмотра свойств объекта, исходящего из Get-Item.

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

В выходных данных показано, что элемент является объектом Microsoft.Win32.RegistryKey , который не имеет свойства Destination . Это объясняет, почему команда завершилась сбоем.

Параметр Path принимает входные данные конвейера по имени или по значению.

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

Чтобы исправить команду, необходимо указать место назначения в командлете Move-ItemProperty и использовать Get-Item для получения пути к элементу, который мы хотим переместить.

Например,

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

Встроенное продолжение строки

Как уже упоминалось, конвейер представляет собой ряд команд, подключенных операторами конвейера (|), обычно написанными в одной строке. Однако для удобства чтения PowerShell позволяет разделить конвейер по нескольким строкам. Когда оператор канала является последним маркером в строке, средство синтаксического анализа PowerShell присоединяет следующую строку к текущей команде, чтобы продолжить построение конвейера.

Например, следующий однострочный конвейер:

Command-1 | Command-2 | Command-3

можно записать как:

Command-1 |
    Command-2 |
    Command-3

Ведущие пробелы в последующих строках не являются значительными. Отступ повышает удобочитаемость.

PowerShell 7 добавляет поддержку продолжения конвейеров с символом конвейера в начале строки. В следующих примерах показано, как использовать эту новую функцию.

# 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

Важно!

При интерактивной работе в оболочке вставка кода с конвейерами в начале строки только при использовании ctrl+V для вставки. Операции вставки правой кнопкой мыши вставляют строки одновременно. Так как строка не заканчивается символом конвейера, PowerShell считает, что входные данные будут завершены и выполняются, как вводимые строки.

См. также