Одна из вещей, которую я люблю и ненавижу одновременно, — органичная гибкость Windows PowerShell. Во многом этот инструмент реализован как язык программирования. Если вы не программист, то можете легко игнорировать этот факт и обращаться с ним, как с инструментом командной строки.
Ну, не совсем уж игнорировать. Видите ли, в Windows PowerShell всегда есть три-четыре способа выполнения одной и той же задачи. Но вам достаточно найти лишь один способ, который гарантировано удовлетворяет ваши потребности. Вместе с тем, пытливый пользователь Windows PowerShell узнает о всех способах. Поэтому при изучении примеров, созданных другими людьми, вы сможете понять, что происходит.
Все дело в отношении
Псевдонимы — первое «быстрое решение», с которым вам придется столкнуться. Вот пример команды:
PS C:\> Get-WmiObject -class Win32_LogicalDisk -Filter "DriveType=3" |
>> Where-Object -FilterScript{ $_.FreeSpace / $_.Size -lt .1 } |
>> Select-Object -Property DeviceID,FreeSpace,Size
>>
Ее можно сократить так:
PS C:\>gwmi Win32_LogicalDisk -Fi "DriveType=3" |
>> ?{ $_.FreeSpace / $_.Size -lt .1 } |
>> SelectDeviceID,FreeSpace,Size
>>
Я воспользовался псевдонимами для обозначения имен команд и сократил длину параметров. Windows PowerShell позволяет урезать имена параметров, если урезанное имя позволяет однозначно определить, какой параметр имеется в виду. Это упрощает ввод с клавиатуры, но сильно усложняет чтение команд. Я рекомендую слушателям своих курсов всегда полностью указывать все имена при публикации примеров, сохранении в сценарии или при любом другом долгосрочном хранении команд. Потом в них будет проще разобраться.
Windows PowerShell ISE и большинство других редакторов позволяют вводить урезанную версию имени параметра и нажатием Tab вставлять полное имя. Так приходится меньше вводить с клавиатуры, а читабельность полученных команд остается на высоте. Я совсем не указал имена других параметров, потому что они позиционные. Иначе говоря, если я укажу все значения в правильном порядке, команда будет работать.
И в этом случае отсутствие имен затрудняет чтение. Большинство людей предпочтут, чтобы в сценарии указывались полные имена параметров. Использование полных имен параметров в некотором смысле упрощает задачу и вам, потому что не нужно помнить их порядок. При явном использовании имен порядок параметров не имеет значения.
Выражения в кавычках
Иногда требуется вставлять результат выражения в строку с кавычками. Это позволяет вставлять результат работы сценария. Один из способов выполнения этой задачи — сохранение результата в переменной, после чего надо заставить Windows PowerShell вставить переменные в строки с двойными кавычками. Windows PowerShell заменит переменную ее содержимым:
$disk = Get-WmiObject -class Win32_LogicalDisk -filter "DeviceID='C:'"
$freepercent = $disk.freespace / $disk.size * 100
Write-Host "Free space on drive C: at $freepercent percent"
Windows PowerShell также позволяет размещать в двойных скобках целое выражение. Синтаксис напоминает один из приемов работы в оболочке Unix. Это выглядит немного странно, но вы увидите, что это используется довольно часто:
$disk = Get-WmiObject -class Win32_LogicalDisk -filter "DeviceID='C:'"
Write-Host "Free space on drive C: at $($disk.freespace / $disk.size * 100) percent"
Я предпочитаю первую форму, потому что мне приходится работать с начинающими пользователями Windows PowerShell. Ее проще читать. На каждом этапе сценария выполняется одна операция, что также способствует пониманию.
Во второй форме используется много пунктуации, поэтому она сложнее для понимания. Вместе с тем, вторая форма технически более эффективна. В ней не создается переменной, которую Windows PowerShell приходится отслеживать. Также она требует вводить меньше текста с клавиатуры, что нравится большинству пользователей.
Создание объектов
Длинная форма снова оказывается кстати, когда нужно создать нестандартный объект:
$cs = Get-WmiObject -class Win32_ComputerSystem
$os = Get-WmiObject -class Win32_OperatingSystem
$obj = New-Object -TypeNamePSObject
$obj | Add-Member -MemberTypeNoteProperty -Name OSVersion -Value $os.version
$obj | Add-Member -MemberTypeNoteProperty -Name Mfgr -Value $cs.manufacturer
$obj | Add-Member -MemberTypeNoteProperty -Name Model -Value $cs.model
Write-Output $obj
Иногда я немного сокращаю эту форму за счет добавления команд Add-Member в один конвейер:
$cs = Get-WmiObject -class Win32_ComputerSystem
$os = Get-WmiObject -class Win32_OperatingSystem
$obj = New-Object -TypeNamePSObject
$obj | Add-Member -MemberTypeNoteProperty -Name OSVersion -Value $os.version –PassThru |
Add-Member -MemberTypeNoteProperty -Name Mfgr -Value $cs.manufacturer –PassThru |
Add-Member -MemberTypeNoteProperty -Name Model -Value $cs.model –PassThru
Недавно я перешел к немного другой форме, которая еще удобнее для чтения.
$cs = Get-WmiObject -class Win32_ComputerSystem
$os = Get-WmiObject -class Win32_OperatingSystem
$props = @{'OSVersion'= $os.version;
'Mfgr'=$cs.manufacturer;
'Model'=$cs.model}
$obj = New-Object -TypeNamePSObject -Property $props
Write-Output $obj
И на этот раз я предпочитаю этот длинный синтаксис из-за удобства чтения. Я работаю с новичками в деле применения Windows PowerShell, и для них читабельность критически важна для того, чтобы понимать, что происходит. Некоторые применяют следующий прием:
$obj = Select-Object OSVersion,Mfgr,Model
$obj.OSVersion = $os.version
$obj.Mfgr = $cs.manufacturer
$obj.Model = $cs.model
$obj
Результаты практически такие же, но текста меньше. Такое использование Select-Object скорее хакерский прием. В нем используется тот факт, что команду Select-Object можно заставить выбрать несуществующее свойство. При этом это свойство создается с пустым значением в качестве выходного объекта,
Мне очень больно видеть такое (зло)употребление командой Select-Object, и я в этом не одинок. Еще в 2008 году некоторые из моих коллег, носящих звание Windows PowerShell MVP высказывались против такого употребления. Независимо от личного отношения к этому приему, нужно научиться распознавать его, чтобы понимать, что при этом происходит.
В Windows PowerShell версии 3 можно делать так:
[pscustomobject]@{'OSVersion'= $os.version;
'Mfgr'=$cs.manufacturer;
'Model'=$cs.model}
Я пока не определился с отношением к такому синтаксису, но в целом все выглядит нормально. В нем сохраняется понятность, а печатать приходится меньше. Люди будут использовать его, поэтому его тоже надо научиться распознавать. Кажется, такой синтаксис еще и выполняется быстрее подробнее о нем можно почитать в блоге Джонатана Медда по адресу
Ярлыки объектов
В последнем приеме используются ярлыки — он похож на прием с выражениями в строках. Это совершенно легальный синтаксис программирования — он сложен только для новичков. Еще раз — в нем нет ничего «неправильного», поэтому надо привыкнуть к такому виду и понимать, что здесь происходит.
Допустим, вам нужно выполнить метод объекта, а затем что-то сделать с одним из свойств полученного в результате объекта:
$string = ' Hello '
$string = $string.Trim()
$string.Length
Можно сократить все с помощью скобок. Сначала вычисляется выражение со скобками, а затем Windows PowerShell по сути заменяет скобки результатом:
$string = ' Hello '
($string.Trim()).length
Здесь наворочено много пунктуации, поэтому сложно разобраться, что следует за чем и определить, что, собственно, происходит, но это вполне «законный» прием. Этот вариант может выполняться даже чуточку быстрее, потому что оболочке Windows PowerShell не нужно так много обращаться к переменной.
Извините за неоднозначность
В некотором смысле очень жаль, что Windows PowerShell не в состоянии предписать один-единственный способ выполнения задач. Тогда бы нам не пришлось изучать массу разных подходов. На сегодняшний день у нас есть оболочка, которая поддерживает много вариантов синтаксиса для выполнения одной и той же задачи. Полезно по крайней мере быть знакомым со всем вариантами синтаксиса. Тогда вы сможете работать с любыми попадающими вам под руки примерами.