Система: Выполнение команд в PowerShell представляет собой одну из самых важных (и полезных) особенностей оболочки и языка сценариев PowerShell. После того как вы поймете основы ее работы и ее возможности, вы сможете использовать ее мощь в своих собственных функциях. В этом уроке вы собираетесь сделать именно это!
Конвейер PowerShell позволяет объединять команды в единый “конвейер”, что упрощает код, обеспечивает параллельную обработку и многое другое. Если вы готовы узнать о конвейере и создать свои собственные функции для использования конвейера, давайте начнем!
Предварительные требования
Этот пост будет учебным пособием и содержит практические демонстрации. Если вы хотите следовать за уроком, вам понадобится PowerShell версии 3 и выше. В этом уроке будет использоваться Windows PowerShell v5.1.
Понимание конвейера PowerShell
Большинство команд PowerShell принимают входные данные через параметры. Команда получает объект в качестве входных данных и выполняет некоторые действия с ним. Затем по желанию возвращает какой-то объект через вывод.
Команды в конвейере действуют подобно человеческим бегунам в эстафете. Каждый бегун в гонке, за исключением первого и последнего, принимает эстафету (объекты) от своего предшественника и передает ее следующему.
Например, у cmdlet Stop-Service
есть параметр, называемый InputObject
. Этот параметр позволяет передать конкретный тип объекта в Stop-Service
, представляющий службу Windows, которую вы хотели бы остановить.
Чтобы использовать параметр InputObject
, вы можете получить объект службы с помощью Get-Service
, а затем передать объект параметру InputObject
, как показано ниже. Этот способ предоставления ввода в cmdlet Stop-Service
через параметр InputObject
отлично работает и выполняет свою задачу.
Этот способ передачи ввода в команду Stop-Service
требует выполнения двух отдельных шагов. PowerShell должен сначала выполнить Get-Service
, сохранить вывод в переменную, а затем передать это значение в Stop-Service
через параметр InputObject
.
Теперь сократите приведенный выше фрагмент с помощью нижеприведенного фрагмента, который выполняет ту же задачу. Это намного проще, потому что вам не нужно создавать переменную $services
или даже использовать параметр InputObject
вообще. Вместо этого PowerShell “знает”, что вы собираетесь использовать параметр InputObject
. Он делает это с помощью концепции, называемой привязкой параметров.
Теперь вы “сцепили” команды вместе с оператором |
. Вы создали канал.
Но вы не обязаны использовать только две команды для создания канала; вы можете соединить столько, сколько захотите (если параметры команд поддерживают это). Например, в следующем фрагменте кода:
- Передает все объекты, которые возвращает командлет
Get-Service
, в командлетWhere-Object
. - Затем командлет
Where-Object
анализирует свойствоStatus
каждого объекта и возвращает только те объекты, у которых значениеRunning
. - Затем каждый из этих объектов отправляется в
Select-Object
, который возвращает только свойстваName
иDisplayName
объектов. - Поскольку ни один другой командлет не принимает объекты, которые выводит
Select-Object
, команда возвращает объекты напрямую на консоль.
Командлеты
Where-Object
иSelect-Object
понимают, как обрабатывать входные данные конвейера посредством концепции, называемой привязкой параметров, о которой будет рассказано в следующем разделе.
Дополнительную информацию о конвейерах можно получить, выполнив команду
Get-Help about_pipelines
.
Привязка параметров конвейера
На первый взгляд конвейер может показаться тривиальным. В конце концов, он просто передает объекты от одной команды к другой. Но на самом деле конвейер намного сложнее. Команды принимают входные данные только через параметры. Каким-то образом конвейер должен понять, какой параметр использовать, даже когда вы явно его не определяете.
Задача определения того, какой параметр использовать, когда команда получает входные данные через конвейер, называется привязкой параметра. Для успешной привязки объекта, поступающего из конвейера, к параметру параметр(ы) входящей команды должен поддерживать это. Параметры команды поддерживают привязку параметра конвейера одним из двух способов; \texttt{ByValue} и/или \texttt{ByPropertyName}.
\texttt{ByValue}
Параметр команды принимает весь поступающий объект в качестве значения параметра. Параметр \texttt{ByValue} ищет объект определенного типа среди поступающих объектов. Если этот тип объекта совпадает, PowerShell предполагает, что объект предназначен для привязки к этому параметру и принимает его.
\texttt{Get-ChildItem}cmdlet имеет параметр, называемый \texttt{Path}, который принимает строковый объект и ввод через конвейер по \texttt{ByValue}. Из-за этого выполнение чего-то вроде \texttt{‘C:\Windows’ | Get-ChildItem} возвращает все файлы в каталоге C:\Windows, потому что \texttt{C:\Windows} является строкой.
\texttt{ByPropertyName}
Параметр команды не принимает весь объект, а только одно свойство этого объекта. Он делает это не, глядя на тип объекта, а на имя свойства.
Команда
Get-Process
имеет параметрName
, который настроен для принятия ввода через конвейерByPropertyName
. Когда вы передаете объект с свойствомName
командеGet-Process
, например, так:[pscustomobject]@{Name='firefox'} | Get-Process
, PowerShell связывает свойствоName
во входящем объекте с параметромName
и использует это значение.
Обнаружение параметров команд, поддерживающих конвейер
Как уже упоминалось ранее, не каждая команда поддерживает ввод через конвейер. Автор команды должен создать такую функциональность в процессе разработки. Команда должна иметь по меньшей мере один параметр, который поддерживает конвейер, указанный как ByValue
или ByPropertyName
.
Как узнать, какие команды и их параметры поддерживают ввод через конвейер? Можно просто попробовать это методом проб и ошибок, но есть лучший способ с использованием системы помощи PowerShell с помощью команды Get-Help
.
Например, рассмотрим параметр команды Get-ChildItem
под названием Path
ниже. Вы можете видеть, что он поддерживает оба типа ввода через конвейер.

Как только вы узнаете, какие параметры команды поддерживают ввод через конвейер, вы сможете воспользоваться этой функциональностью, как показано ниже.
Но, с другой стороны, параметр DisplayName
в команде Get-Service
не поддерживает ввод через конвейер.

Создание своей собственной функции конвейера
Несмотря на то, что стандартные средства PowerShell поддерживают ввод через конвейер, это не означает, что вы не можете воспользоваться этой функциональностью. К счастью, вы можете создавать функции, которые также принимают ввод через конвейер.
Давайте, например, рассмотрим существующую функцию с именем Get-ConnectionStatus
.
- У этой функции есть один параметр (не принимающий ввод через конвейер) с именем
ComputerName
, который позволяет передавать ей одну или несколько строк. Вы можете определить, что параметрComputerName
не принимает ввод через конвейер, потому что он не определен как атрибут параметра ([Parameter()]
). - Затем функция считывает каждую из этих строк и запускает команду
Test-Connection
для каждой из них. - Для каждой переданной строки имени компьютера затем возвращается объект с свойствами
ComputerName
иStatus
.
Затем вы вызываете функцию Get-ConnectionStatus
, передавая параметр ComputerName
одно или несколько имен хостов или IP-адресов, как показано ниже.
Шаг 1: Разрешение ввода через конвейер
Чтобы заставить эту функцию принимать ввод через конвейер, сначала необходимо определить соответствующий атрибут параметра. Этот атрибут может быть либо ValueFromPipeline
, чтобы принимать ввод через конвейер по значению, либо ValueFromPipelineByPropertyName
, чтобы принимать ввод через конвейер по имени свойства.
Например, добавьте атрибут параметра ValueFromPipeline
в скобках определения [Parameter()]
, как показано ниже.
На этом этапе это технически все, что вам нужно сделать. Функция Get-ConnectionStatus
теперь будет привязывать любой переданный ей объект строки к параметру ComputerName
. Но, даже если привязка параметров происходит, это не означает, что функция будет выполнять что-то значимое с ними.
Шаг 2: Добавление блока Process
Когда вам нужно, чтобы PowerShell обрабатывал все объекты, поступающие из конвейера, следует добавить блок Process
. Этот блок указывает PowerShell обрабатывать каждый объект, поступающий из конвейера.
Без блока
Process
PowerShell будет обрабатывать только первый объект, поступающий из конвейера. БлокProcess
указывает PowerShell продолжать обработку объектов.
Всегда отправляйте вывод из блока
Process
. Отправка вывода из блокаProcess
“передает” объекты в конвейер, что позволяет другим командам принимать эти объекты из конвейера.
Передача объектов в конвейер PowerShell
После определения блока Process
в функции вы можете вызвать функцию, передавая значения параметра ComputerName
через конвейер, как показано ниже.
На этом этапе вы можете использовать настоящую мощь конвейера и начать включать в него больше команд. Например, возможно, у вас есть текстовый файл, C:\Test\computers.txt, с строкой IP-адресов, разделенных новой строкой, как показано ниже.
Затем вы можете использовать cmdlet Get-Content
, чтобы прочитать каждый из этих IP-адресов в текстовом файле и передать их непосредственно в функцию Get-ConnectionStatus
.
Поднимая эту настройку на шаг дальше, вы можете направлять объекты, которые возвращает Get-ConnectionStatus
, непосредственно в cmdlet ForEach-Object
.
Код ниже:
- чтение всех имен компьютеров в текстовом файле и передача их функции
Get-ConnectionStatus
. Get-ConnectionStatus
обрабатывает каждое имя компьютера и возвращает объект с свойствамиComputerName
иStatus
.Get-ConnectionStatus
затем передает каждый объект в командлетForEach-Object
, который возвращает единственную строку, окрашенную в цвет циан с читаемым человеком статусом.
Если передача по конвейеру не была включена для параметра
ComputerName
или еслиGet-ConnectionStatus
не возвратил объект в блокеProcess
, PowerShell не вернет статус в консоль до тех пор, пока все объекты (IP-адреса) не будут обработаны.
Привязка конвейера по имени свойства
До сих пор командлет Get-ConnectionStatus
настроен принимать ввод конвейера по значению (ValueFromPipeline
), принимая массив строк, например '127.0.0.1', '192.168.1.100'
. Будет ли эта функция работать ожидаемым образом, если ввод будет получен из CSV-файла вместо текстового файла с IP-адресами?
Возможно, у вас есть файл CSV, который выглядит так, как показано ниже по пути C:\Test\pc-list.csv.
Обратите внимание, что поле
ComputerName
в файле CSV имеет то же имя, что и параметрComputerName
Get-ConnnectionStatus
.
Если вы попытаетесь импортировать CSV и передать его по конвейеру в Get-ConnectionStatus
, функция вернет неожиданный результат в столбце ComputerName
.
Можете ли вы догадаться, что пошло не так? Ведь имя параметра совпадает, почему тогда конвейер PowerShell не привязал вывод, который возвращает Import-CSV
, к параметру ComputerName
на Get-ConnectionStatus
? Потому что вам нужен атрибут параметра ValueFromPipelineByPropertyName
.
На данный момент параметр ComputerName
функции имеет определение параметра, которое выглядит как [Parameter(ValueFromPipeline)]
. Поэтому вы должны добавить ValueFromPipelineByPropertyName
, чтобы установить параметр ComputerName
для поддержки ввода ByPropertyName, как показано ниже.
После того как вы добавили поддержку конвейера по имени параметра, вы говорите PowerShell начать рассматривать имена свойств объекта и тип объекта. После внесения этого изменения вы должны увидеть ожидаемый вывод.
Резюме
В этом руководстве вы узнали, как работает конвейер PowerShell, как он привязывает параметры и даже как создавать собственную функцию, поддерживающую конвейер PowerShell.
Несмотря на то, что функции будут работать без конвейера, они не будут «потоком» объектов от одной команды к другой и упростят код.
Можете ли вы вспомнить о функции, которую вы написали, или, быть может, собираетесь написать, которая может оказаться полезной после внесения изменений для поддержки конвейера?