Разное

Конвейер powershell: Использование конвейеров в Windows PowerShell

Содержание

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

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

Акт передачи выходных данных одного командлета во входные данные другого командлета называется конвейеризацией. Возможно, в других оболочках вам уже приходилось использовать конвейер. Например, это стандартная команда в Cmd.exe:

Dir | More

Здесь выходные данные команды Dir перенаправляются во входные данные команды More, которая создает постраничное отображение выходных данных.

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

Командлеты в Windows PowerShell ориентированы на такой способ работы в большей степени, чем команды в любой традиционной оболочке Windows или Unix. Конвейер позволяет выполнять очень сложные и мощные задачи без необходимости написания длиннейших скриптов и программ. Конвейеризация является настолько мощной и важной концепцией, что следующий раздел курса будет полностью посвящен ей.

Все команды в рамках Shell выполняются в виде конвейера. Чтобы лучше понять принцип действия такого конвейера, представьте трубу, по которой течет поток информации. Каждый командлет – это насосная станция, а труба соединяет каждую станцию с последующей. Каждая станция забирает информацию из трубы и добавляет туда новые данные, после чего видоизмененный поток движется к следующей станции.

Команды выполняются в одностороннем порядке – слева направо, а знак (“|”) является разграничителем между командлетами. Вы уже видели такие примеры, скажем, этот:

Get-Process | Format-List

Это простейший пример использования конвейера. В действительности с его помощью можно создавать значительно более сложные и мощные команды. Например, чуть позже вы узнаете, как написать командную строку вроде этой:

Import-CSV c:\new_users.csv | New-ADUser –passThru | Enable-ADAccount

Эта простая трехкомпонентная командная строка позволяет создать сотни новых пользователей Active Directory и активировать их аккаунты. При старых административных технологиях выполнение этой задачи могло потребовать десятки строк на скриптовом языке программирования; с командлетами и конвейером Windows PowerShell данная задача выполняется исключительно с помощью синтаксиса, который вы только что видели (конечно, если были выполнены некоторые предварительные условия, о которых вы узнаете чуть позже).

Windows PowerShell – это, конечно, не первая оболочка, в которой используется технология конвейера. В оболочках Unix уже давным-давно информация от команды к команде передается по конвейеру, да и в Windows (в частности, в Cmd.exe) используется нечто подобное. Проблема же с этими старыми оболочками заключалась в том, что они передавали от одной команды к другой не что иное, как текст. Например, представьте себе, что команда под названием Plist передает текстовое описание текущего процесса, которое выглядит приблизительно так:

Pid Name Image

— ————————- ————-

092 Notepad notepad.exe

098 Windows Paint mspaint.exe

112 Calculator calc.exe

164 Windows PowerShell powersehll.exe

Предположим, вашей целью является завершить работу всех копий Windows Notepad. У вас есть команда под названием Kill, которая принимает ID (PID) процесса и завершает этот процесс. Но вы не можете просто запустить команду:

Plist | Kill

Для начала вы должны определить, какая строка выходных данных Plist относится к Notepad; затем вам нужно сократить эту строку до PID, который требуется команде Kill. В Unix все это осуществляется с использованием утилит, анализирующих текст, таких как Sed, Grep, и Awk. Например, обычной для Unix является такая команда:

ps auxwww | grep httpd

Проблема при таком подходе состоит в том, что вам приходится тратить больше времени на анализ и модификацию текста, нежели на выполнение самой задачи. Скриптовые языки вроде Perl приобрели популярность исключительно из-за их мощных возможностей анализа текста. Администраторы, использующие такие оболочки, должны быть экспертами в области анализа текста, хотя технически эти манипуляции не имеют ничего общего с административными задачами.

Windows PowerShell позволяет отказаться от текстовых выходных данных в пользу другой их формы. Командлет Windows PowerShell всегда производит элементы, которые представляют собой компоненты операционной системы вместо текста.

Что же это за элементы? По сути элемент – это самостоятельная структура данных, которая описывает часть функционирования операционной системы или серверной технологии. Процесс, например, является частью функционирования операционной системы. Сервис – это часть операционной системы. Файл или папка – часть операционной системы. Пользовательский аккаунт – это элемент операционной системы, точно так же, как и группа, организационная единица или сам домен. Вы можете воспринимать элемент как разновидность структурной единицы, с которой Windows PowerShell работает очень легко и быстро. Вместо того, чтобы давать оболочке команду «пропустить 10 символов в каждом ряду, чтобы найти колонку, которая содержит название процесса», вы можете просто дать ей задание обратиться к атрибуту Name, поскольку структура, используемая в конвейере, создана таким образом, что атрибуты становятся легкодоступными.

Терминология

Windows PowerShell использует терминологию, которая иногда кажется тесно связанной с разработкой программного обеспечения. Помните, что это всего лишь слова, и их употребление не означает, что вам придется стать программистом, чтобы начать пользоваться Windows PowerShell. Например, в предыдущем разделе вы видели, что слово «элемент» используется для обозначения того, что командлет отправляет по конвейеру. Например, командлет Get-Process помещает элементы процесса в конвейер. Более официальным термином здесь будет слово «объект», т.е. вы можете сказать, что командлет Get-Process помещает объекты процесса в конвейер.

Эти объекты имеют атрибуты. Для процесса атрибутами могут быть имя, ID, объем занимаемой памяти и.т.д. Формальным названием атрибута будет термин «свойство». Другими словами, вы можете сказать, что объект процесса обладает свойством имени, свойством ID, и.т.д.

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

Pid       Name                                  Image

—        ————————-          ————-

092      Notepad                              notepad.exe

098      Windows Paint                   mspaint.exe

112      Calculator                           calc.exe

164      Windows PowerShell          powershell.exe

Однако, как вы уже знаете, совершение различных манипуляций с текстом занимает слишком много времени. Структура данных объекта, в свою очередь, во многом упрощает доступ как к одному свойству объекта, так и ко всему набору его свойств. В рамках данного курса мы будем употреблять термин «свойство» наряду с термином «атрибут», чтобы время от времени напоминать читателям, что название – это не главное. Намного важнее – понять, что Windows PowerShell обеспечивает простой и быстрый доступ к данной информации через конвейер.

Свойства объектов

Поскольку свойства стали доступными без необходимости текстово-синтаксического анализа, они стали обеспечивать более быстрый и простой доступ к определенным частям информации. Командлеты Windows PowerShell производят объекты, а также могут принимать объекты как входящие данные. Конкретные параметры командлетов могут создаваться для поиска конкретных свойств входящих объектов, а если эти свойства найдены, командлет может использовать данную информацию в качестве входящих данных параметра.

Например, представьте, что командлет Get-Service имеет параметр, который называется –computerName. Данный параметр будет уточнять имя удаленного компьютера, с которого вы хотите получить службы:

Get-Service –computerName SEA-SRV2

Данный параметр также может быть создан разработчиком командлета для поиска свойства «Имя компьютера» у входящего объекта. Предположим, у вас есть командлет под названием Get-ComputerInventory, который извлекает список компьютеров из конфигурационной базы данных. Каждый компьютер в базе данных будет выступать в качестве объекта, а каждый объект может иметь несколько свойств, включая свойство «Имя компьютера» (ComputerName). В данном случае для извлечения служб из всех обслуживаемых компьютеров можно запустить следующую команду:

Get-ComputerInventory | Get-Service

Нет нужды отображать инвентаризационную информацию в виде текстовой таблицы, подвергать анализу колонку «Computer Name», и.т.д., так как вы работаете не с текстом. Windows PowerShell передает объекты по конвейеру от одного командлета к другому и устанавливает связь между свойствами объекта и параметрами второго командлета.

В действительности командлета Get-ComputerInventory, встроенного в Windows PowerShell нет, но командлет Get-Service функционирует именно так, как было описано. Вы увидите примеры его использования чуть позже.

Другим исследовательским командлетом PowerShell является командлет Get-Member. Если вы поместите объект в конвейер к нему, то получите список членов класса объекта, который будет включать все свойства объекта. Например

Get-EventLog Security –newest 10 | Get-Member

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

Get-Process | Format-List *

Другим хорошим путем является использование Get-Member вместе с командлетом Out-GridView

Get-Process | Out-GridView

В ответ будет выдано окно с просмотрщиком табличных данных

Помимо командлета Out-GridView, с которым вы только что познакомились, оболочка предлагает еще несколько командлетов Out-. Каждый из них предназначен для перенаправления выходных данных в конкретное место:

·       Out-Printer

·       Out-File

·       Out-Host

Out-Host перенаправляет данные в окно консоли, что является действием по умолчанию, о котором вы уже слышали.

Out-File – исключительно полезный командлет; при указании имени файла он перенаправляет данные непосредственно в этот файл. При этом объект не форматируется, а значит, предварительно следует использовать один из форматирующих командлетов:

Get-EventLog Application –newest 100 | Format-List * | Out-File events.txt

Не забывайте обращаться к справочной информации! Out-File, например, может иметь множество параметров, которые позволяют уточнить кодировку символов, желаемое количество знаков в каждой строке выходного текстового файла, и.т.д.

Out-Null – еще одна опция, имеющая специальное предназначение. Иногда командлеты превращают объекты не в то, что вам нужно. Чтобы отменить данные изменения, используется командлет Out-Null:

Get-Service | Out-Null

Вы также можете использовать старый синтаксис > и >> для перенаправления объекта в файл. В данном случае командлет Out- File используется скрыто, что упрощает отправку данных в текстовый файл, хотя при этом вы лишаетесь возможности применить дополнительные параметры Out- File:

Get-Process > procs.txt

На заметку: Если вы не знакомы с данным синтаксисом, запомните, что > отправляет объект в новый файл, заменяя любой существующий файл с таким же названием (Out-File ведет себя точно так же по умолчанию), а >> добавляет объект в конец существующего объекта (так же, как это делает параметр –append командлета Out-File).

Windows PowerShell передает объекты по конвейеру до его конца. На конце конвейера оставшиеся объекты передаются в специальный командлет, который называется Out-Default. Его не нужно прописывать вручную, поскольку он уже встроен в конвейер. Его работа заключается в том, чтобы принять получившиеся объекты и передать их на консольный командлет Out-Host, который отображает выходные данные на экране в текстовом виде. Поскольку объекты не конвертируются в текст до конца конвейера, вы имеете доступ к их свойствам до самого конца. Это означает возможность создавать сложные команды без необходимости текстового анализа.

Это общее правило имеет два исключения:

·       Командлеты Format- производят объекты, но эти объекты являются крайне специфическими – их «понимают» только командлеты Out-. Это означает, что Format- командлет должен быть либо последним в конвейере, либо предпоследним (перед командлетом Out-).

·       Большинство командлетов Out- не производят никаких объектов; они лишь отправляют объекты (например, текст) на какое-либо устройство (файл, принтер или окно консоли). Поэтому, командлеты Out- следует располагать в конце конвейера.

Во многом мощь и гибкость Windows PowerShell связаны с возможностью передавать объекты от одного командлета к другому по конвейеру. Поскольку эти командные строки – даже очень длинные – представляют собой всего одну логическую цепочку текста внутри оболочки, их часто называют однолинейниками (one-liners). В большинстве случаев один такой однолинейник может выполнить те же самые задачи, для решения которых потребовалось бы множество строк, написанных на скриптовом языке в рамках любой их старых технологий, например, Visual Basic Scripting Edition (VBScript). Например, этот однолинейник с использованием техник, которые мы рассмотрим позже, объединяет все имена компьютеров, перечисленных в c:\computers.txt и даже создает отчет о размере журнала регистрации событий:

Get-Content c:\computers.txt | where {($_.Trim()).length -gt 0} | foreach {

Get-WmiObject Win32_NTEventLogFile -computer $_.Trim() `

-filter «NumberOfRecords > 0» | Select-Object `

@{Name=»Computername»;Expression={$_.CSName}},LogFileName,NumberOfRecords,`

@{Name=»Size(KB)»;Expression={$_.FileSize/1kb}},`

@{Name=»MaxSize(KB)»;Expression={($_.MaxFileSize/1KB) -as [int]}}, `

@{name=»PercentUsed»;Expression={«{0:P2}» -f ($_.filesize/$_.maxFileSize)}}

} | Sort Computername | Format-Table -GroupBy Computername `

-property LogFileName,NumberOfRecords,*Size*,PercentUsed

Конечно, это сложная команда, но она прекрасно иллюстрирует мощь и гибкость конвейера.

Когда вы начнете работать с Windows PowerShell, основной причиной ошибок и затруднений могут стать ваши предположения об именах свойств или значений, которые они содержат. Например, если вы предположите, что существует свойство с именем DriveLetter, тогда как в действительности его имя – DeviceID, вы можете потерять несколько минут на выяснение этого факта. Создание однолинейника или скрипта, в котором предполагается значение FixedDisk для свойства DriveType, может повлечь проблемы, если на самом деле это свойство имеет значение «3».

Вывод из всего этого заключается в том, что никогда не надо строить предположения. Всегда используйте Get-Member, чтобы уточнить имя свойства, и перенаправляйте объекты в Format-List *, чтобы уточнить содержание этих свойств. Проверка займет совсем не много времени и позволит вам избежать ошибок и сэкономить время.

Конвейер в PowerShell | Windows IT Pro/RE

Конвейер — основная концепция, которую необходимо изучить, иначе вы сможете использовать лишь ничтожную часть функций PowerShell. Конвейерный принцип был реализован еще в оболочках операционной системы Unix, затем скопирован в Cmd.exe, а в PowerShell поднят на новый уровень. Прежде чем перейти к теме конвейера PowerShell, я считаю целесообразным напомнить общие сведения, касающиеся стандартного входа и выхода.

Стандартный вход и выход

В Cmd.exe конвейер тесно связан со стандартным входом, перенаправлением входа, стандартным выходом и его перенаправлением. По существу, стандартный вход — это ввод с клавиатуры, если только вы не предписываете оболочке считать входные данные откуда-либо еще (перенаправление входа). Стандартный выход — это обычный результат выполнения команды, отображаемый на экране, если только вы не предписываете оболочке сохранить выходные данные куда-либо еще (перенаправление выхода).

Чтобы увидеть работу стандартного входа, введем в окне PowerShell или Cmd.exe следующую команду:

sort.exe

После ввода этой команды курсор стоит на месте и ждет дальнейшего ввода, так как по умолчанию команда Sort.exe сортирует стандартный вход. Поскольку мы ничего не указали, команда ожидает ввода. Для отмены действия команды нажмите комбинацию клавиш Ctrl+C.

Теперь предположим, что существует файл с именем MyData.txt, данные в котором требуется сортировать. Вот как будет выглядеть на экране сортируемый выход файла (стандартный выход):

type MyData.txt | sort.exe

В этом примере команда Type выводит содержимое файла MyData.txt как стандартный выход, который поступает на конвейер (|) и используется в качестве входа для Sort.exe.

Таким образом, используя в команде символ конвейера (|), мы создаем конвейер. При этом выход команды слева от этого символа поступает на вход команды, находящейся справа.

В большинстве командных оболочек (например, Cmd.exe) стандартный выход и стандартный вход представляют собой текст. Это делает решение многих задач, связанных с различными манипуляциями с данными, неудобным и громоздким. На приведенном экране показан пример «кульбитов», которые приходится сделать в Cmd.exe, чтобы всего лишь вывести список текстовых файлов, последний раз сохраненных в текущем году.

 

Экран. Сценарии Cmd.exe для вывода списка текстовых файлов, созданных в текущем году

Сценарий Sample1.cmd выводит время последнего сохранения каждого файла, за которым следует символ жесткой табуляции, после чего выводится имя файла. Сценарий Sample2.cmd берет текущий год и выполняет Sample1.cmd, выводя лишь те файлы, у которых год последнего сохранения совпадает с текущим годом. Красная стрелка указывает на символ жесткой табуляции в обоих сценариях. На экране также показан выходной результат выполнения Sample2.cmd (File1.txt и File3.txt).

Отметим, что оба сценария предусматривают синтаксический анализ строк, зависящий от формата строки даты (%%~tF в Sample1.cmd и %DATE% в Sample2.cmd). В отличных от американо-англоязычных версиях Windows строки кода, где используется дата, придется корректировать, так как различные языковые стандарты используют разные форматы даты. Кроме того, из-за мудреного синтаксиса сценарии Cmd.exe малопонятны и неудобны в использовании (к примеру, что означает%DATE:~10,4%?).

Цель данного примера — продемонстрировать неуклюжее и громоздкое решение задачи, кажущейся простой (вывод списка файлов, созданных в текущем году). Здесь значительная часть проблемы обусловлена необходимостью выполнения синтаксического анализа строк для определения года. Кроме всего прочего, синтаксис года зависит от языкового стандарта, что может осложнить ситуацию для сред, в которых сценарии используются коллективно. Заметим также, что с появлением дополнительных требований (например, удалить файлы, последний раз сохраненные до наступления текущего года) сценарии будут становиться все более громоздкими и сложными для восприятия. Должен существовать более удобный путь. Давайте посмотрим, как подобные задачи решает PowerShell.

Конвейер PowerShell

Как уже говорилось, стандартный выход и стандартный вход — это средство, позволяющее командным оболочкам на базе текста (например, Cmd.exe) реализовать конвейерную передачу текстовых данных между программами. Конвейер PowerShell использует ту же основную схему, в рамках которой выход одной команды подается на вход другой команды, с той лишь разницей, что в этом случае выход и вход — это объекты, а не текст. При своей принципиальной простоте данная концепция имеет далеко идущие результаты.

Фильтрация с помощью Where-Object

Рассмотрим предыдущий пример: задача вывода списка файлов *.txt, в последний раз записанных в текущем году. В PowerShell это делается путем извлечения объектов файловой системы (Get-ChildItem) и выбора (Where-Object) только тех из них, у которых значением свойства LastWriteTime является текущий год:

Get-ChildItem *.txt | Where-Object {
  $_.LastWriteTime.Year -eq (Get-Date).Year
}

Эту команду можно записать в одну строку, но я разбил ее на несколько строк, чтобы облегчить восприятие. Код между фигурными скобками {} называется блоком сценария. В блоке сценария Where-Object переменная $_ означает «текущий объект с конвейера». Таким образом, данная команда предписывает «взять объекты файловой системы, относящиеся к типу *.txt (Get-ChildItem *.txt) и вывести из них только те, у которых (Where-Object) год последнего сохранения ($_.LastWriteTime.Year) равен (-eq) текущему году ((Get-Date).Year)».

Как видно из этого примера, команда Where-Object позволяет фильтровать объекты, подаваемые слева на конвейер. В качестве результата выводятся объекты, удовлетворяющие критерию, указанному в фильтре. Заметим, что синтаксический анализ даты не проводится, а просто определяется год последнего сохранения каждого файла.

Теперь предположим, что требуется удалить файлы, последнее сохранение которых выполнялось до начала текущего года. Чтобы решить эту задачу, достаточно немного изменить фильтр и подать результат по конвейеру на вход Remove-Item:

Get-ChildItem *.txt | Where-Object {
  $_.LastWriteTime.Year -lt (Get-Date).Year
} | Remove-Item

Все, что мы изменили, — использовали -lt (меньше) вместо -eq (равно), а затем после символа конвейера добавили команду Remove-Item.

В этих двух командах PowerShell вместо передачи текстовых строк между командами передаются объекты: файл — это объект; дата его последнего сохранения — тоже объект.

Выполнение действий с помощью ForEach-Object

Помимо фильтрации с помощью Where-Object, для каждого объекта, проходящего по конвейеру, можно выполнить определенное действие с помощью ForEach-Object. Подобно Where-Object, команда ForEach-Object использует блок сценария и переменную $_, представляющую текущий объект на конвейере.

Для примера предположим, что нам требуется вывести полный путь и имя каждого файла *.txt. Команда, позволяющая решить эту задачу, выглядит так:

Get-ChildItem *.txt | ForEach-Object {
  $_.FullName
}

Выход этой команды — полный путь и имя каждого файла *.txt. Конечно, внутри блока сценария можно выполнить много других действий. Например, записать имена, а затем удалить файлы *.log из каталога C:\Logs позволяет такая команда:

Get-ChildItem C:\Logs\*.log |
ForEach-Object {
  «Removing $($_.FullName)»
  Remove-Item $_
} | Out-File C:\Logs\Cleanup.txt -Append

Эта команда выводит текстовую строку «Удаление», а затем удаляет файл (Remove-Item). Все выведенные строки записываются в файл C:\Logs\Cleanup.txt.

Фильтрацию (Where-Object) можно комбинировать с действиями (ForEach-Object) для построения еще более гибких команд. Например, удалить файлы *.log старше шести месяцев и записывать имя каждого из них перед удалением позволяет следующая команда:

Get-ChildItem C:\Logs\*.log | Where-Object {
  $_.LastWriteTime -lt
  (Get-Date).AddMonths(-6)
} | ForEach-Object {
  "Removing $($_.FullName)"
  Remove-Item $_
} | Out-File C:\Logs\Cleanup.txt -Append

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

Мощь конвейера

Конвейер — это краеугольный камень, открывающий возможность реализации всего богатства функций PowerShell. Поэкспериментировав с описанными выше примерами, вы обнаружите, что PowerShell упрощает сложные задачи намного эффективнее, чем это возможно в Cmd.exe. Получить дополнительную информацию и ознакомиться с другими примерами можно в разделе справки PowerShell, посвященном конвейерам (https://technet.microsoft.com/en-us/library/hh847902.aspx).

Поделитесь материалом с коллегами и друзьями

Использование конвейера в PowerShell | Windows IT Pro/RE

Конвейерная обработка данных присутствует во многих командных интерпретаторах, однако PowerShell выгодно отличает от прочих то, что между составными частями конвейера, то есть командами и функциями, передается не текст, который представляет собой результат выполнения предыдущей команды, а объекты. Таким образом, когда мы вводим команду:

Get-Service -Name Spooler | Restart-Service

команда Restart-Service получает в качестве входных данных не три строки текста, как на рисунке 1, а объект типа System.ServiceProcess.ServiceController.

 

Рисунок 1. Результат выполнения команды Get-Service -Name Spooler

Что это нам дает? Возможность использовать результаты выполнения команд (объекты) по своему усмотрению: сортировать и группировать на основе значений свойств, вызывать методы, передавать в другие команды или собственноручно написанные функции и даже определять, как они должны выглядеть при вводе команд вида Format-*.

Тем не менее давайте сосредоточимся на теме статьи и попробуем разобраться, почему приведенная выше команда сработала именно таким образом и как мы можем это использовать при решении своих ежедневных задач.

Получение данных по конвейеру

Хотя выше уже упоминалось, что результатом выполнения команды Get-Service будет объект System.ServiceProcess.ServiceController, стоит уточнить, как мы получили эту информацию, поскольку знание типа возвращаемого командой объекта поможет нам определить, на взаимодействие с какими командами и параметрами мы можем рассчитывать.

Для получения типа объекта, а также списка его свойств и методов мы можем задействовать команду Get-Member. Например, так:

Get-Service -Name Spooler | Get-Member

В самом начале вывода мы увидим строку:

TypeName: System.ServiceProcess.ServiceController

Это и есть тип возвращаемого командой Get-Service объекта.

Теперь нам нужно узнать, какие команды и параметры поддерживают получение данного типа объектов по конвейеру. Для получения списка команд, принимающих объекты типа System.ServiceProcess.ServiceController в качестве значений параметров, пусть и безотносительно возможности получения этими параметрами данных непосредственно по конвейеру, воспользуемся командой:

Get-Command -ParameterType
   System.ServiceProcess.ServiceController

В качестве результатов ее выполнения мы получим данные, приведенные на рисунке 2.

 

Рисунок 2. Результаты работы команды Get-Command

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

Для того чтобы определить, какие именно параметры принимают объекты служб в виде значений и, что не менее важно, поддерживают ли они получение этих объектов по конвейеру, нам потребуется команда Get-Help. Так как из всего содержимого файла справки для команды Restart-Service нам нужны только сведения о параметрах, мы слегка ограничим ее вывод:

Get-Help -Name Restart-Service
   -Parameter *

Среди всех свойств параметров нас больше всего интересует ‘Accept pipeline input?’. Его значениями могут быть True либо False. Значение True сообщает нам о том, что данный параметр поддерживает получение значений по конвейеру. В скобках после True указывается, каким именно способом он может это делать.

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

ByPropertyName говорит о том, что значением данного параметра будет значение одноименного свойства получаемого по конвейеру объекта.

Возвращаясь к команде Restart-Service, мы видим, что параметров, поддерживающих получение данных по конвейеру, у нее два — InputObject и Name (рисунок 3).

 

Рисунок 3. Параметры команды Restart-Service

InputObject поддерживает только вариант с передачей всего объекта — ByValue, и, как мы видим, это может быть только объект (или объекты) типа ServiceController.

Параметр Name принимает объект целиком (и в данном случае это должна быть строка — String) или же, если по конвейеру поступает объект иного типа, аргументом для данного параметра будет значение свойства Name входящего объекта.

Однако, присмотревшись к объекту System.ServiceProcess.Service Controller (рисунок 4), мы увидим, что он тоже содержит свойство Name. В связи с этим возникает вопрос: значением какого параметра, InputObject или Name, становятся получаемые по конвейеру данные?

 

Рисунок 4. Свойства объекта System.ServiceProcess.ServiceController

Вспомним о таком понятии, как наборы параметров — Parameter Sets. Их существование обусловлено возможностью присутствия в одной команде несовместимых друг с другом параметров.

Например, мы можем получить объект процесса, указав его имя — параметр Name, или идентификатор — параметр Id. Указывать оба параметра в одной команде бессмысленно, поэтому они принадлежат разным наборам параметров, Name и Id соответственно. В данном случае имена наборов повторяют имена входящих в них несовместимых параметров, однако это не является обязательным.

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

И стоит сказать, что наша команда

Get-Service -Name Spooler | Restart-Service

это тот самый случай. Теперь, чтобы узнать, значением какого параметра станет поступающий по конвейеру объект службы, нам нужно определить набор параметров по умолчанию. Для этого нам понадобится команда Get-Command с параметром -Syntax:

Get-Command Restart-Service -Syntax

Выведенные в качестве результата три строки — это три набора параметров команды Restart-Service. И первый из них — набор параметров по умолчанию. Таким образом, мы теперь знаем, что поступающий по конвейеру объект ServiceController становится значением параметра InputObject.

Стоит сказать, что вывод команды Get-Help, например

Get-Help Restart-Service

тоже содержит наборы параметров указанной команды, однако в данном случае нет никакой гарантии в том, что первым указан именно набор параметров по умолчанию. Соответственно, для этих целей лучше задействовать команду Get-Command.

Для получения более подробной информации о наборах параметров команды, их именах и о том, какой из них является набором параметров по умолчанию, можно использовать следующую команду:

Get-Command Restart-Service |
   Select-Object -ExpandProperty
      ParameterSets | Select-Object -Property *

К слову, упомянутые выше имена наборов параметров команды Get-Process — Name и Id — мы получили именно таким образом.

Итак, при передаче объекта службы команде Restart-Service используется параметр -InputObject. Для чего же тогда потребовалось обеспечивать возможностью получения данных по конвейеру и параметр Name, да еще с использованием обоих вариантов, как ByValue, так и ByPropertyName? Для гибкости.

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

'Audiosrv’, ‘Spooler’, ‘SysMain’ |
   Restart-Service

Вариант ByPropertyName позволяет нам создать пользовательский объект со свойством Name или же приспособить для этого объект другого типа. Например, мы можем получить сведения об определенных службах при помощи команды Get-CimInstance:

Get-CimInstance -ClassName
   Win32_Service -Filter "Name='Spooler'"

Результатом исполнения данной команды будет объект Microsoft.Manage­ment.Infrastructure.CimInstance, который к ожидаемому параметром InputObject команды Restart-Service объекту типа System.ServiceProcess.ServiceController не имеет никакого отношения. Тем не менее он содержит свойство Name, значением которого является имя службы. И поэтому мы вполне можем использовать следующую команду для выполнения перезагрузки необходимой нам службы:

Get-CimInstance -ClassName
   Win32_Service -Filter "Name='Spooler'"
   | Restart-Service

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

 

Рисунок 5. Объект, содержащий в качестве значения свойства Name имя службы

Вычисляемые свойства

Если мы присмотримся к структуре возвращаемого командой Get-CimInstance объекта службы, то увидим, что он обладает свойством PathName, значением которого является путь к исполняемому файлу. Давайте предположим, что нам необходимо получить об этом файле как можно более подробную информацию.

Для этого нам потребуется команда Get-Item. Однако если мы попробуем передать по конвейеру полученный в результате выполнения команды Get-CimInstance объект команде Get-Item, то получим сообщение об ошибке, как на рисунке 6.

 

Рисунок 6. Сообщение об ошибке при выполнении команды Get-CimInstance

Почему так получилось? Если мы заглянем в файл справки команды Get-Item, то увидим, что параметров, способных принимать значения по конвейеру, у нее три: Path, LiteralPath и Credential. Причем оба варианта взаимодействия с конвейером, ByValue и ByPropertyName, поддерживает только параметр Path. Остальные — LiteralPath и Credential — поддерживают только ByPropertyName.

Попытка сопоставления параметрам поступающих по конвейеру данных начинается с варианта ByValue. Из файла справки мы знаем, что параметр Path в качестве значения принимает объект строки, однако по конвейеру поступает объект Microsoft.Management.Infrastructure.CimInstance.

Что же в подобном случае делает командная среда? Убедившись, что входящий объект не содержит таких свойств, как Path, LiteralPath или Credential, что могли бы пригодиться при сопоставлении его параметрам с использованием способа ByPropertyName, она пытается нам помочь и конвертирует поступивший по конвейеру объект в требуемый тип данных — System.String. Что из этого получается, мы можем увидеть на рисунке 7, выполнив следующую команду:

Get-CimInstance -ClassName
   Win32_Service -Filter "Name='Spooler'"
   | ForEach-Object -MemberName ToString

 

Рисунок 7. Результат работы Get-CimInstance

Как видите, это не совсем то, что мы хотели бы передать команде Get-Item в качестве значения параметра Path.

Путь к файлу представлен значением свойства PathName объекта CimInstance. Команда Get-Item для получения пути к нужному файлу использует параметр Path. Что нам нужно сделать, так это каким-либо образом передать свойство PathName под именем Path.

Вариантов здесь достаточно много: от добавления к объекту свойства с тем же значением (листинг 1) до создания уже упомянутого выше пользовательского объекта (листинг 2).

Однако более удобным в данном случае будет использование вычисляемых свойств — Calculated Properties.

Вычисляемые свойства — это метод взаимодействия с данными, используя который вы можете определять новые свойства объектов в процессе их прохождения по конвейеру. При этом вы можете задать нужное имя свойства, выражение, результат которого будет значением этого свойства, а также, при использовании команд Format-*, если потребуется, указать параметры форматирования полученного значения. Например, так, как показано в листинге 3, результат исполнения которого приведен на рисунке 8.

 

Что же касается нашего случая, то здесь потребуется команда Select-Object (листинг 4), результат исполнения которой показан на рисунке 9.

 

Если мы присмотримся к результату выполнения команды Select-Object, приведенной в листинге 5, то увидим, что он представляет собой объект с единственным свойством Path — тем, что мы определили в параметре Property (рисунок 10).

 

Кроме того, запросив значение свойства pstypenames с помощью команды, приведенной в листинге 6, мы обнаружим, что по сути это тот же пользовательский объект, разве что полученный другим способом (рисунок 11).

 

Блоки сценария

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

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

Get-CimInstance -ClassName
   Win32_Service -Filter "Name='Spooler'"
   | Get-Item -Path {$_.PathName}

Внутри блока сценария для ссылки на текущий объект конвейера мы используем переменную $_. Таким образом, мы указываем свойство PathName объекта Microsoft.Management.Infrastructure.CimInstance в качестве значения параметра Path.

Кроме того, начиная с третьей версии Windows PowerShell вместо переменной $_ можно использовать $PSItem, что никоим образом не сказывается на функциональности, однако может показаться более логичным с точки зрения наименования.

Тем не менее кажущаяся простота этого способа многих может ввести в заблуждение. Давайте рассмотрим следующий пример. Допустим, нам нужно запросить WMI-класс Win32_ComputerSystem с компьютеров Comp-1, Comp-2 и Comp-3. Предположим, что информацию об этих компьютерах мы решили получить из службы каталогов Active Directory. Мы можем воспользоваться командой, приведенной в листинге 7.

В этом случае все сработает наилучшим образом. Однако если мы, к примеру, решили запросить несколько классов WMI с локального компьютера, сохранили их имена в переменной и передали ее содержимое по конвейеру, как в листинге 8, то результатом будет сообщение об ошибке, показанное на рисунке 12. И так для каждого класса.

 

Рисунок 12. Ошибка при выполнении запроса листинга 8

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

Что сейчас попыталась сделать команда Get-CimInstance? Если мы заглянем в файл справки, то увидим, что изо всех поддерживающих получение данных по конвейеру параметров метод ByValue используют только два из них — CimSession и InputObject.

Параметр InputObject работает с объектами типа Microsoft.Management.Infrastructure.CimInstance. Мы же передаем массив строк, так что в данном случае он не используется.

Второй параметр, CimSession, поддерживает только значения типа Microsoft.Management.Infrastructure.CimSession, и, казалось бы, он тоже не должен взаимодействовать с поступающими объектами, однако, как выясняется, строки замечательно преобразуются в объекты CimSession. Проверить это мы можем при помощи команд, приведенных на рисунке 13.

 

Рисунок 13. Строки преобразуются в объекты CimSession

Таким образом, когда мы передаем массив строк, который представляет собой список классов WMI, команда Get-CimInstance указывает их в качестве значений параметра CimSession и пытается выполнить запрос компьютеров с именами Win32_ComputerSystem, Win32_OperatingSystem, Win32_BaseBoard и Win32_BIOS.

Что мы можем предпринять? Задать значение параметра CimSession явным образом. Теперь у команды Get-CimInstance не осталось параметров в используемом наборе (Parameter Set, в данном случае это ClassNameSessionSet), которым можно было бы сопоставить входящие данные. Поэтому, с одной стороны, команда не будет самостоятельно распределять поступающие по конвейеру объекты, а с другой мы по-прежнему можем к ним обращаться и использовать в качестве значений нужных нам параметров.

$Classes | Get-CimInstance -ClassName
   {$_} -CimSession localhost

Еще одним вариантом будет применение параметра ComputerName. В данном случае мы точно так же предотвращаем самостоятельное сопоставление входящих данных командой Get-CimInstance, используя на этот раз другой набор параметров — ClassNameComputerSet.

$Classes | Get-CimInstance -ClassName
   {$_} -ComputerName localhost

Переменная конвейера

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

Относящийся к набору общих параметров (Common Parameters), PipelineVariable позволяет нам указать имя переменной, в которую будут помещены результаты выполнения отдельной команды. По сути, указанная переменная будет содержать те же самые данные, что были переданы этой командой далее по конвейеру.

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

Предположим, мы хотим получить объект, содержащий свойства Name, StartMode и State объекта службы Spooler, возвращаемого командой Get-CimInstance, полный путь к исполняемому файлу этой службы, его описание, версию, а также букву диска, на котором расположен данный файл, его объем и доступное свободное пространство в гигабайтах.

Сделать это мы можем при помощи следующей команды. Стоит обратить внимание, что вертикальная черта — символ конвейера — позволяет нам продолжить команду на следующей строке без необходимости использования символа обратной кавычки, backtick, он же grave (`). То же самое справедливо и для запятой.

Кроме того, для краткости вместо полного имени параметра PipelineVariable мы будем использовать его псевдоним (alias) — ‘pv’.

При формировании результирующего объекта в листинге 9 мы будем задействовать уже знакомые нам вычисляемые свойства, однако на этот раз, опять же в целях уменьшения объема кода, вместо Name и Expression будем использовать их сокращенные варианты — n и e.

Помощник ForEach-Object

Несмотря на предоставляемую конвейером функциональность, его использование поддерживают не все команды. Возьмем, к примеру, Get-WMIObject. В ее файле справки вы не найдете параметра, который бы поддерживал получение данных подобным образом.

Кроме того, так как команда не принимает значения по конвейеру, мы не сможем воспользоваться методом указания значений параметров в виде блоков сценария с использованием переменной $_ для ссылки на текущий объект конвейера. Таким образом, результатом следующей команды будет сообщение об ошибке (рисунок 14):

'Win32_Service' | Get-WmiObject
   -Class {$_}

 

Рисунок 14. Ошибка при выполнении команды, не поддерживающей конвейер

Тем не менее это не помешает нам воспользоваться преимуществами конвейерной обработки данных. В тех случаях, когда используемые команды не поддерживают получение данных по конвейеру или же методы сопоставления значений определенным параметрам не вполне соответствуют вашим целям, вы можете воспользоваться командой ForEach-Object. Она позволяет получить данные по конвейеру и распределить значения свойств поступающих объектов по соответствующим параметрам.

Таким образом, добавив в предыдущий пример команду ForEach-Object, мы все-таки сможем получить все экземпляры класса Win32_Service, не подвергая структуру команды значительным изменениям.

'Win32_Service' | ForEach-Object
   {Get-WmiObject -Class $_}

Если же нас интересует несколько классов, мы можем указать их в виде массива, как в листинге 10.

Дополнительное преимущество этого метода заключается в том, что, в отличие от использования блоков сценария в качестве значений, здесь нам необязательно предсказывать логику сопоставления командой входящих объектов тем или иным параметрам. Так, в случае с передачей команде Get-CimInstance имен классов, как в листинге 11, использование ForEach-Object позволяет нам не указывать имя компьютера явным образом:

$Classes | ForEach-Object
   {Get-CimInstance -ClassName $_}

Кроме полного имени команды, мы можем задействовать один из ее псевдонимов: foreach или%.

Таким образом, следующие две команды равнозначны приведенной выше:

$Classes | foreach
   {Get-CimInstance -ClassName $_}
$Classes |% {Get-CimInstance
   -ClassName $_}

И в завершение, чтобы для определения того, что команда не поддерживает работу с конвейером, вам не приходилось просматривать весь файл справки, вы можете воспользоваться сценарием, приведенным в листинге 12. Он позволяет выбрать из входящего массива команды, не поддерживающие получение данных по конвейеру. В качестве примера возьмем команды модуля Microsoft.PowerShell.Management.

Листинг 1. Добавление к объекту свойства с тем же значением

Get-CimInstance -ClassName Win32_Service -Filter "Name='Spooler'" | Add-Member
   -MemberType AliasProperty -Name Path -Value PathName -PassThru | Get-Item

Листинг 2. Создание пользовательского объекта

$Cim = Get-CimInstance -ClassName Win32_Service -Filter "Name=’Spooler’"

$SpoolerObject = [PSCustomObject]@{
Name = $Cim.Name
Path = $Cim.PathName
}
$SpoolerObject | Get-Item

Листинг 3. Пример использования вычисляемых свойств

Get-Process -Name powershell | Format-Table -Property Name,Id, @{Name = ‘TimeRunning’; Expression = {(Get-Date) - $_.StartTime}; FormatString = "d\.hh\:mm\:ss"}

Листинг 4. Использование команды Select-Object

Get-CimInstance -ClassName Win32_Service -Filter "Name=’Spooler’" | Select-Object -Property @{Name = ‘Path’; Expression = {$_.PathName}} | Get-Item

Листинг 5. Пример расширенной команды Select-Object

Get-CimInstance -ClassName Win32_Service -Filter "Name='Spooler'" | Select-Object -Property @{Name = 'Path'; Expression = {$_.PathName}} | Get-Member

Листинг 6. Запрос значения свойства pstypenames

Get-CimInstance -ClassName Win32_Service -Filter "Name=’Spooler’" | Select-Object -Property @{Name = ‘Path’; Expression = {$_.PathName}} | ForEach-Object -MemberName PSTypeNames

Листинг 7. Получение информации о компьютерах из Active Directory

$Computers = Get-ADComputer -Filter {name -like 'Comp-*'}
$Computers | Get-CimInstance -ClassName Win32_ComputerSystem -ComputerName {$_.Name}

Листинг 8. Запрос нескольких классов WMI с локального компьютера

$Classes = 'Win32_ComputerSystem', 'Win32_OperatingSystem', 'Win32_BaseBoard', 'Win32_BIOS'
$Classes | Get-CimInstance -ClassName {$_}

Листинг 9. Формирование результирующего объекта

Get-CimInstance -ClassName Win32_Service -Filter "Name=’Spooler’" -pv CimInstance |
Get-Item -Path {$_.PathName} -pv Item |
Split-Path -Path {$_.FullName} -Qualifier -pv Drive |
Get-Volume -DriveLetter {$_.Substring(0,1)} |
Select-Object -Property @{n = ‘ServiceName’; e = {$CimInstance.Name}},
@{n = ‘ServiceStartMode’; e = {$CimInstance.StartMode}},
@{n = ‘ServiceState’; e = {$CimInstance.State}},
@{n = ‘FileName’; e = {$Item.FullName}},
@{n = ‘FileVersion’; e = {$Item.VersionInfo.FileVersion}},
@{n = ‘FileDescription’; e = {$Item.VersionInfo.FileDescription}},
@{n = ‘DriveLetter’; e = {$Drive}},
@{n = ‘DriveSizeGB’; e = {[math]::Truncate($_.Size/1GB)}},
@{n = ‘SizeRemainingGB’; e = {[math]::Truncate($_.SizeRemaining/1GB)}}

ServiceName : Spooler
ServiceStartMode : Auto
ServiceState : Running
FileName : C:\WINDOWS\System32\spoolsv.exe
FileVersion : 10.0.16299.15 (WinBuild.160101.0800)
FileDescription : Spooler SubSystem App
DriveLetter : C:
DriveSizeGB : 100
SizeRemainingGB : 40

Листинг 10. Указание классов в виде массива

‘Win32_Service’, ‘Win32_Process’, ‘Win32_ComputerSystem’, ‘Win32_OperatingSystem’ | ForEach-Object {Get-WmiObject -Class $_}

Листинг 11. Передача команде Get-CimInstance имен классов

$Classes = 'Win32_ComputerSystem', 'Win32_OperatingSystem', 'Win32_BaseBoard', 'Win32_BIOS'
$Classes | Get-CimInstance -ClassName {$_} -ComputerName localhost

Листинг 12. Определение того, что команда не поддерживает работу с конвейером

$Commands = Get-Command -Module 'Microsoft.PowerShell.Management'
foreach ($Command in $Commands)
{
$Command.Parameters.Values.Attributes |
Where-Object {$_.TypeId.Name -eq 'ParameterAttribute'} |
ForEach-Object {if ($_.ValueFromPipeline -or
$_.ValueFromPipelineByPropertyName){continue}}
Write-Output -InputObject $Command.Name
} 

Поделитесь материалом с коллегами и друзьями

Как работает конвейер и $_ в PowerShell? — powershell

$_ -это автоматическая переменная, доступная для использования на входе scriptblock определенного cmdlets для представления текущего элемента в конвейере.

Get-Process | Get-Member $_ не работает, потому что вы отправляете объект конвейера в Get-Member через | , но у вас тогда нет никакого способа получить доступ к внутренностям Get-Member .

Вы могли бы сделать это:

Get-Process | ForEach-Object {
    $_ | Get-Member
}

Затем вы получите вывод Get-Member для каждого элемента в коллекции объектов, выводимых Get-Process, хотя это будет избыточно, поскольку каждый будет одинаковым.

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

Get-Process | ForEach-Object {
   $_
   Start-Sleep 1
}

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

Другие места, где вы можете использовать переменную $_ , находятся в Where-Object и Select-Object . Например:

Get-Process | Where-Object { $_.name -like 'win*' }

Здесь командлет Where-Object принимает каждый элемент конвейера, и мы используем $_ для доступа к свойству name этого элемента, чтобы увидеть, похож ли он на строку win . Если это так, то он отправляется вперед (и поэтому выходит на консоль), если это не так, Where-Object отбрасывает его.

Вы можете использовать $_ в Select-Object при выполнении вычисляемых свойств. Например:

Get-Process | Select-Object name,@{N='WorkingSetGB';E={$_.WorkingSet / 1GB}}

Здесь мы используем $_ , чтобы получить свойство WorkingSet каждого элемента, а затем преобразовать его в значение GB с помощью / 1GB .

Собственные функции в конвейере PowerShell | Windows IT Pro/RE

Как было показано в одной из предыдущих моих статей, одним из основных механизмов 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, а ValueFromPipe­lineByPropertyName задает использование способа ByPropertyName. Указываются они таким образом, как показано в листинге 7.

Кроме того, поддерживается и другой, более краткий способ, представленный в листинге 8.

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

Теперь укажем, что значением параметра Name должен быть объект строки, а также добавим код для вывода функцией полученного значения (листинг 9).

Далее попробуем передать функции некоторое значение. Сделаем мы это несколькими способами, как посредством конвейера, причем в двух вариантах — ByValue и ByPropertyName, так и с использованием имени параметра (лис­тинг 10).

Как видите, все три способа работают. Теперь давайте добавим параметру Name два псевдонима, HostName и NodeName. Делается это при помощи атрибута Alias (листинг 11).

При передаче данных функции с использованием варианта ByProperty­Name вместо имени параметра 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, из которых может состоять функция, особенности использования наборов параметров при распределении поступающих по конвейеру объектов, а также способы сопоставления значений параметрам, как при совпадении типа данных входящего объекта и параметра, так и в случаях, когда для этого требуется преобразование значения к определенному типу. Я надеюсь, что эта статья поможет вам в освоении такого мощного и удобного механизма, как конвейер.

Листинг 1. Пример серии команд

Get-Process | Group-Object -Property Name | Sort-Object -Property Count -Descending | Select-Object -First 10

Листинг 2. Пример фильтра

filter NotTouchingPipelineElements
{
    "Step: $($i++; $i)"
}

‘first’, ‘second’, ‘third’ | NotTouchingPipelineElements

Step: 1
Step: 2
Step: 3

Листинг 3. Расширенный вариант фильтра из листинга 2

filter TouchingPipelineElements
{
    "`nStep: $($i++; $i)"
    "Value: $_"
}

'first', 'second', 'third' | TouchingPipelineElements

Step: 1
Value: first

Step: 2
Value: second

Step: 3
Value: third

Листинг 4. Фильтр с параметрами

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

Листинг 5. Способы сопоставления параметров

Get-Help -Name Stop-Process -Parameter InputObject

-InputObject 
    Specifies 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

Листинг 6. Пример передачи параметра по варианту 

ByPropertyName
C:\> Get-Help -Name Stop-Process -Parameter Name

-Name 
    Specifies 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

Листинг 7. Использование аргументов ValueFromPipeline и ValueFromPipelineByPropertyName

function UsingPipeline
{
    Param(
        [Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
        $Name
    )
}

Листинг 8. Укороченный вариант листинга 7

function UsingPipeline
{
    Param(
        [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)]
        $Name
    )
}

Листинг 9. Значением параметра Name будет строка

function UsingPipeline
{
    Param(
        [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [string]$Name
    )
    
    $Name
}

Листинг 10. Передача функции параметров разными вариантами

'first' | UsingPipeline
first

[PSCustomObject]@{Name = 'first'} | UsingPipeline
first

UsingPipeline -Name 'first'
first

Листинг 11. Добавление псевдонима параметру

function UsingPipeline
{
    Param(
        [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('HostName','NodeName')]
        [string]$Name
    )
    
    $Name
}

Листинг 12. Использование псевдонимов

[PSCustomObject]@{HostName = ‘first’} | UsingPipeline

first

[PSCustomObject]@{NodeName = 'first'} | UsingPipeline

first

Листинг 13. Добавление поддержки массива в качестве входных данных

function UsingPipeline
{
    Param(
        [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('HostName','NodeName')]
        [string[]]$Name
    )
    
    $Name
}

Листинг 14. Результаты обработки массива в качестве входных данных

UsingPipeline -Name 'first', 'second', 'third'
first
second
third
'first', 'second', 'third' | UsingPipeline
third
[PSCustomObject]@{Name = 'first'}, [PSCustomObject]@{Name = 'second'}, [PSCustomObject]@{Name = 'third'} | UsingPipeline
third

Листинг 15. Блок Begin выполняется до обработки первого объекта

function Structure
{
    Param()
    Begin
    {
        "Begin: $_"
    }
    Process {}
    End {}
}
'first', 'second', 'third' | Structure
Begin:

Листинг 16. Блок Begin и параметр

function Structure
{
    Param(
        [Parameter(ValueFromPipeline)]
        [string[]]$Name
    )
    Begin
    {
        "Begin"
        "`$_: $_"
        "`$Name: $Name"
    }
    Process {}
    End {}
}
‘first’, ‘second’, ‘third’ | Structure
Begin
$_:
$Name:

Листинг 17. Блок Begin и параметры, не поддерживающие конвейерное получение данных

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

Листинг 18. Примеры блока Process

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

Листинг 19. Пример блока End

function Structure
{
    Param()
    Begin
    {
        "Begin: $_"
    }
    Process
    {
        "Process: $_"
    }
    End
    {
        "End: $_"
    }
}

‘first’, ‘second’, ‘third’ | Structure

Begin:
Process: first
Process: second
Process: third
End: third

НОУ ИНТУИТ | Лекция | Конвейеризация и управление выводом команд Windows PowerShell

Аннотация: Обсуждается процесс конвейеризации объектов в PowerShell. Рассматриваются процедуры фильтрации и сортировки потока объектов. Описываются командлеты для форматирования выводимых данных и перенаправления выходных потоков команд

Конвейеризация и управление выводом команд Windows PowerShell

Ранее в
«лекции 2»
мы рассматривали понятие конвейеризации (или композиции) команд интерпретатора Cmd.exe, когда выходной поток одной команды перенаправлялся во входной поток другой, объединяя тем самым две команды вместе. Подобные конвейеры команд используются в большинстве оболочек командной строки и являются средством, позволяющим передавать информацию между разными процессами.

Механизм композиции команд представляет собой, вероятно, наиболее ценную концепцию, используемую в интерфейсах командной строки. Конвейеры не только снижают усилия, прилагаемые при вводе сложных команд, но и облегчают отслеживание потока работы в командах. Полезной чертой конвейеров является то, что они не зависят от числа передаваемых элементов, так как конвейер действует на каждый элемент отдельно. Кроме того, каждая команда в конвейере (называемая элементом конвейера) обычно передает свой вывод следующей команде в конвейере, элемент за элементом. Благодаря этому, как правило, снижается потребление ресурсов для сложных команд и возникает возможность получать выводимую информацию немедленно.

В оболочке PowerShell также очень широко используется механизм конвейеризации команд, однако здесь по конвейеру передается не поток текста, как во всех других оболочках, а объекты. При этом с элементами конвейера можно производить различные манипуляции: фильтровать объекты по определенному критерию, сортировать и группировать объекты, изменять их структуру (ниже мы подробнее рассмотрим операции фильтрации и сортировки элементов конвейера).

Конвейеризация объектов в PowerShell

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

Передача данных между командами в виде объектов имеет большое преимущество над обычным обменом информацией посредством потока текста. Ведь команда, принимающая поток текста от другой утилиты, должна его проанализировать, разобрать и выделить нужную ей информацию, а это может быть непросто, так как обычно вывод команды больше ориентирован на визуальное восприятие человеком (это естественно для интерактивного режима работы), а не на удобство последующего синтаксического разбора.

При передаче по конвейеру объектов этой проблемы не возникает, здесь нужная информация извлекается из элемента конвейера простым обращением к соответствующему свойству объекта. Однако теперь возникает новый вопрос: каким образом узнать, какие именно свойства есть у объектов, передаваемых по конвейеру? Ведь при выполнении того или иного командлета мы на экране видим только одну или несколько колонок отформатированного текста. Например, запустим командлет Get-Process, который выводит информацию о запущенных в системе процессах:

PS C:\> Get-Process

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    158      11    45644      22084   126   159.69   2072 AcroRd32
     98       5     1104        284    32     0.10    256 alg
     39       1      364        364    17     0.26   1632 ati2evxx
     57       3     1028        328    30     0.38    804 atiptaxx
    434       6     2548       3680    27    21.96    800 csrss
     64       3      812        604    29     0.22   1056 ctfmon
    364      11    14120       9544    69    11.82    456 explorer
     24       2     1532       2040    29     5.34   2532 Far

Фактически на экране мы видим только сводную информацию (результат форматирования полученных данных), а не полное представление выходного объекта. Из этой информации не понятно, сколько точно свойств имеется у объектов, генерируемых командой Get-Process, и какие имена имеют эти свойства. Например, мы хотим найти все «зависшие» процессы, которые не отвечают на запросы системы. Можно ли это сделать с помощью командлета Get-Process, какое свойство нужно проверять у выводимых объектов?

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

Просмотр структуры объектов

Для анализа структуры объекта, возвращаемого определенной командой, проще всего направить этот объект по конвейеру на командлет Get-Member (псевдоним gm ), например:

PS C:\> Get-Process | Get-Member

   TypeName: System.Diagnostics.Process

Name                    MemberType     Definition
----                    ----------     ----------
Handles                 AliasProperty  Handles = Handlecount
Name                    AliasProperty  Name = ProcessName
NPM                     AliasProperty  NPM = NonpagedSystemMemorySize
PM                      AliasProperty  PM = PagedMemorySize
VM                      AliasProperty  VM = VirtualMemorySize
WS                      AliasProperty  WS = WorkingSet
. . .
Responding              Property       System.Boolean Responding {get;}
. . .

Здесь мы видим имя .NET-класса, экземпляры которого возвращаются в ходе работы исследуемого командлета (в нашем примере это класс System.Diagnostic.Process), а также полный список элементов объекта (в частности, интересующее нас свойство Responding, определяющего «зависшие» процессы). При этом на экран выводится очень много элементов, просматривать их неудобно. Командлет Get-Member позволяет перечислить только те элементы объекта, которые являются его свойствами. Для этого используется параметр MemberType со значением Properties:

PS C:\> Get-Process | Get-Member -MemberType Property

   TypeName: System.Diagnostics.Process

Name                       MemberType Definition
----                       ---------- ----------
BasePriority               Property   System.Int32 BasePriority {get;}
Container                  Property   System.ComponentModel.IContainer...
EnableRaisingEvents        Property   System.Boolean EnableRaisingEvents...
ExitCode                   Property   System.Int32 ExitCode {get;}
ExitTime                   Property   System.DateTime ExitTime {get;}
Handle                     Property   System.IntPtr Handle {get;}
HandleCount                Property   System.Int32 HandleCount {get;}
HasExited                  Property   System.Boolean HasExited {get;}
Id                         Property   System.Int32 Id {get;}
MachineName                Property   System.String MachineName {get;}
. . . 
Responding                 Property   System.Boolean Responding {get;}
. . .

Как мы видим, процессам операционной системы соответствуют объекты, имеющие очень много свойств, на экран же при работе командлета Get-Process выводятся лишь несколько из них (способы отображения объектов различных типов задаются конфигурационными файлами в формате XML, находящимися в каталоге, где установлен файл powershell.exe).

Теперь, когда мы знаем, какие свойства имеют объекты, передаваемые по конвейеру, перейдем к рассмотрению наиболее часто используемых операций над элементами конвейера: фильтрации и сортировки.

Фильтрация объектов в конвейере

В PowerShell поддерживается возможность фильтрации объектов в конвейере, т.е. удаление из конвейера объектов, не удовлетворяющих определенному условию. Данную функциональность обеспечивает командлет Where-Object, позволяющий проверить каждый объект, находящийся в конвейере, и передать его дальше по конвейеру, только если объект удовлетворяет условиям проверки.

Например, для вывода информации о «зависших» процессах (объекты, возвращаемые командлетом Get-Process, у которых свойство Responding равно False ) можно использовать следующий конвейер:

Get-Process | Where-Object {-not $_.Responding}

Другой пример – оставим в конвейере только те процессы, у которых значение идентификатора (свойство Id ) больше 1000:

Get-Process | Where-Object {$_.Id -gt 1000}

В блоках сценариев командлета Where-Object для обращения к текущему объекту конвейера и извлечения нужных свойств этого объекта используется специальная переменная $_, которая создается оболочкой PowerShell автоматически. Данная переменная используется и в других командлетах, производящих обработку элементов конвейера.

Условие проверки в Where-Object задается в виде блока сценария – одной или нескольких команд PowerShell, заключенных в фигурные скобки {}. Результатом выполнения данного блока сценария должно быть значение логического типа: True (истина) или False (ложь). Как можно понять из примеров, в блоке сценария используются специальные операторы сравнения.

Замечание

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

Основные операторы сравнения приведены в табл. 17.1.

Таблица
17.1.
Операторы сравнения в PowerShell
Оператор Значение Пример (возвращается значение True)
-eq равно 10 -eq 10
-ne не равно 9 -ne 10
-lt меньше 3 -lt 4
-le меньше или равно 3 –le 4
-gt больше 4 -gt 3
-ge больше или равно 4 -ge 3
-like сравнение на совпадение с учетом подстановочного знака в тексте «file.doc» –like «f*.doc»
-notlike сравнение на несовпадение с учетом подстановочного знака в тексте «file.doc» –notlike «f*.rtf»
-contains содержит 1,2,3 –contains 1
-notcontains не содержит 1,2,3 –notcontains 4

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

Таблица
17.2.
Логически операторы в PowerShell
Оператор Значение Пример (возвращается значение True)
-and логическое И (10 -eq 10) –and (1 –eq 1)
-or логическое ИЛИ (9 -ne 10) –or (3 –eq 4)
-not логическое НЕ -not (3 –gt 4)
! логическое НЕ !(3 -gt 4)
Сортировка объектов

Сортировка элементов конвейера – еще одна операция, которая часто применяется при конвейерной обработке объектов. Данную операцию осуществляет командлет Sort-Object: ему передаются имена свойств, по которым нужно произвести сортировку, а он возвращает данные, упорядоченные по значениям этих свойств.

Например, для вывода списка запущенных в системе процессов, упорядоченного по затраченному процессорному времени (свойство cpu ), можно воспользоваться следующим конвейером:

PS C:\> Get-Process | Sort-Object cpu

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
      0       0        0         16     0               0 Idle
     36       2      728         32    23     0.05   1792 klswd
     98       5     1104        764    32     0.09    252 alg
     21       1      164         60     4     0.09    748 smss
     30       2      672        724    27     0.11   1700 WinCinemaMgr
     39       1      364        464    17     0.12   1644 ati2evxx
    163       6     1536       1404    35     0.12   1612 svchost
     55       3     1088        852    27     0.14   1220 svchost
     22       2      504        712    23     0.14    772 winampa
    120       4     2364       1228    35     0.26   1876 svchost
    170       5     4516        796    44     0.27   1260 GoogleToolbarNotifier
    193       5     2916       1488    59     0.29   1040 svchost
     64       3      812       1080    29     0.30   1252 ctfmon
    140       5     3208       1220    41     0.32   1524 spoolsv
    281      14     1764       1688    37     0.34   1120 svchost
     57       3     1028        996    30     0.39    932 atiptaxx
    503      52     7296       3596    51     2.47    836 winlogon
    259       6     1432       1340    19     2.48    880 services
    341       8     3572       1856    40     5.36    892 lsass
    240     158    29536      10388   175     5.58   1780 outpost
    149       4     2940       1108    41     9.29   1248 kav
    398       5    36140      26408   137     9.97   1984 powershell
    375      12    15020      10456    75    14.03   1116 explorer
    376       0        0         36     2    14.97      4 System
    409       6     2500       3192    26    20.10    812 csrss
   1513      54    13528       9800    95    25.78   1156 svchost
    717      75    37432        704   145    56.97   1748 kavsvc
    152       4     2372       2716    38    58.09   2028 wmiprvse
    307      13    10952      27080   173  9128.03   1200 WINWORD

17.1.

Для сортировки в обратном порядке используется параметр Descending:

PS C:\> Get-Process | Sort-Object cpu -Descending

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    307      13    10956      27040   173  9152.23   1200 WINWORD
    152       4     2372       2716    38    59.19   2028 wmiprvse
    717      75    37432       1220   145    57.15   1748 kavsvc
   1524      54    13528       9800    95    26.13   1156 svchost
    410       6     2508       3224    26    20.62    812 csrss
    376       0        0         36     2    15.11      4 System
    377      13    15020      10464    75    14.20   1116 explorer
    374       5    36484      26828   137    10.53   1984 powershell
    149       4     2940       1108    41     9.34   1248 kav
    240     158    29536      10388   175     5.61   1780 outpost
    344       8     3572       1856    40     5.40    892 lsass
    512      53     7324       3608    51     2.51    836 winlogon
    259       6     1432       1340    19     2.48    880 services
     57       3     1028        996    30     0.39    932 atiptaxx
    281      14     1764       1688    37     0.34   1120 svchost
    140       5     3208       1220    41     0.32   1524 spoolsv
     64       3      812       1080    29     0.30   1252 ctfmon
    193       5     2916       1488    59     0.29   1040 svchost
    170       5     4516        796    44     0.27   1260 GoogleToolbarNotifier
    120       4     2364       1228    35     0.26   1876 svchost
     22       2      504        712    23     0.15    772 winampa
     55       3     1088        852    27     0.14   1220 svchost
     39       1      364        464    17     0.13   1644 ati2evxx
    163       6     1536       1404    35     0.12   1612 svchost
     30       2      672        724    27     0.11   1700 WinCinemaMgr
     21       1      164         60     4     0.09    748 smss
     98       5     1104        764    32     0.09    252 alg
     36       2      728         32    23     0.05   1792 klswd
      0       0        0         16     0               0 Idle

17.2.

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

Get-Process | Where-Object {$_.Id -gt 1000} | Sort-Object cpu -Descending

Конвейеры в Windows PowerShell | вебисторий

Как мы уже говорили ранее, Windows PowerShell работает с объектами, которые являются совокупностью свойств и методов. По сути, работая с файлами, параметрами реестра или чем-то еще, PowerShell интерпретирует их в понятные для себя объекты.

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

Строго говоря, конвейер это не изобретение PowerShell. Конвейеризация есть и классической командной строке Windows. Правда там это объединение команд, при котором идет направление выходного потока текста из одной команды во входной команд другой.

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

Ниже пошагово составим конвейер, который выводил бы на экран только конкретные параметры объекта PowerShell.

Как мы знаем, информацию о запущенных в системе процессах, можно получить командой

Get-Process

ниже пример её выполнения.

Каждый процесс это по сути своей объект в Windows PowerShell, который обладает определенными параметрами (Handles, NPM(K), PM(K) и т.д.).

По умолчанию в окне консоли выводится информацию не обо всех параметрах объекта. Для того, чтобы просмотреть все параметры нашего объекта (в данном случае запущенный процесс), составим небольшой конвейер:

Get-Process | Get-Member

В этом конвейере командлет Get-Process вывел информацию о запущенных процессах, которые в PowerShell являются объектами и передал эти объекты командлету Get-Member, который вывел информацию об их параметрах.

В чем практический смысл этого конвейера? Допустим, Вы хотите отслеживать «зависшие» процессы или их приоритет. По умолчанию PowerShell такие параметры не выводит. Для изменения параметров вывода нужно править определенные конфигурационные файлы. Но для начала нам нужно было узнать имена искомых параметров, что мы и сделали этим конвейером. Так, в частности, «завис» процесс или нет, показывает параметр Responding.

Что касается правки конфигурационных файлов, то к этой теме надо подходить с особенной осторожностью, и лучше было бы осветить её в отдельной статье. Для общего понимания скажу, что информация об отображении параметров объектов в PowerShell хранится в специальных конфигурационных файлах pslxml, находящихся в каталоге с PowerShell. Править эти файлы крайне не рекомендуется. Желательно создавать собственные конфигурационные файлы и включать их в состав автоматически загружаемых файлов при старте Windows PowerShell.

Обо всём этом поговорим подробнее в следующий раз.

советов по реализации поддержки трубопроводов | Изучите Powershell

Во время первого события Scripting Games я увидел использование (или неправильное использование) реализации поддержки конвейера для параметра в функции или скрипте. Хотя большинство людей делало это правильно, я действительно видел, как приличное количество людей делало некоторые вещи, которые вообще никогда не сработали бы, если бы кто-то попытался передать данные в их функцию. Я хочу прояснить / расширить некоторые вещи, о которых я говорил в предыдущей статье, которые следует сделать, и почему некоторые из используемых методов не будут работать так, как вы думаете.

Все мы знаем, что возможность передавать объекты (не текст!) Через конвейер с помощью PowerShell — это просто потрясающе и очень эффективно. Для этого мы получаем вывод из другого командлета и затем передаем его в другую команду, что позволяет нам легко и без усилий объединять команды в цепочку. Это также приведет к ограничению объема памяти, выделяемой (в большинстве случаев), которую текущий сеанс использует для команд.

Начало работы…

Хотите узнать больше о трубопроводе? Тогда поступайте правильно и исследуйте замечательную справочную систему PowerShell с помощью следующей команды:

Возможно, вы не знаете, как правильно реализовать это, чтобы получить преимущества конвейера в ваших функциях.Под этим я говорю о блоках Begin, Process и End в коде. Сначала я покажу некоторые ошибки, которые можно было бы сделать с помощью этой реализации, и способы их преодоления.

Во-первых, как разрешить моему параметру принимать ввод конвейера? Указав один из следующих атрибутов параметра:

  1. ValueFromPipeline
    1. Принимает значения того же типа, который ожидается параметром, или значения, которые могут быть преобразованы в тип, ожидаемый параметром.
  2. ValueFromPipelineByPropertyName
    1. Принимает значения того же типа, которого ожидает параметр, но также должно иметь то же имя, что и параметр, принимающий ввод конвейера.

Теперь, разобравшись с этим, давайте посмотрим, что вы можете ожидать увидеть:

 Param (
        [параметр (ValueFromPipeline = $ True)]
        [строка []] $ Computername
    ) 

Параметр Computername позволяет конвейерную поддержку по значению чего-либо, что является строкой или набором строк.Если бы мы принимали конвейер с тем же именем, что и Computername (или любые определенные псевдонимы с атрибутом [Alias ​​()], мы бы использовали следующее:

 Param (
        [параметр (ValueFromPipelineByPropertyName)]
        [Псевдоним ('IPAddress', '__ Server', 'CN')]
        [строка []] $ Computername
    ) 

Это позволяет мне делать что-то вроде конвейера вывода WMI-запроса с помощью Get-WMIObject в функцию, и он захватывает свойство __Server объекта и использует его в конвейере функции.Довольно крутая штука! Если вы используете атрибут * ByPropertyName, убедитесь, что в объекте действительно есть свойство, которое либо поддерживает его, либо вы используете атрибут Alias, который имеет свойство, которое будет отображаться на то, что есть у входящего объекта.

Теперь перейдем к основному пункту этой статьи, который настраивает внутренности функции для правильной обработки.

Начало, обработка и конец без поддержки конвейера

Во-первых, если вы не принимаете ввод конвейера, вам действительно не нужно использовать Begin, Process и End, потому что, откровенно говоря, он ничего не делает для вас, кроме как просто занимает место в вашем коде.Я знаю, что люди могут делать это как способ организации своего кода, но есть способ получше, который я вам сейчас покажу.

 Функция Get-Something {
    [cmdletbinding ()]
    Param (
        [параметр ()]
        [строка []] $ Computername
    )
    Начать {
        Write-Verbose «Инициализировать материал в блоке Begin»
    }
    Процесс {
        Write-Verbose «Материал в блоке процесса для выполнения»
        ForEach ($ Computer в $ Computername) {
            $ Компьютер
        }
    }

    Конец {
        Write-Verbose «Заключительная работа в конце блока»
    }
} 

Это действительно ложное ощущение работы блоков, поскольку они просто работают в порядке, указанном в коде.Вместо этого воспользуйтесь преимуществами PowerShell V3 ISE и ее способностью использовать сворачивание кода с областями для соответствующей организации кода.

 Функция Get-Something {
    [cmdletbinding ()]
    Param (
        [параметр ()]
        [строка []] $ Computername
    )
    #region Код инициализации
    Write-Verbose «Инициализировать материал в блоке Begin»
    #endregion Код инициализации

    #region Данные процесса
    Write-Verbose «Материал в блоке процесса для выполнения»
    ForEach ($ Computer в $ Computername) {
        $ Компьютер
    }
    #endregion Данные процесса

    #region Завершить все
    Write-Verbose «Заключительная работа в конце блока»
    #endregion Завершить все
} 

Тот же вывод, но без блоков Begin, Process и End.Я повторю это еще раз, если вы не разрешаете ввод в конвейер, просто используйте теги # region / # endregion для организации кода (вы также должны делать это независимо от ввода конвейера или нет).

Опора трубопровода без опоры технологического блока

Хорошо, а что произойдет, если мы укажем параметр, который поддерживает конвейер, но не имеет блока процесса? Это было что-то обычное, что я видел во время события 1, и покажу вам, что происходит при попытке запустить команду, настроенную таким образом.

 Функция Get-Something {
    [cmdletbinding ()]
    Param (
        [параметр (ValueFromPipeline = $ True)]
        [строка []] $ Computername
    )
    Write-Verbose «Инициализировать материал в блоке Begin»

    Write-Verbose «Материал в блоке процесса для выполнения»
    $ Computername

    Write-Verbose «Заключительная работа в конце блока»
} 

Как вы думаете, что произойдет, если я запущу это с конвейером ввода? Он все обработает? Он ничего не будет обрабатывать? Давай выясним!

Если вы думали, что будет отображаться только последний элемент в конвейере, то вы победитель! Что происходит, так это то, что без блока Process поведение похоже на то, что мы ожидаем от блока End.

Как это сделать…

Итак, как правильно это сделать, позвольте мне показать вам это на следующем примере.

 Функция Get-Something {
    [cmdletbinding ()]
    Param (
        [параметр (ValueFromPipeline = $ True)]
        [строка []] $ Computername
    )
    Начать {
        Write-Verbose «Инициализировать материал в блоке Begin»
    }

    Процесс {
        Write-Verbose «Материал в блоке процесса для выполнения»
        $ Computername
    }

    Конец {
        Write-Verbose «Заключительная работа в конце блока»
    }
} 

Теперь работает как чемпион.Но взгляните на кое-что здесь. Оператор Write-Verbose выполняется для каждого элемента, обрабатываемого в конвейере. Что это значит? Что ж, это означает, что вы должны быть осторожны с тем, что помещается в блок Process, поскольку он будет запускаться каждый раз для каждого элемента, проходящего через конвейер. Другими словами, не пытайтесь создать тот же файл для записи с выводом или создать основной массив, который будет хранить в нем данные, например, в этом примере:

 Функция Get-Something {
    [cmdletbinding ()]
    Param (
        [параметр (ValueFromPipeline = $ True)]
        [строка []] $ Computername
    )
    Начать {
        Write-Verbose «Инициализировать материал в блоке Begin»
    }

    Процесс {
        $ report = @ ()
        Write-Verbose «Материал в блоке процесса для выполнения»
        $ report + = $ Computername
    }

    Конец {
        Write-Verbose «Заключительная работа в конце блока»
        $ Отчет
    }
} 

Все собранные данные были перезаписаны каждым элементом.Я также видел нечто подобное в нескольких материалах во время События 1. Будьте осторожны, чтобы не совершить эту ошибку!

Нужны ли мне все эти блоки Begin, Process и End?

Означает ли это, что со всей этой информацией вам нужно указать только блок процесса в своей функции? Ну да и нет. Да, если все, что у вас есть, это конвейерный материал для обработки и вам не нужно ничего инициализировать в начале. Если у вас есть что-то, что нужно раскрутить, то добавьте блок Begin для обработки, иначе ваша функция выйдет из строя при запуске следующим образом:

 Функция Get-Something {
    [cmdletbinding ()]
    Param (
        [параметр (ValueFromPipeline = $ True)]
        [строка []] $ Computername
    )
    Write-Verbose «Инициализировать материал в блоке Begin»
    Процесс {
        Write-Verbose «Материал в блоке процесса для выполнения»
        $ Computername
    }
} 

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

Кажется, все хорошо, пока мы не перейдем к части «Процесс». Вместо того, чтобы считаться блоком процесса, он ошибочно интерпретируется как Get-Process, который, очевидно, не работает. Дело в том, чтобы сохранить все, что в блоках Begin, Process и End, если они вам нужны.

 Функция Get-Something {
    [cmdletbinding ()]
    Param (
        [параметр (ValueFromPipeline = $ True)]
        [строка []] $ Computername
    )
    Начать {
        Write-Verbose «Инициализировать материал в блоке Begin»
    }
    Процесс {
        Write-Verbose «Материал в блоке процесса для выполнения»
        $ Computername
    }
} 

Намного лучше!

Несколько параметров, которые принимают ввод конвейера

Теперь кое-что другое.Я видел по крайней мере одну отправку, в которой было несколько параметров с вводом конвейера, и задавался вопросом, как это будет работать (оказывается, не так хорошо!). См. Этот пример:

 Функция Get-Something {
    [cmdletbinding ()]
    Param (
        [параметр (ValueFromPipeline = $ True)]
        [строка []] $ Имя,
        [параметр (ValueFromPipeline = $ True)]
        [строка []] $ Каталог
    )
    Начать {
        Write-Verbose «Инициализировать материал в блоке Begin»
    }

    Процесс {
        Подробная запись «Блок процесса»
        Write-Host "Имя: $ Name"
        Write-Host "Каталог: $ Directory"
    }

    Конец {
        Write-Verbose «Заключительная работа в конце блока»
        $ Отчет
    }
} 

Вместо обычных чисел в конвейере я собираюсь использовать Get-ChildItem и передать его в свою функцию, чтобы посмотреть, что произойдет.

Странно, не правда ли? Он будет обрабатывать одно и то же значение для каждого параметра только потому, что он принял ввод конвейера. Как решить эту проблему? Вместо этого используйте атрибут PipelineValueByPropertyName.

 Функция Get-Something {
    [cmdletbinding ()]
    Param (
        [параметр (ValueFromPipelineByPropertyName = $ True)]
        [строка []] $ Имя,
        [параметр (ValueFromPipelineByPropertyName = $ True)]
        [строка []] $ Каталог
    )
    Начать {
        Write-Verbose «Инициализировать материал в блоке Begin»
    }

    Процесс {
        Подробная запись «Блок процесса»
        Write-Host "Имя: $ Name"
        Write-Host "Каталог: $ Directory"
    }

    Конец {
        Write-Verbose «Заключительная работа в конце блока»
        $ Отчет
    }
} 

Теперь мы можем извлекать два отдельных значения с двумя параметрами, которые принимают ввод конвейера.Другой вариант — использовать ParameterSets, но это будет означать, что у вас будет только один параметр или другой, чтобы принимать ввод конвейера, и не будет вывода, который у меня есть выше, позволяющего использовать несколько параметров для приема ввода И использовать его в функция рядом.

И последнее: будьте осторожны при использовании как ValueFromPipeline, так и… ByPropertyName с несколькими параметрами, так как это может вызвать некоторую сумасшествие на выходе.

 Функция Get-Something {
    [cmdletbinding ()]
    Param (
        [параметр (ValueFromPipeline = $ True, ValueFromPipelineByPropertyName = $ True)]
        [строка []] $ Имя,
        [параметр (ValueFromPipeline = $ True, ValueFromPipelineByPropertyName = $ True)]
        [строка []] $ Каталог
    )
    Начать {
        Write-Verbose «Инициализировать материал в блоке Begin»
    }

    Процесс {
        Подробная запись «Блок процесса»
        Write-Host "Имя: $ Name"
        Write-Host "Каталог: $ Directory"
    }

    Конец {
        Write-Verbose «Заключительная работа в конце блока»
    }
} 

Фактически, это полностью сбивает с толку параметр Directory и ни к чему не привязывается.Это связано с порядком привязки при использовании обоих этих атрибутов.

Порядок привязки параметров из трубопровода

  1. Привязать параметр по значению с тем же типом (без принуждения)
  2. Привязать параметр по имени свойства к тому же типу (без принуждения)
  3. Привязать параметр по значению с преобразованием типа (принуждение)
  4. Привязать параметр по PropertyName с преобразованием типа (Coercion)

Вы можете использовать Trace-Command, чтобы углубиться в это и действительно увидеть, что происходит.Работа с Trace-Command может быть сложной, и чтение всего вывода, безусловно, может быть утомительным, поэтому используйте на свое усмотрение!

Это небольшой бонусный контент по работе с Trace-Command и просмотру того, где происходит привязка параметров, а также когда происходит принуждение и отсутствие принуждения. Я расскажу о 6 однострочниках, чтобы выделить конкретные элементы с привязкой параметров.

Базовая линия, которую я буду использовать, — это метка времени, которая представляет собой строку, а затем другой объект с типом [datetime], который будет передан в 5 функций для отображения каждого метода привязки.

 # String time
$ nonType = New-Object PSObject -prop @ {Datetime = "17:00"}
# [datetime] тип
$ Type = New-Object PSObject -prop @ {Datetime = [datetime] "17:00"} 

Рассмотрение того, как привязка параметров обрабатывает различные типы

Здесь мы рассмотрим простую функцию, которая принимает конвейерный ввод от PropertyName для обработки входящих данных.

 Функция Get-Something_PropName {
    [cmdletbinding ()]
    Param (
        [параметр (ValueFromPipelineByPropertyName = $ True)]
        [datetime []] $ Datetime
    )
    Обработка {$ Datetime}
} 

Сначала запустим мою переменную с уже определенным типом [datetime] и посмотрим, где происходит привязка параметра:

 Привязка параметров Trace-Command {$ Type | get-Something_PropName} -PSHost 

Вы можете видеть, что он начался со строки 5, сначала проверив, может ли он передать NO COERCION с атрибутом ByPropertyName, проверив, что это тип [datetime] с результатом SUCCESSFUL .

Далее: свойство без типа для параметра datetime.

 Привязка параметров команды трассировки {$ nonType | get-Something_PropName} -PSHost 

Помните, где НЕТ ПРИНУЖДЕНИЯ сработало при последнем запуске, потому что свойство было того же типа, что и требование параметра? Что ж, с моим строковым значением «5:00 ​​PM» это не так хорошо. Вы можете увидеть, где это не проходит, с SKIPPED . Следующим шагом является попытка привести входные данные ( COERCION ) к типу [datetime], чтобы он мог соответствовать тому, что требует параметр $ DateTime.Это делается с помощью [System.Management.Automation.ArgumentTypeConverterAttribute], и ​​в данном случае это SUCCESSFUL .

В качестве примечания, я буду использовать переменную $ nonType с этого момента, чтобы каждый раз показывать, как она терпит неудачу при попытке NO COERCION перед попыткой COERCION.

Работа с атрибутом [Псевдоним ()]

Написание расширенных функций означает, что поддержка конвейерной обработки означает также возможное использование атрибута параметра [Alias ​​()] для обработки других свойств, которых этот параметр не имеет.Это более важно при работе с ByPropertyName.

 Функция Get-Something_PropName_NoAlias ​​{
    [cmdletbinding ()]
    Param (
        [параметр (ValueFromPipelineByPropertyName = $ True)]
        [datetime []] $ Дата
    )
    Обработка {$ Date}
} 
 Привязка параметров команды трассировки {$ nonType | get-Something_PropName_NoAlias} -PSHost 

Что ж, это конечно интересно. Если вы посмотрите на строку 4 здесь, она показывает аргумент System.Management.Automation.PSCustomObject, который не так уж и полезен. Это связано с тем, что передаваемое свойство — DateTime, а параметр этой функции — Date. PropertyByName полностью не работает, потому что не имеет представления о входящих данных. Так будет ли работать ByValue? Давай выясним.

 Привязка параметров команды трассировки {$ nonType | get-Something_Value_NoAlias} -PSHost 

На этот раз немного лучше, но все же неудача. Поскольку это ByValue, параметр не заботится о том, какое имя передается объекту.Он видит входные данные как хэш-таблицу с доступными для просмотра данными, но все равно не работает, потому что это не тип [datetime], и ​​он также не может быть преобразован в этот тип. На всякий случай давайте передадим в него одно целое число и посмотрим, как это работает.

 Привязка параметров команды трассировки {1 | get-Something_Value_NoAlias} -PSHost 

Очевидно, что он никогда не будет иметь тип [datetime], но он был легко преобразован в объект типа [datetime], поэтому он мог связываться с параметром даже без атрибута alias.

Хорошо, теперь мы собираемся добавить атрибут Alias ​​для DateTime для обработки входящего объекта.

 Функция Get-Something_PropName_Alias ​​{
    [cmdletbinding ()]
    Param (
        [параметр (ValueFromPipelineByPropertyName = $ True)]
        [Псевдоним ('DateTime')]
        [datetime []] $ Дата
    )
    Обработка {$ Date}
} 
 Привязка параметров команды трассировки {$ nonType | get-Something_PropName_Alias} -PSHost 

Как и ожидалось с вводом, отличным от типа, первая проверка не выполняется, а затем, благодаря псевдониму, который обрабатывает параметр DateTime, успешно выполняется проверка COERCION.Что, по вашему мнению, произойдет на этот раз, вместо того, чтобы показывать, что произойдет с попыткой ByValue? ПОДСКАЗКА: История повторится.

Работа с ByPropertyName и ByValue и псевдонимами

До сих пор я работал либо с ByValue, либо с ByPropertyName, но на самом деле никогда не объединял их в функцию. Это изменится в следующем примере. Здесь я настрою оба параметра, а также установлю псевдоним, чтобы показать, что происходит с вводом без типа.

 Функция Get-Something_PropName_Value_Alias ​​{
    [cmdletbinding ()]
    Param (
        [параметр (ValueFromPipeline = $ True, ValueFromPipelineByPropertyName = $ True)]
        [Псевдоним ("Datetime")]
        [datetime] $ Date
    )
    Обработка {$ Date}
} 

Это прекрасный пример показанного мной списка того, как параметр пытается привязать входящие данные. Сначала он пытается использовать ByValue и ByPropertyName с NO COERCION (соответствует типу объекта параметру), а затем переходит к преобразованию типа ( COERCION ) с ByPropertyName, прежде чем, наконец, успешно выполнить преобразование типа ByValue.

Выполнение того, что я сделал ранее, добавив 1 вместо $ nonType, показывает другой результат при успешном выполнении ValueFromPipeline WITH COERCION.

Если в вашей функции используются параметры без псевдонимов, также могут возникнуть проблемы. Если вы передаете в свою функцию объект, имя свойства которого не совпадает с именем вашего параметра, то он завершится ошибкой независимо от того, как установлены атрибуты конвейера. Единственный способ добиться успеха — передать один объект (vs.объект, имеющий несколько свойств), который будет работать для ByValue и относится к тому же типу или может быть преобразован в требуемый тип.

На этом завершается статья о реализации поддержки конвейера, а также об использовании Trace-Command для отладки привязки параметров. Надеюсь, это предоставило вам достаточно информации, чтобы лучше подготовиться к реализации поддержки конвейера, а также к устранению неполадок в случае сбоя.

Нравится:

Нравится Загрузка …

Связанные

.Конвейер Windows PowerShell

| или ¦ символ вертикальной черты

PowerShell Scripting — Pipeline Symbol (|) или (¦)

Pipelining можно почти описать как фирменную мелодию PowerShell. Windows PowerShell рекомендует объединить два оператора, чтобы выходные данные первого предложения стали входными данными второго предложения.

Конвейер Windows PowerShell (|) Темы

Конвейер (|) или (¦) — Возможная путаница при отображении

При вводе в блокноте символ конвейера выглядит как сплошная вертикальная черта |, но при вводе в Windows PowerShell PS> подскажите, похоже ¦.На моей клавиатуре соответствующая клавиша находится рядом с Z; однако я видел клавиатуры, на которых клавиша находится рядом с цифрой 1 в верхнем ряду. После того, как вы нажмете правильную клавишу, вы получите такой символ трубы или полосы: |.

Майкл Бэббит пишет, что на его американской клавиатуре трубка находится над \ (обратной косой чертой) и рядом с правой клавишей «Shift».

Для ясности, этот символ вертикальной черты (|) соответствует ASCII 124, а не ASCI 0166. Протестируйте, удерживая клавишу Alt, затем введите число 124 на цифровой клавиатуре и, наконец, отпустите клавишу Alt. получить |,

Примеры конвейеров

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

Пример 1: Процессы диспетчера задач

Проблема: Get-Process производит слишком много вывода.
Решение требует фильтра «где».

# Простой пример конвейера PowerShell
Get-Process | Где-Объект {$ _.handlecount -gt 100}

Пример 2: Слишком много!

Проблема: Get-Process производит слишком много вывода.
Решение вызывает «Еще», чтобы вывод приостанавливался между заполнением экрана.

# Простой пример конвейера PowerShell
Clear-Host
Get-Process | подробнее

Пример 3: Два канала PowerShell

Проблема: нам нужно управлять свойствами
Решение: второй канал, затем управлять отображением с помощью Format-Table

Get-Process `
| Где-Объект {$ _.компания -Не похож на «* Microsoft *»} `
| Format-Table ProcessName, Company -auto

Примечание: `(обратный апостроф) означает продолжение на следующей строке. Я решил использовать три линии, чтобы выделить трубы.

Guy рекомендует: Бесплатная пробная версия Network Performance Monitor (NPM) v11.5

SolarWinds Network Performance Monitor поможет вам узнать, что происходит в вашей сети. Эта утилита также поможет вам в устранении неполадок; на приборной панели будет указано, является ли основная причина неисправной ссылкой, неисправным оборудованием или перегрузкой ресурсов.

Что мне больше всего нравится, так это то, как NPM предлагает решения сетевых проблем. Он также имеет возможность отслеживать состояние отдельных виртуальных машин VMware. Если вы заинтересованы в устранении неполадок и создании сетевых карт, я рекомендую вам попробовать NPM прямо сейчас.

Загрузите бесплатную пробную версию монитора производительности сети Solarwinds.

$ _ Заполнитель Значение в текущем конвейере

$ _ — это специальный заполнитель PowerShell, который ссылается на свойство в текущем конвейере.В приведенном выше примере текущим свойством является .handlecount, следовательно, $ _. Handlecount. Я уверен, что $ _ — это функция PowerShell, которую вы будете использовать во многих сценариях, поэтому стоит запомнить ритм команды: свойство
доллар / подчеркивание / точка. Например, $ _. Имя. Чтобы поместить это в контекст

# PowerShell $ _ Пример заполнителя
Get-Help * | Где {$ _. Name -Match «get»} | Имя FT, синопсис -auto

Другой пример PowerShell $ _

# PowerShell $ _ Пример заполнителя
Get-Help * | Где {$ _.синопсис -Соответствие «объекту»} | ft name, synopsis ¦ auto

Идея состоит в том, чтобы перечислить все элементы PowerShell, в синопсисе которых есть слово «Object». Проследите, как пример построен из трех предложений, разделенных двумя | трубы.

Важность конвейера PowerShell

Если это помогает оптимизировать вашу задачу, у вас может быть более одного соединения для каждого оператора, таким образом, выходные данные второго предложения становятся входными данными третьего элемента. Чтобы разделить эти отдельные предложения, Microsoft выбрала вертикальную черту, иногда называемую клавишей «|».

Чтобы дать вам физическую аналогию, представьте себе нефтепровод с множеством цилиндров, соединенных вместе с полыми кольцевыми уплотнениями. Или еще лучше — конвейер по производству бутылки пива!

Одним из распространенных способов использования конвейерной обработки является предложение «Где», например:
Get-Eventlog system | где {$ _. eventId -eq 17}. Между прочим, эта команда обнаруживает активность агента обновления Windows.

Я также использую пайп для исследования, моя техника заключается в размещении символа «|» между объектом, который я исследую, и Get-Member.Например, Get-Service | Get-Member.

Краткое описание символа конвейера | Подпись Windows PowerShell

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

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

Если вам нравится эта страница, поделитесь ею со своими друзьями


См. Другие руководства по Microsoft PowerShell

• Учебники по PowerShell • Методы • Командлеты • PS Snapin • Profile.ps1 • Exchange 2007

• Режим команд и выражений • Конвейер PowerShell (|) • PowerShell «где» • PowerShell «Сортировка»

• Модули Windows PowerShell • Модуль импорта • Каталог модулей PowerShell

Если вы видите ошибку любого рода, дайте мне знать.Пожалуйста, сообщайте о любых фактических ошибках, грамматических ошибках или неработающих ссылках.

.

Автостопом по конвейеру модулей PowerShell

Аннотация

В следующей статье освещаются концепции высокого и среднего уровня по созданию простого конвейера выпуска для модулей PowerShell.
Основное внимание будет уделено файловой структуре, методам тестирования, запуску задач и переносимости между системами CI / CD .
Дополнительные темы включают сгенерированные отчеты, шаблоны проектирования для согласованности кода и реализацию Jenkins CI .Дополнительный проект: Xainey / PSHitchhiker доступен на Github для анализа вместе с проектом.



Введение

PowerShell оказался удивительно интересным и простым для изучения языком.
При этом он также сильно изменился по сравнению с более ранними версиями.
Когда я впервые начал изучать язык, я последовал советам и примерам из самых ранних публикаций на Hey, Scripting Guy! , StackOverflow и другие платы TechNet .Как новооткрытый шеф-повар на кухне PowerShell, я готовил спагетти-код целыми грузовиками.
Как только ремонтопригодность превратилась в кошмар, я начал создавать повторно используемые модули, ресурсы DSC и скрипты небольшого размера.
Сообщество DSC научило меня тонне тестов и руководств по стилю.
Вначале я подумал, что это отличная идея — собрать 50 строк кода, заполненных псевдонимами. Играть: CodeGolfClap.mp4

После нескольких месяцев написания сценариев пора приступить к созданию модулей.Как трудно это может быть?
На какое-то время я потерялся в деталях.
Вам понадобится этот устрашающий манифест модуля и полдюжины других

.

Основы PowerShell: $ _. В этой переменной конвейера

Введение в переменную подчеркивания доллара PowerShell

Конструкции, такие как… | Где { $ _. name -Match «win»} невероятно полезны в PowerShell. Такие определения, как «$ _ означает в этом конвейере», немного скучны. Лучший способ понять $ _ — изучить примеры.

Примеры переменной $ _ в PowerShell

Переменная $ _ в PowerShell

Первое, что нужно запомнить, — это то, что $ _ — это переменная или заполнитель.

 
# PowerShell $ _ Пример переменной
Get-Service | Где-Объект { $ _  .name -Match "win"}
  

Здесь мы хотим сказать
Получите услуги, имя которых совпадает с «Win». Не показалось ли повторение «службы» слишком многословным? Что ж, написание сценария прекрасно избавляет от посторонних слов?

В двух словах $ _ спасает нас от повторения Get-Service или чего-то еще, что может быть слева от… | Где {$ _

Значение точки в $ _ в PowerShell.

Вот аналогичный пример подчеркивания в долларах, но с .DisplayName вместо .Name. Я хочу показать, как команда .dot представляет свойство.

Если вы используете графический интерфейс PowerShell ISE, то как только вы введете точку (.), Вы получите раскрывающийся список свойств.

 
# PowerShell $ _ Пример переменной
Get-Service | Где { $ _  .DisplayName -Match "win"}
  

Задача: Узнайте больше о недвижимости с Get-Service | Get-Member.

$ _ Пример фильтрации WmiObjects

Ключом к запоминанию синтаксиса является разбивка конструкции на: $ dollar / _underscore / dot.property. Самый распространенный пример: $ _. Name.

 
# Сценарий PowerShell для поиска сетевых объектов WMI
Get-WmiObject -List | Where-Object {$ _. Name -Match 'Network'}
  

Примечание 1: Реальной задачей является поиск объектов WMI сетевого типа. Без предложения where это было бы похоже на поиск иголки в стоге сена.

Guy рекомендует: Network Performance Monitor (БЕСПЛАТНАЯ ПРОБНАЯ ПЕРЕДАЧА)

SolarWinds Network Performance Monitor ( NPM) поможет вам узнать, что происходит в вашей сети. Эта утилита также поможет вам в устранении неполадок; на приборной панели будет указано, является ли основная причина неисправной ссылкой, неисправным оборудованием или перегрузкой ресурсов.

Что мне больше всего нравится, так это то, как NPM предлагает решения сетевых проблем. Он также имеет возможность отслеживать состояние отдельных виртуальных машин VMware.Если вы заинтересованы в устранении неполадок и создании сетевых карт, я рекомендую вам попробовать NPM в 30-дневной бесплатной пробной версии.

SolarWinds Network Performance Monitor Загрузить 30-дневную БЕСПЛАТНУЮ пробную версию

Переменная PowerShell $ _

Другой способ взглянуть на $ _ PowerShell — это просто как переменная. В конце концов, знак доллара — это способ PowerShell ввести любую переменную, а не только этот специальный элемент «В этом конвейере».

 
#PowerShell Script для вывода списка файлов Dll
$ Path = "C: \ Windows \ System32 \"
Get-ChildItem $ Path | Где { $ _ .расширение -eq '.DLL'}
  

Примечание 2: Целью этого примера является сравнение специальной переменной $ _ с обычной переменной с именем $ Path.

Значение «Где» {Оценка}

В большинстве примеров $ _ используется фильтр «где». Хотя многим разработчикам сценариев также нравится псевдоним?, Основной командлет: Where-Object. Давайте потратим время на изучение его свойств.

 
Clear-Host
Get-Help Where-Object -full
  

Примечание 3: Помните, что Where-Object — это фильтр; поэтому для выполнения своей работы требуется ScriptBlock для оценки теста.На самом деле два символа «$ и _» играют небольшую, но важную роль; Задача $ _ состоит в том, чтобы сократить оценку, сказав: «В этом конвейере», вместо того, чтобы явно упоминать тест во второй раз.

Подробнее об измерении скорости Where-Object

PowerShell $ _ с ForEach

Большинство моих примеров $ _ можно найти в предложениях Where-Object, но вот другое использование этой специальной переменной конвейера: ForEach. Еще раз обратите внимание, что $ _ — это первый элемент в фигурных скобках, но на этот раз за подчеркиванием следует параметр -replace, а не a.свойство.

Целью этого сценария является удаление и дублирование «the» в документах, хранящихся в папке ProofRead.

Clear-Host
$ file = gci «D: \ ProofRead \ *. Doc»
$ file
ForEach ($ str в $ file)
{
$ cont = Get-Content -path $ str
$ cont
$ продолжение | ForEach { $ _ -заменить «the, the»} | Set-Content $ str
}

Примечание 4: В примере ForEach синтаксис чисто $ _, нет свойства (.).

Guy рекомендует: Бесплатный WMI Monitor для PowerShell (БЕСПЛАТНЫЙ ИНСТРУМЕНТ)

Инструментарий управления Windows (WMI) — одно из скрытых сокровищ операционных систем Microsoft.К счастью, компания SolarWinds создала бесплатный монитор WMI для PowerShell , чтобы вы могли обнаружить эти жемчужины информации о производительности и, таким образом, улучшить свои сценарии PowerShell.

Не гадайте, какие счетчики WMI использовать при написании сценариев для операционной системы, Active Directory или Exchange Server. Попробуйте этот монитор WMI — это бесплатно.

SolarWinds WMI Monitor Скачать бесплатно инструмент

Дополнительные встроенные переменные PowerShell

Перечислить переменные PowerShell можно с помощью этой команды:

 
Get-Variable | Format-Table имя, значение -auto
  
Имя переменной Описание
$ _ Текущий объект трубопровода; используется в блоках сценария, фильтрах, разделе процесса функций, where-object, ForEach-object и переключателе
$ Args Используется при создании функций, требующих параметров
$ Env: Path Environment

.

Добавить комментарий

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