Довольно распространено желание того, чтобы сценарии выводили данные в приятном для чтения виде, и меня часто спрашивают, как лучше всего создавать такие штуки, как таблицы. Существует пара способов это сделать. Первый из них – написать сценарий, который помещает данные – то есть любые данные, которые мне нужно отобразить в отформатированном виде, – в переменные, названные $data1, $data2, and $data3. Теперь можно приступить к форматированию данных.
Текстовый способ
Наиболее распространенный способ подхода к подобной задаче – такой же, как и при работе на таком языке, как VBScript, Perl, KiXtart или любом другом ориентированном на текст языке. Я начну с того, что напишу несколько заголовков столбцов на экране:
Write-Host "ColumnA'tColumnB'tColumnC"
Обратите внимание на то, что `t – это особая отделенная последовательность в Windows PowerShellTM которая вставляет символ табуляции. Для того, чтобы выписать каждую из строк в таблице – помните, что данные находятся в $data1, $data2 и $data3, – я могу поступить по-разному. Один из способов – просто написать переменные, полагаясь на способность консоли заменять переменные их содержимым в двойных кавычках:
Write-Host "$data1't$data2't$data3"
Есть чуть более привлекательный способ сделать это – оператор f. Эта методика отделяет форматирование от данных и создает строку кода, которую я считаю наиболее удобной для прочтения и более простой в поддержке:
Write-Host "{0}'t{1}'t{3}" –f $data1,$data2,$data3
Несмотря на это, данных подход имеет несколько значительных недостатков. Прежде всего, полагаясь на фиксированные остановки по табуляции в окне консоли, я создаю довольно странное форматирование. Например, если определенная строка таблицы имеет в первом столбце 20-символьное значение, я отправляю все форматирование в эту строку. Кроме того, поскольку я вывожу весь текст с помощью Write-Host, мои действия двольно сильно ограничены отображением консоли.
Не существует простого способа отправки выводных данных в файл или преобразования их в другие форматы, даже если мне очень этого захочется. Что важнее всего, этот подход, ориентированный на текст, полностью игнорирует основанную на объекте консоль, в которой я работаю, и не испольует преимуществ невероятных возможностей и технологий Windows PowerShell.
Путь Windows PowerShell
Windows PowerShell – объектно-ориентированный. А это в идеале означает, что все, над чем работает программист, должно быть в объектах, позволяя консоли превращать все при необходимости в текстовые отображения. Но как создать объекты для случайных обрывков данных?
Я, продолжая свой пример, начинаю с создания пустого пользовательского объекта и сохранения его в переменной:
$obj = New-Object PSObject
Благодаря этому у меня есть свежий пустой объект для работы. Затем я добавляю данные к объекту в виде свойств. Для этого я просто направляю свой объект по каналу в командлет Add-Member. Я добавляю нечто, называемое NoteProperty, даю свойству имя (хорошая идея – использовать заголовки столбцов как имена свойств), а затем вставляю данные как значения свойств:
$obj | Add-Member NotePropertyColumnA $data1
$obj | Add-Member NotePropertyColumnB $data2
$obj | Add-Member NotePropertyColumnC $data3
Теперь я просто вывожу этот объект – не в консоль, а в конвейер:
Write-Output $obj
Затем можно повторить эти действия для каждой строки таблицы, которую нужно вывести. Ниже приведена короткая функция, принимающая строку и выводящая ее версии в верхнем и нижнем регистре, а также ее первоначальный вариант:
functionStringVersions {
param([string]$inputString)
$obj = New-Object PSObject
$obj | Add-Member NoteProperty Original($inputString)
$obj | Add-Member NoteProperty Uppercase($inputString.ToUpper())
$obj | Add-Member NoteProperty Lowercase($inputString.ToLower())
Write-Output $obj
}
$strings = @("one","two","three")
foreach ($item in $strings) {
StringVersions $item
}
Я начиная с массива строковых переменных, работаю с одной за раз, и отправляю их в функцию. А выводные данные функции записываются в конвеер.
Следует отметить, что существуют более короткие способы написания этого кода, но я выбрал именно этот, поскольку он четко иллюстрирует то, что я хочу сказать. Результатом является идеально отформатированная таблица, как показано на рис. 1. А все потому, что консоль уже знает, как форматировать объекты в таблице.
Рис. 1 Выводные данные Windows PowerShell, отображенные в таблице
Еси объект имеет четыре свойства или менее, Windows PowerShell автоматически выбирает таблицу. Поэтому у меня есть отличная таблица без каких-либо усилий с моей стороны.
Но это еще не все.
Командлет месяца: Get-Command
Я уверен, что вы использовали Get-Command или его полезный псевдоним (gcm) раз или другой для просмотра списка доступных командлетов Windows PowerShell. Но, возможно, вам неизвестно, насколько гибок gcm в действительности. Например, если вы хотите увидеть все, что Windows PowerShell может сделать со службой, запустите службу gcm -noun. Или же, если вы хотите увидеть все возможности экспорта Windows PowerShell, попробуйте выполнить экспорт gcm -verb. Если вам просто нужно увидеть, какие командлеты были добавлены той или иной оснасткой, например PowerShell Community Extensions, воспользуйтесь gcm -pssnapin pscx. (Можно заменить «pscx» именем любой оснастки, чтобы увидеть ее командлеты.)
Как можно видеть, Get-Command – основной игрок на поле изучения Windows PowerShell. Он позволяет узнать, какая функция доступна, не заглядывая в руководство. Следует отметить, что gcm – более точный способ обнаружения функциональности, нежели использование команды, например Help *. Функция Help только перечисляет темы справки. Любые командлеты, которые не были включены в изначальную версию справки, не отображаются в этом списке – даже если они находятся в нужном вам месте.
Преимущества этой методики распространяются далеко за пределы таблиц. При работе с объектами Windows PowerShell знает, как выполнить огромный диапазон задач. Нужно, чтобы данные содержались в CSV-файле? Используйте Export-CSV. Больше нравится HTML-таблица? Передайте объекты по конвейеру в ConvertTo-HTML. Данные нужны в формате списка? Передайте их в Format-List. Используя объекты, можно использовать все, что консоль уже умеет делать. Вот пересмотренный пример.
functionStringVersions {
PROCESS {
$obj = New-Object PSObject
$obj | Add-Member NoteProperty Original($_)
$obj | Add-Member NoteProperty Uppercase($_.ToUpper())
$obj | Add-Member NoteProperty Lowercase($_.ToLower())
Write-Output $obj
}
}
На этот раз я изменил функцию таким образом, чтобы она принимала выводные данные конвейера – это всегда хорошая идея при работе с объектами – и передавала выводные данные по конвейеру.
Функция поместила свой код в блок сценария PROCESS. Это значит, что функция примет вводные данные конвейера и выполнит блок сценария PROCESS по разу для каждого переданного объекта.
В блоке сценария PROCESS есть специальная переменная $_. Это ссылка на текущий объект конвейера,который обрабатывается. Практический результат заключается в том, что я могу передавать через конвейер массив строк:
@("one","two","three") | StringVersions
Я получаю таблицу, поскольку функция помещает свои выводные данные в конвейер. В конце конвейера консоль вызывает подсистему форматирования, которая принимает решение использовать таблице, поскольку объекты в конвейере имеют менее пяти свойств (если свойств больше, по умолчанию будет использован список).
Но мне не обязательно полагаться на поведение по умолчанию. Я просто могу передать эти объекты в другой командлет, чтобы сделать с ними что-нибудь другое:
@("one","two","three") | StringVersions | Format-List
@("one","two","three") | StringVersions | ConvertTo-HTML | Out-File "strings.html"
@("one","two","three") | StringVersions | Export-CSV "strings.csv"
@("one","two","three") | StringVersions | Select Uppercase,Lowercase -unique
На Рис. 2 показан полученный в результате выполнения второго примера HTML, отображаемый в веб-обозревателе. Просто представив выводные данные в виде объектов, а не в виде простого текста, я вдруг получил полный доступ ко всему многообразию функций, встроенных в консоль, – набор шаблонов форматирования, преобразование в HTML, параметры экспорта, возможность сортировки, фильтрации, группировки и многие другие.
Рис. 2 Выводные данные Windows PowerShell в формате HTML
Это очень мощная штука. В действительности я даже предложу вам сделать так, чтобы каждый написанный вами сценарий создавал объект как свои выводные данные, чтобы их можно было использовать максимальным количеством возможных способов – и при этом не придется написать ни единой строки кода.