Недавно я сделал запись в своем блоге, где изложил двенадцать рекомендаций по использованию Windows PowerShell. Двенадцатая рекомендация предлагает разработчикам выводить в результате работы сценариев и функций объекты, а не текст. Я предложил активно использующим командлет Write-Host разработчикам остановиться и подумать, что же они делают. Командлет Write-Host не возвращает текст.
Вскоре после публикации этой записи ко мне обратился один читатель с вопросом о коде, который я собственно использовал для создания пользовательского объекта. Он сказал, что никогда не еще не встречался с таким подходом. Это неудивительно, потому что Windows PowerShell предоставляет десятки способов выполнения одной и той же задачи. Читатель предложил мне написать статью о различных способах создания пользовательских объектов, и это именно эта статья.
Полноформатный способ
Скорее всего, именно этим способом большинство людей создают пользовательские объекты. Этот подход я называю «хрестоматийным». Его отличает максимальная четкость, но он предусматривает много работы по вводу текста. Если у меня есть по одному объекту в переменных $os и $bios, я могу скомбинировать имеющуюся информацию так:
$object = New-Object –TypeNamePSObject
$object | Add-Member –MemberTypeNoteProperty –Name OSBuild –Value $os.BuildNumber
$object | Add-Member –MemberTypeNoteProperty –Name OSVersion –Value $os.Version
$object | Add-Member –MemberTypeNoteProperty –Name BIOSSerial –Value $bios.SerialNumber
Write-Output $object
Эту схему можно использовать для добавления любой другой информации в окончательный выходной объект до его записи в конвейер.
Короткий путь с -PassThru
Первый способ можно сделать чуть более лаконичным, заставив Add-Member вернуть объект в конвейер:
$object = New-Object –TypeNamePSObject
$object | Add-Member –MemberTypeNoteProperty –Name OSBuild –Value $os.BuildNumber –PassThru |
Add-Member –MemberTypeNoteProperty –Name OSVersion –Value $os.Version –PassThru |
Add-Member –MemberTypeNoteProperty –Name BIOSSerial –Value $bios.SerialNumber
Write-Output $object
Если строка заканчивается вертикальной чертой (|), Windows PowerShell ожидает увидеть в следующей физической строке следующую команду в конвейере. В сущности, это способ разбиения длинной команды на несколько строк. Этот прием вместе с параметром –PassThru превращает это в серию из трех разных команд.
Хеш-таблицы, вперед!
Описанные способы эффективны, но при этом «многословны». В сценарии бывает сложно визуально проследить, что происходит. New-Object предоставляет возможность решить задачу более лаконично. Это позволяет создать хеш-таблицу (или ассоциативный массив), содержащую имена свойств и значения, которые надо добавить ко вновь созданному объекту. Каждое из этих свойств автоматически создается как свойство NoteProperty:
$properties = @{'OSBuild'=$os.BuildNumber;
'OSVersion'=$os.version;
'BIOSSerial'=$bios.SerialNumber}
$object = New-Object –TypeNamePSObject –Prop $properties
Write-Output $object
Следующий вариант дает тот же результат, но более лаконичными средствами. Некоторые умные люди используют выражение со скобками, тогда оно становится еще короче. Однако я считаю, что это немного затрудняет чтение:
$object = New-Object –TypeNamePSObject –Prop
(@{'OSBuild'=$os.BuildNumber;
'OSVersion'=$os.version;
'BIOSSerial'=$bios.SerialNumber})
Write-Output $object
Еще один шажок
Вы заметили, что во всех этих примерах перед передачей пользовательского объекта по конвейеру я сохраняю его в переменной $object. Причина проста: может потребоваться дополнительно поработать с объектом. Например, можно дать объекту имя пользовательского типа:
$object.PSObject.TypeNames.Insert(0,'My.Custom.Name')
Это позволяет создать пользовательский формат для отображения вашего объекта. Я использовал этот прием с замечательными результатами в «Windows PowerShell Scripting and Toolmaking» (Concentrated Technology and Interface Technical Training, 2011), краткой книге, которую я написал и в которой рассказал о таких вещах, как использование пользовательских объектов для вывода данных из пользовательских средств Windows PowerShell.
На любой вкус и цвет…
Не существует «неправильных» способов решения задач в Windows PowerShell, если в конечном итоге вы решаете поставленную задачу. Вместе с тем, есть ряд приемов, которые я стараюсь не использовать, в основном из-за того, что они менее читабельны и сложнее для преподавания, особенно для начинающих пользователей. Вот один из таких приемов, в котором, как и раньше, предполагается, что переменные $os и $bios содержат объекты, из которых мне нужно извлечь информацию:
$os | Select-Object –Property @{n='OSVersion';e={$_.Version}},
@{n='OSBuild';e={$_.BuildNumber}},
@{n='BIOSSerial';e={$bios.SerialNumber}}
Результат будет таким же, что и в предыдущих примерах, но синтаксис ужасен. Здесь масса пунктуации, структурных особенностей и нужно знать много других вещей о Select-Object, чтобы справиться с этими тремя хеш-таблицами.
Здесь фактически генерируются пользовательские свойства средствами синтаксиса, присущего этому конкретному командлету (и еще командлетам Format). Такой синтаксис обычно характерен для людей с программистским опытом. Для меня (и многих других) разбирать такой текст очень трудно, поэтому я стараюсь не писать в таком стиле.
Оболочка PowerShell ничто, если она не гибкая
Уникальность Windows PowerShell в том, что она позволяет делать некоторые по-настоящему дикие вещи. Вернемся к моему примеру с хеш-таблицами. Задачу можно решить и так:
$info = @{}
$info.OSBuild=$os.BuildNumber
$info.OSVersion=$os.version
$info.BIOSSerial=$bios.SerialNumber
$object = New-Object –TypeNamePSObject –Prop $info
Write-Output $object
Мы создаем пустую хеш-таблицу, а затем добавляем информацию, ссылающуюся на несуществующие свойства. Например, при попытке впервые обратиться к OSBuild оболочка Windows PowerShell понимает, что в объекте $info (которые представляет собой пустую хеш-таблицу) такого свойства не существует. Но она просто создает это свойство и присваивает ему значение. Совершенно безбашенная вещь, тем не менее работает.
Мораль: объекты, а не текст
Как бы вы не создавали свои пользовательские объекты, можно создавать объекты, а не возвращать в окно консоли простой текст. Объекты намного гибче и позволяют интегрировать код сценария или функции с любыми другими компонентами Windows PowerShell.