Поделиться через


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 поставщика реестра New-ItemProperty PowerShell или Get-ChildItem в командлет . В этом примере в раздел реестра MyCompany добавляется новая запись реестра NoOfEmployees со значением 8124.

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

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

Get-Process | Sort-Object -Property handles

Объекты можно передавать по конвейеру в командлеты форматирования, экспорта и вывода, такие как Format-List, Format-Table, Export-Clixml, Export-CSVи 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

Важно!

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

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

Принцип работы конвейеров

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

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

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

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

Get-Help Start-Service -Full

или

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

При отправке объектов через конвейер в PowerShell пытается Start-Serviceсвязать объекты с параметрами 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

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

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

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

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

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

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

При выполнении конвейера PowerShell автоматически перечисляет любой тип, реализующий IEnumerable интерфейс , и отправляет члены через конвейер по одному за раз. Исключением является [hashtable], для которого требуется GetEnumerator() вызов метода .

В следующих примерах массив и хэш-сводка передаются в 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-Process в Get-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. Это преобразование может привести к повреждению выходных данных необработанных данных.

В качестве обходного решения вызовите собственные команды с помощью cmd.exe /c или sh -c и используйте | операторы и > , предоставляемые собственной оболочкой.

Исследование ошибок конвейера

Если 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`]
...

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

См. также раздел