Windows PowerShell

Что такое $_?

Дон Джонс

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

Import-CSV c:\users.csv | ForEach-Object New- QADUser –logonname $_.username –name $_.Name }

На недавней конференции TechMentor я начал свое выступление с подобного выражения и использовал более длинную его версию на обсуждениях Tech·Ed.Этот сценарий импортирует информацию о пользователях из файла .CSV и преобразовывает ее в новых пользователей в Active Directory. Это хороший пример, демонстрирующий, что Windows PowerShell может выполнять достаточно много простых исполняемых команд вместо сложных сценариев.

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

Передача объектов

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

Get-Service

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

Get-Service | Stop-Service

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

Get-Service | Sort-Object Name Get-Process | Sort-Object Name

В этом примере Sort-Object сможет работать с любыми объектами служб или объектами процессов, передав их в зависимости от свойств объекта.В обоих случаях вы укажете свойство Name.

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

Get-Process | Sort-Object –property Name

Это означает «Использовать этот объект и сортировать его по свойству Name». Каждый командлет имеет один или несколько параметров, которые настраивают поведение командлета на определенный режим. Sort-Object, например, имеет другие параметры, которые предлагают изменить порядок сортировки на обратный:

Get-Process | Sort-Object –property Name –descending

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

Суперпараметры: блоки сценариев

Некоторые особенно мощные командлеты могут принимать в качестве параметров целые сценарии. В Windows PowerShell они обычно называются блоками сценариев (scriptblocks), потому что каждый из них является блоком кода сценария. Чаще всего они состоят из блоков команд, а не сценариев с подробным кодом, но вы можете поместить в блок что-нибудь более привлекательное.

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

Если блок сценария выводит булево значение True, то преданный в него объект будет выведен; если он выводит False, то переданный в него объект будет отвергнут. Используя эту возможность, вы можете применять Where-Object для фильтрации объектов на основе некоторого критерия. Например:

Get-WmiObject –class Win32_Service | Where-Object –filterscript $_.State –eq "Running"}

И снова мы видим $_. На самом деле, как –class так и –filterscript являются позиционными параметрами, т. е. если вы передаете значение параметра на первой позиции, то не нужно передавать имя этого параметра. Это означает, что, как правило, вы получите что-то вроде этого:

Get-WmiObject Win32_Service | Where-Object $_.State –eq "Running"

Это то же самое. Фигурные скобки — это стандартный способ оболочки заключить код в блок сценария. В этом случае, блок сценария, это не больше, чем просто сценарий; справедливое и простое сравнение. Вот где появляется $_: помните, что блок сценария выполняется один раз для каждого переданного объекта. Когда выполняется блок сценария, $_ заменяется каждым входящим объектом. Другими словами, $_ это заменитель для текущего передаваемого объекта. В данном случае $_ предлагает сравнить свойства объектов — а именно, свойство State со значением «Running».

Что находится в Name?

Я всегда думал, что $_ является избыточной частью синтаксиса. Иногда мне казалось, что легко читаемые имена, вроде $piped-in или $inputobject могут передавать больше смысла, но $_ легко вводить. Я говорил, что $_ имеет такое обозначение, потому что напоминает символ вертикальной черты (|), лежащей на боку, подразумевая, что он содержит все, что было в него передано.

Человек, который рассказал мне это, знал о чем говорил и был вполне серьезен — но я все еще не верил в это. Я решил, что для меня, $_ будет как пустой значок, что-то вроде «заполни пустое». Это пустое место должно автоматически заполняться оболочкой при выполнении блока сценария.

И в этом состоит как раз то, что приводит большинство людей в замешательство: значок $_ необходим и нужен в указанных местах, где Windows PowerShell явно ищет его и готова заменить на входящие объекты. Одно из таких мест, это блок сценария, используемый для командлетов Where-Object и ForEach-Object (как в начале этой статьи).

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

Гнезда: не только для птиц

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

Get-Content c:\names .txt | ForEach-Object Get-WmiObject –computername $_ -class Win32_ OperatingSystem | ForEach-Object $_.Reboot() } }

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

Get-Content c:\names .txt | ForEach-Object Get-WmiObject –computername $_ -class Win32_ OperatingSystem | ForEach-Object $_.Reboot() } }

 

Это легче выполнять, чем вложенный командлет ForEach-Object. Каждый из них имеет блок сценария и каждый блок сценария использует $_. Фокус в том, что $_ фактически меняет свой смысл во время выполнения этой командной строки, но он всегда содержит один из объектов, которые были переданы в командлет ForEach-Object.

Итак, первый ForEach-Object принимает имя компьютера из Get-Content; поэтому первый $_ содержит имена компьютеров, которые будут переданы параметру — computername.

Второй ForEach-Object принимает значение из Get-WmiObject, поэтому $_ содержит объекты WMI, в частности, экземпляры классаWin32_ OperatingSystem, которые имеют соответствующий метод Reboot().

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

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

Это всегда ошибки

Разница между новичком и гуру Windows PowerShell заключается не в знании какой-либо подходящей команды или умении написать сложный сценарий. Скорее, гуру может справиться с несколькими «ошибками» или лишними частями синтаксиса, которые есть в каждой оболочке или языке. $_ один из таких примеров. Это не так уж просто понять, лишь взглянув на него.

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

гуру Windows PowerShell.

 

Дон Джонс (Don Jones) — один из наиболее опытных национальных инструкторов и авторов по Windows PowerShell. Он ведет еженедельный блог Windows PowerShell на ConcentratedTech.com и с ним можно связаться там же..