Как было показано в одной из предыдущих моих статей, одним из основных механизмов PowerShell является конвейер. Он позволяет передавать результаты выполнения одной команды PowerShell следующей. Не менее важно и то, что эти результаты являются не текстовым представлением полученных данных, а объектами. Таким образом, мы можем построить серию команд (расположенных в одну строку, one-liner), состоящую из любого количества элементов — команд PowerShell, функций и даже сценариев — и на любом ее шаге использовать свойства и методы проходящих по конвейеру объектов. Пример приведен в листинге 1.
Кроме того, нам известно, что многие команды PowerShell поддерживают конвейер в качестве механизма получения исходных данных. Каждая из таких команд PowerShell, в соответствии с определенной логикой, сопоставляет поступающие данные с одним или несколькими параметрами.
Однако в этой статье мы не будем рассматривать взаимодействие конвейера со встроенными командами PowerShell. Предметом нашего внимания станет использование его возможностей при создании собственных функций. А начнем мы с фильтров.
Фильтр
Фильтр — это функция, основным предназначением которой является работа с конвейером. По структуре он напоминает функцию, содержащую только блок Process, о чем я расскажу немного позже. Работает он следующим образом. Поступление объекта приводит к выполнению кода фильтра. Таким образом, код будет выполнен столько раз, сколько объектов поступит по конвейеру. Еще необходимо иметь в виду, что работа над следующим объектом не начнется до тех пор, пока не будет обработан предыдущий. Точнее сказать, следующий цикл выполнения кода фильтра не начнется до тех пор, пока не закончится предыдущий. Дело в том, что поступающие объекты инициируют выполнение кода фильтра, однако никакой проверки относительно того, обратились ли к текущему объекту хотя бы раз, не производится. В качестве иллюстрации приведен код фильтра в листинге 2. Мы видим, что он был запущен три раза, однако к поступающим элементам конвейера — ‘first’, ‘second’ и ‘third’ мы в нем не обращались.
Для доступа к текущему элементу конвейера используются переменные $_ или $PSItem, что с точки зрения функциональности абсолютно равноценно. Также для этих целей мы можем задействовать переменную $input. И хотя она не является полным аналогом переменных $_ и $PSItem, в данном случае результат обращения к ней будет тем же самым. Более подробно о переменной $input я расскажу позже.
Расширив функциональность предыдущего фильтра с целью вывода значений поступающих по конвейеру элементов, мы получим такой результат, как показан в листинге 3. Так же, как и функции, фильтры поддерживают использование параметров (листинг 4).
ByValue и ByPropertyName
Как мы помним, существует два способа сопоставления командой PowerShell или функцией входящих объектов различным параметрам: ByValue и ByPropertyName.
ByValue означает, что определенному параметру будет сопоставлен поступивший по конвейеру объект при условии, что тип этого объекта соответствует ожидаемому параметром типу данных.
ByPropertyName указывает, что значением параметра будет значение свойства входящего объекта, имя которого соответствует имени или псевдониму (alias) параметра, опять же с учетом соответствия типов данных свойства объекта и параметра (листинг 5).
Как мы видим в строке Accept Pipeline Input?, параметр InputObject команды PowerShell Stop-Process поддерживает получение данных ByValue. Это говорит о том, что значением данного параметра станет поступивший объект, но только в том случае, если тип данных этого объекта — System.Diagnostics.Process или же некоторый тип данных, который можно преобразовать в System.Diagnostics.Process. Его сокращенное название мы можем видеть в угловых скобках сразу после имени параметра.
Выглядеть это будет так:
Get-Process Calculator | Stop-Process
Параметр Name этой же команды PowerShell поддерживает вариант ByPropertyName (листинг 6).
В этом случае значением параметра может стать содержимое свойства Name поступающего по конвейеру объекта при условии, что тип данных этого свойства — System.String или же некий тип данных, поддерживающий преобразование в строку. Причем принадлежность самого объекта к какому-либо типу не имеет значения. Мы для этих целей будем использовать объект типа System.Management.Automation.PSCustomObject.
[PSCustomObject]@{Name = ‘Calculator’} | Stop-Process
Если мы попробуем получить список псевдонимов параметра Name команды PowerShell Stop-Process
(Get-Command Stop-Process). Parameters.Item('Name').Aliases ProcessName
то увидим, что единственным его алиасом является ProcessName. Это означает, что мы можем преобразовать приведенную выше команду следующим образом:
[PSCustomObject]@{ProcessName = ‘Calculator’} | Stop-Process
без каких-либо изменений в ее функциональности.
Функция
Для того чтобы определенный параметр функции поддерживал получение данных посредством конвейера, потребуется явным образом указать способ его взаимодействия с поступающими данными. Делается это при помощи аргументов ValueFromPipeline и ValueFromPipelineByPropertyName атрибута Parameter.
Аргумент ValueFromPipeline указывает, что параметр будет поддерживать способ ByValue, а ValueFromPipelineByPropertyName задает использование способа ByPropertyName. Указываются они таким образом, как показано в листинге 7.
Кроме того, поддерживается и другой, более краткий способ, представленный в листинге 8.
При определении параметра как поддерживающего получение данных по конвейеру мы можем использовать тот или иной аргумент либо оба сразу.
Теперь укажем, что значением параметра Name должен быть объект строки, а также добавим код для вывода функцией полученного значения (листинг 9).
Далее попробуем передать функции некоторое значение. Сделаем мы это несколькими способами, как посредством конвейера, причем в двух вариантах — ByValue и ByPropertyName, так и с использованием имени параметра (листинг 10).
Как видите, все три способа работают. Теперь давайте добавим параметру Name два псевдонима, HostName и NodeName. Делается это при помощи атрибута Alias (листинг 11).
При передаче данных функции с использованием варианта ByPropertyName вместо имени параметра Name мы можем использовать HostName или NodeName (листинг 12).
Begin, Process, End
Приведенные выше примеры хорошо работают с единственным объектом. Однако ситуация изменится, если в качестве значения мы укажем массив. Для того чтобы параметр Name поддерживал получение массива строк в качестве входных данных, добавим еще одну пару квадратных скобок к определению его типа (листинг 13).
Надо сказать, что это изменение влияет только на способ вызова функции с использованием имени параметра. На получение данных посредством конвейера оно не оказывает никакого воздействия, однако об этом чуть позже.
Теперь, если в качестве входных данных мы используем массив, результаты будут такими, как показано в листинге 14.
Как мы видим, результаты вызова функции с использованием имени параметра вполне соответствуют ожидаемым. Однако оба использующих конвейер варианта возвращают только последний элемент массива. Почему так получается? Здесь самое время поговорить о структуре функций.
Итак, функция может состоять из трех блоков — Begin, Process и End.
function Structure { Param() Begin {} Process {} End {} }
Begin
Блок Begin выполняется первым и только один раз. Если для передачи данных функции мы используем конвейер, то блок Begin выполняется еще до того, как первый объект поступит на обработку. Соответственно, переменные $_ и $PSItem в блоке Begin не будут содержать никакого значения (листинг 15).
То же касается и случая, когда мы явным образом определяем параметр как поддерживающий получение данных через конвейер (листинг 16). Если же мы вызовем функцию с использованием имени параметра, то получим следующие результаты:
Structure -Name 'first', 'second', 'third' Begin $_: $Name: first second third
В данном случае функции не нужно ожидать поступления объекта по конвейеру, чтобы назначить его параметру, потому что на момент запуска его значение уже известно. Как следствие, мы можем получить значение параметра уже в блоке Begin.
Однако если ваша функция будет использоваться и тем, и другим способом, то есть будет получать данные как посредством конвейера, так и с использованием имени параметра (естественно, не одновременно), подобного следует избегать.
Тем не менее это не касается параметров, не поддерживающих конвейерное получение данных. Обращение к ним в блоке Begin не внесет в ваш код какой-либо непредсказуемости (листинг 17).
Process
При использовании конвейера блок Process выполняется для каждого полученного элемента поочередно. Таким образом, он будет запущен столько раз, сколько объектов поступит через конвейер. К обрабатываемому в данный момент элементу мы можем обратиться как при помощи переменных $_ или $PSItem, так и посредством переменной, соответствующей имени параметра, которому эти данные были сопоставлены (листинг 18).
Если же мы вызовем функцию с использованием имени параметра
Structure -Name ‘first’, ‘second’, ‘third’
то, как и ожидалось, увидим, что его значение доступно нам как в блоке Begin, так и в блоке Process.
Begin: first second third Process: first second third
End
Завершается работа функции однократным выполнением блока End, который запускается сразу после того, как блок Process закончит обработку полученных объектов (листинг 19).
Как мы видим, обратившись к переменной $_ или $PSItem в блоке End, мы получим значение последнего переданного по конвейеру элемента. Тот же результат мы получим и при использовании переменной, соответствующей имени параметра, которому были назначены поступающие по конвейеру элементы (листинг 20).
Как и в предыдущих случаях, если мы не будем использовать конвейер, а вызовем функцию, указав имя параметра, то увидим, что присвоенное ему значение будет доступно нам во всех трех блоках.
Structure -Name 'first', 'second', 'third' Begin: first second third Process: first second third End: first second third
И опять же стоит сказать, что, если ваша функция предназначена как для использования в конвейере, так и вне его, следует избегать обращения к поддерживающим конвейерную обработку данных переменным в блоках Begin и End.
Однако, как и в случае с блоком Begin, мы вполне можем задействовать в блоке End переменные из тех, что не предназначены для работы с конвейером (листинг 21).
Теперь, когда мы рассмотрели три составляющие функцию блока, я расскажу о том, для чего же они нужны.
Блок Begin предназначен для тех действий, которые должны быть выполнены еще до поступления по конвейеру первого элемента. Это может быть создание переменных, выполнение каких-либо проверок или же определение структур данных, к примеру классов, которые будут использоваться в последующих блоках.
Process применяется непосредственно для обработки поступающих данных. Именно в этом блоке используются $_, $PSItem, а также переменные, соответствующие имени получающего по конвейеру данные параметра.
Блок End содержит код, выполняющий окончательную обработку полученных в предыдущих блоках данных, такую, например, как фильтрация, форматирование или создание на их основе объектов определенного типа.
Теперь давайте вернемся к ситуации, когда при создании поддерживающей конвейерное получение данных функции мы не используем блоки Begin, Process и End (листинг 22).
Мы видим, что результатом ее становится значение последнего переданного по конвейеру элемента. Почему так происходит?
Дело в том, что в случае, если в функции не определено явным образом ни одного блока, считается, что весь ее код находится в блоке End. Таким образом, приведенная выше функция не содержит никаких инструкций для обработки каждого из поступающих по конвейеру элементов и обращается к переменной $Name уже после того, как ей был назначен последний элемент. По сути, эта функция аналогична показанной в листинге 23.
Массивы
Теперь давайте создадим функцию со всеми тремя блоками и протестируем ее. В блоке Begin мы определим переменную $i как равную нулю, в блоке Process выведем значения всех переданных функции элементов с указанием их порядкового номера, а в блоке End выведем количество полученных функцией элементов. Код функции приведен в листинге 24.
Сначала обратимся к ней с использованием конвейера:
'first', 'second', 'third' | Structure Begin processing. Element 1: first Element 2: second Element 3: third Number of elements: 3
Как видите, результат вполне ожидаемый. Сначала был запущен блок Begin, где мы инициализировали переменную. Затем блок Process, который отработал три раза — по одному для каждого поступившего по конвейеру элемента. Завершилось выполнение функции блоком End.
Теперь давайте воспользуемся параметром Name:
Structure -Name 'first', 'second', 'third' Begin processing. Element 1: first second third Number of elements: 1
Что же у нас получилось? Блок Process был вызван один раз, равно как и блоки Begin и End, а выведенное значение содержит все три полученных элемента. Почему функция отработала именно таким образом? Дело в том, что когда мы передаем данные функции (или команде PowerShell) посредством конвейера, они передаются друг за другом, по одному. Таким образом, в каждый момент времени переменная содержит только один элемент из тех, что передаются по конвейеру.
Если же мы используем параметр и в качестве его значения указываем массив данных, переменная, соответствующая этому параметру, будет содержать указанный массив целиком. То есть в этом случае блок Process запустится один раз для всего массива, без разделения его на элементы и обработки каждого из них независимо от всех остальных.
Таким образом, если мы ожидаем, что наша функция будет использоваться обоими способами, нам нужно сделать так, чтобы в любом случае каждый элемент был обработан по отдельности. Для этого в блок Process мы добавим конструкцию foreach. На использование функции как части конвейера это не окажет никакого влияния, а в случае ее вызова с указанием параметра поможет разделить входящий массив на отдельные элементы и обработать каждый из них независимо (листинг 25).
Теперь запустим функцию, как передав ей данные при помощи конвейера
'first', 'second', 'third' | Structure Begin processing. Element 1: first Element 2: second Element 3: third Number of elements: 3
так и с использованием параметра Name:
Structure -Name 'first', 'second', 'third' Begin processing. Element 1: first Element 2: second Element 3: third Number of elements: 3
Как мы видим, функция работает одинаково вне зависимости от способа получения данных.
Входные данные
Теперь давайте поговорим о переменной $Input. Мы можем использовать ее при обращении к обрабатываемым элементам, равно как и переменные $_ и $PSHost, хотя для этого нам нужно учитывать некоторые важные детали.
Начнем с того, что переменная $Input — это нумератор, и этим объясняются некоторые ее особенности.
Нумераторы позволяют получать данные из коллекций и делают это следующим образом. Нумератор расположен в начале коллекции, и для того, чтобы получить первый элемент, нам нужно переместить его на один элемент вперед, что позволяет сделать метод MoveNext. Получить этот элемент мы можем при помощи свойства Current нумератора (листинг 26).
Затем, для перехода к следующим элементам коллекции, используется все тот же метод MoveNext. Узнать, не достигли ли мы конца коллекции, позволяет возвращаемое этим методом значение. Если это True, то нумератор еще не дошел до конца коллекции и мы можем получить текущий элемент при помощи свойства Current. Если же возвращаемое значение False, это говорит о том, что конца коллекции мы достигли. При этом свойство Current не будет содержать какого-либо значения.
$e.MoveNext() True $e.MoveNext() True $e.Current third $e.MoveNext() False $e.Current
Для того чтобы переместить нумератор к началу коллекции, используется метод Reset.
$e.Reset()
Обращение к нумератору напрямую выводит все содержимое коллекции и перемещает нумератор в ее конец.
$e first second third $e.MoveNext() False
Вернемся к $Input. Так же, как и $_ и $PSItem, переменная $Input не будет содержать каких-либо значений в блоке Begin. В блоке Process она будет содержать обрабатываемый в данный момент элемент. После того как все поступившие по конвейеру элементы будут обработаны в блоке Process, в блоке End она опять же не будет содержать никакого значения (листинг 27).
Однако если блок Process в функции отсутствует, то, в отличие от $_ и $PSItem, обращение к переменной $Input в блоке End выведет весь массив полученных функцией данных (листинг 28).
И, конечно, не стоит забывать о том, что $Input — это нумератор. Поэтому, напрямую обратившись к этой переменной в любой части кода и получив нужное значение, мы переместим нумератор в конец коллекции, и для того, чтобы получить эти данные еще раз, нам потребуется воспользоваться методом Reset, как показано в листинге 29.
Параметры
А теперь давайте подумаем, что же будет, если мы определим несколько параметров в качестве поддерживающих получение данных через конвейер. Создадим такую функцию, как приведена в листинге 30.
Если при помощи конвейера мы передадим ей некие данные, то получим следующий результат:
'first' | SeveralParameters StringOne: first StringTwo: first Integer:
Таким образом, получается, что оба параметра, принимающие входные данные в виде объектов строки, StringOne и StringTwo, получили в качестве значения поступивший по конвейеру объект.
Мы помним, что поступающий по конвейеру объект может быть сопоставлен параметру в том случае, если его тип данных соответствует тому, что ожидается параметром, или же может быть к нему преобразован.
В нашем случае входящий объект ‘first’ является строкой и поэтому может быть назначен в качестве значения параметрам StringOne и StringTwo. Третий параметр — Integer — ожидает объект типа System.Int32. Строка, содержащая любые символы, кроме цифр, не может быть преобразована в этот тип данных. Однако давайте попробуем сделать следующее:
'110' | SeveralParameters StringOne: 110 StringTwo: 110 Integer: 110
В данном случае входящий объект был сопоставлен всем трем параметрам: в виде строки — параметрам StringOne и StringTwo и в виде Int32 — параметру Integer.
Наборы параметров
При создании собственных функций вам, скорее всего, не понадобится, чтобы входящие данные были сопоставлены нескольким параметрам одновременно.
Вы, вероятно, захотите воспользоваться возможностью определить несколько параметров как поддерживающих работу с конвейером и задействовать их в различных ситуациях, в зависимости от типа поступающих данных или же использования разных наборов параметров. Хотя чаще всего эти два условия будут использоваться вместе, ведь это в значительной мере облегчит вам построение функции.
Основываясь на предыдущем примере из листинга 30, предположим, что вы создаете функцию, которая, хотя и принимает данные как в виде строки, так и в виде целого числа, обрабатывает их по-разному. Например, такую как показана в листинге 31.
Теперь, если мы передадим ей строку, то получим следующий результат:
'first' | SeveralParameters The object is a string: first
Однако если мы передадим число, то вывод будет следующим:
110 | SeveralParameters The object is a string: 110 The object is an integer: 110
Как видите, преобразование командной средой целого числа в строку при сопоставлении входящих данных параметрам также не вызывает особых затруднений.
Здесь можно разнести параметры по разным наборам — Parameter Sets, что исключит возможность их одновременного использования, в том числе при сопоставлении поступающих по конвейеру объектов (листинг 32).
Теперь, если мы передадим функции как строку, так и целое число, результаты будут следующими:
'first' | SeveralParameters The object is a string: first 110 | SeveralParameters The object is an integer: 110
Почему же функция при сопоставлении поступающих объектов параметрам не конвертирует число 110 в строку и не назначает ее в качестве значения параметра String?
Дело в том, что, решая, какому параметру назначить входящий объект, PowerShell сначала пытается сопоставить его без выполнения преобразования и только в том случае, если подходящие параметры отсутствуют, приступает к его конвертации.
Стоит упомянуть, что передача функции значения 110 в виде строки, чего мы можем достичь, заключив его в кавычки, приведет к тому, что оно будет назначено параметру String.
'110' | SeveralParameters The object is a string: 110
Еще одним способом разделения логики функции, кроме получения значений переменных $String и $Integer, будет проверка имени используемого набора параметров. Мы указали, что параметр String принадлежит набору параметров str, а параметр Integer — набору праметров int. Получить имя текущего набора параметров мы можем при помощи автоматической переменной $PSCmdlet и ее свойства ParameterSetName.
Для того чтобы переменная $PSCmdlet была доступна из кода функции, нам нужно определить функцию как расширенную — Advanced Function, что делается при помощи атрибута CmdletBinding. Еще одним вариантом является указание атрибута Parameter хотя бы для одного параметра функции. Однако использование CmdletBinding — более формальный способ, поэтому давайте воспользуемся им. Вместе с видоизмененной логикой наша функция будет выглядеть так, как показано в листинге 33.
Передав ей массив значений, содержащий как строчные, так и числовые данные, мы получим следующий результат:
'one', 2, 3, 'four' | SeveralParameters The object is a string: one The object is an integer: 2 The object is an integer: 3 The object is a string: four
Стоит добавить, что если при использовании конвейера вы запросите содержимое свойства ParameterSetName переменной $PSCmdlet из блока Begin, то получите имя набора параметров по умолчанию. И это будет либо ‘__AllParameterSets’, либо то, что вы явным образом задали в аргументе DefaultParameterSetName атрибута CmdletBinding. В блоке End имя набора параметров будет соответствовать тому, что использовался при обработке последнего элемента конвейера (листинг 34).
Конвейер
Теперь давайте подытожим сказанное и рассмотрим очередность, с которой PowerShell пытается сопоставить поступающие по конвейеру данные различным параметрам.
Сначала проверяются параметры, поддерживающие работу с конвейером при помощи способа ByValue, то есть те из них, которые предназначены для получения объекта целиком. При этом попыток конвертации типа объекта на данном шаге не предпринимается.
Затем проверяются параметры, для которых указан способ ByPropertyName. Как вы помните, это способ, при котором значением параметра становится значение свойства входящего объекта, чье имя соответствует имени или псевдониму параметра. Конвертация данных здесь также не выполняется.
Если параметр, которому можно сопоставить поступающие по конвейеру объекты, все еще не найден, предпринимается попытка сопоставления с использованием их конвертации к нужному типу данных. Происходит это в той же последовательности: сначала используется способ ByValue, затем ByPropertyName.
Результат
Если сопоставление объектов параметрам происходит неочевидным для вас образом, вы можете воспользоваться командой PowerShell с именем Trace-Command, которая, помимо прочего, позволяет отследить каждый шаг, предпринимаемый командной средой для назначения поступающих данных различным параметрам. Например, так:
Trace-Command -Name ParameterBinding -Expression {'one', 2, 3, 'four' | SeveralParameters} -PSHost
Кроме того, для работы как с собственными функциями, так и с существующими командами PowerShell, вам может пригодиться модуль sthPipelineTools и его функции — Get-sthPipelineCommand и Get-sthPipelineParameter.
Get-sthPipelineCommand позволяет узнать, поддерживает ли команда PowerShell или функция получение данных при помощи конвейера. Варианты использования могут быть следующими:
$command = Get-Command -Module ‘Microsoft.PowerShell. Management’ Get-sthPipelineCommand -Command $command 'Get-Process', 'Get-Service', 'gcim', 'gwmi' | Get-sthPipelineCommand
Get-sthPipelineParameter выводит информацию о поддерживающих конвейерную обработку данных параметрах команды PowerShell или функции, наборах параметров, в которых они могут быть использованы, а также о применяемых ими способах сопоставления — ByValue, ByPropertyName, поступающих по конвейеру объектов.
Например, так:
Get-sthPipelineParameter Get-Item ‘Get-Content’, ‘sv’ | Get-sthPipelineParameter
Таким же образом мы можем получить информацию и о параметрах функции, созданной нами ранее.
Get-sthPipelineParameter -Command SeveralParameters
Установить модуль sthPipelineTools можно из хранилища сценариев PowerShell Gallery следующим образом:
Install-Module -Name sthPipelineTools
Итак, мы рассмотрели основные способы применения конвейера при создании собственных функций. При этом мы затронули блоки Begin, Process и End, из которых может состоять функция, особенности использования наборов параметров при распределении поступающих по конвейеру объектов, а также способы сопоставления значений параметрам, как при совпадении типа данных входящего объекта и параметра, так и в случаях, когда для этого требуется преобразование значения к определенному типу. Я надеюсь, что эта статья поможет вам в освоении такого мощного и удобного механизма, как конвейер.
Get-Process | Group-Object -Property Name | Sort-Object -Property Count -Descending | Select-Object -First 10
filter NotTouchingPipelineElements { "Step: $($i++; $i)" } ‘first’, ‘second’, ‘third’ | NotTouchingPipelineElements Step: 1 Step: 2 Step: 3
filter TouchingPipelineElements { "`nStep: $($i++; $i)" "Value: $_" } 'first', 'second', 'third' | TouchingPipelineElements Step: 1 Value: first Step: 2 Value: second Step: 3 Value: third
filter TouchingPipelineElements { Param( $Parameter ) "`nStep: $($i++; $i)" "Value: $_" "Parameter Value: $Parameter" } 'first', 'second', 'third' | TouchingPipelineElements -Parameter 'some_value' Step: 1 Value: first Parameter Value: some_value Step: 2 Value: second Parameter Value: some_value Step: 3 Value: third Parameter Value: some_value
Get-Help -Name Stop-Process -Parameter InputObject -InputObjectSpecifies the process objects to stop. 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
ByPropertyName C:\> Get-Help -Name Stop-Process -Parameter Name -NameSpecifies the process names of the processes to stop. You can type multiple process names, separated by commas, or use wildcard characters. Required? true Position? named Default value None Accept pipeline input? True (ByPropertyName) Accept wildcard characters? false
function UsingPipeline { Param( [Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] $Name ) }
function UsingPipeline { Param( [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)] $Name ) }
function UsingPipeline { Param( [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)] [string]$Name ) $Name }
'first' | UsingPipeline first [PSCustomObject]@{Name = 'first'} | UsingPipeline first UsingPipeline -Name 'first' first
function UsingPipeline { Param( [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias('HostName','NodeName')] [string]$Name ) $Name }
[PSCustomObject]@{HostName = ‘first’} | UsingPipeline first [PSCustomObject]@{NodeName = 'first'} | UsingPipeline first
function UsingPipeline { Param( [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias('HostName','NodeName')] [string[]]$Name ) $Name }
UsingPipeline -Name 'first', 'second', 'third' first second third 'first', 'second', 'third' | UsingPipeline third [PSCustomObject]@{Name = 'first'}, [PSCustomObject]@{Name = 'second'}, [PSCustomObject]@{Name = 'third'} | UsingPipeline third
function Structure { Param() Begin { "Begin: $_" } Process {} End {} } 'first', 'second', 'third' | Structure Begin:
function Structure { Param( [Parameter(ValueFromPipeline)] [string[]]$Name ) Begin { "Begin" "`$_: $_" "`$Name: $Name" } Process {} End {} } ‘first’, ‘second’, ‘third’ | Structure Begin $_: $Name:
function Structure { Param( [Parameter(ValueFromPipeline)] [string[]]$Name, [string]$AnotherParameter ) Begin { "Begin" "`$AnotherParameter: $AnotherParameter" } Process {} End {} } 'first', 'second', 'third' | Structure -AnotherParameter 'some_value' Begin $AnotherParameter: some_value
----- function Structure { Param() Begin { "Begin: $_" } Process { "Process: $_" } End {} } ‘first’, ‘second’, ‘third’ | Structure Begin: Process: first Process: second Process: third ----- function Structure { Param( [Parameter(ValueFromPipeline)] [string[]]$Name ) Begin { "Begin: $Name" } Process { "Process: $Name" } End {} } ‘first’, ‘second’, ‘third’ | Structure Begin: Process: first Process: second Process: third
function Structure { Param() Begin { "Begin: $_" } Process { "Process: $_" } End { "End: $_" } } ‘first’, ‘second’, ‘third’ | Structure Begin: Process: first Process: second Process: third End: third
function Structure { Param( [Parameter(ValueFromPipeline)] [string[]]$Name ) Begin { "Begin: $Name" } Process { "Process: $Name" } End { "End: $Name" } } 'first', 'second', 'third' | Structure Begin: Process: first Process: second Process: third End: third
function Structure { Param( [Parameter(ValueFromPipeline)] [string[]]$Name, [string]$AnotherParameter ) Begin { "Begin" "`$AnotherParameter: $AnotherParameter" } Process {} End { "`nEnd" "`$AnotherParameter: $AnotherParameter" } } 'first', 'second', 'third' | Structure -AnotherParameter 'some_value' Begin $AnotherParameter: some_value End $AnotherParameter: some_value
function UsingPipeline { Param( [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias('HostName','NodeName')] [string[]]$Name ) $Name } 'first', 'second', 'third' | UsingPipeline third
function UsingPipeline { Param( [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias('HostName','NodeName')] [string[]]$Name ) End { $Name } } 'first', 'second', 'third' | UsingPipeline third
function Structure { Param( [Parameter(ValueFromPipeline)] [string[]]$Name ) Begin { "Begin processing.`n" $i = 0 } Process { $i++ "Element ${i}: $Name" } End { "`nNumber of elements: $i" } }
function Structure { Param( [Parameter(ValueFromPipeline)] [string[]]$Name ) Begin { "Begin processing.`n" $i = 0 } Process { foreach ($n in $Name) { $i++ "Element ${i}: $n" } } End { "`nNumber of elements: $i" } }
$collection = ‘first’, ‘second’, ‘third’ $e = $collection.GetEnumerator() $e.Current $e.MoveNext() True $e.Current first
function GettingInput { Begin { "Begin: $input" } Process { "Process: $input" } End { "End: $input" } } 'first', 'second', 'third' | GettingInput Begin: Process: first Process: second Process: third End:
function GettingInput { Begin { "Begin: $input" } End { "End: $input" } } 'first', 'second', 'third' | GettingInput Begin: End: first second third
function GettingInput { Process { "Process: $input" "One more time: $input" $input.Reset() "After reset: $input" } } 'first' | GettingInput Process: first One more time: After reset: first
function SeveralParameters { Param( [Parameter(ValueFromPipeline)] [string[]]$StringOne, [Parameter(ValueFromPipeline)] [string[]]$StringTwo, [Parameter(ValueFromPipeline)] [int[]]$Integer ) Process { "StringOne: $StringOne" "StringTwo: $StringTwo" "Integer: $Integer" } }
function SeveralParameters { Param( [Parameter(ValueFromPipeline)] [string[]]$String, [Parameter(ValueFromPipeline)] [int[]]$Integer ) Process { if ($String) { "The object is a string: $String" } if ($Integer) { "The object is an integer: $Integer" } } }
function SeveralParameters { Param( [Parameter(ParameterSetName='str', ValueFromPipeline)] [string[]]$String, [Parameter(ParameterSetName='int', ValueFromPipeline)] [int[]]$Integer ) Process { if ($String) { "The object is a string: $String" } if ($Integer) { "The object is an integer: $Integer" } } }
function SeveralParameters { [CmdletBinding()] Param( [Parameter(ParameterSetName=’str’, ValueFromPipeline)] [string[]]$String, [Parameter(ParameterSetName=’int’, ValueFromPipeline)] [int[]]$Integer ) Process { if ($PSCmdlet.ParameterSetName -eq ‘str’) { "The object is a string: $String" } if ($PSCmdlet.ParameterSetName -eq ‘int’) { "The object is an integer: $Integer" } } }
function SeveralParameters { [CmdletBinding(DefaultParameterSetName='default')] Param( [Parameter(ParameterSetName='str', ValueFromPipeline)] [string[]]$String, [Parameter(ParameterSetName='int', ValueFromPipeline)] [int[]]$Integer ) Begin { "Begin: $($PSCmdlet.ParameterSetName)" } Process { "Process: $($PSCmdlet.ParameterSetName)" } End { "End: $($PSCmdlet.ParameterSetName)" } } 'one', 2, 3, 'four' | SeveralParameters Begin: default Process: str Process: int Process: int Process: str End: str