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

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

Использование существующих свойств объектов

Как правило, существующее свойство используется для группирования объектов. Group-Object записывает новый объект в конвейер. Рассмотрим команду:

Get-Service | Group Status

Хотя эта команда начинается со служб, в конце конвейера — объект Microsoft.PowerShell.Commands.GroupInfo. Большинство его свойств можно увидеть в примере на экране 1. Свойство Group — коллекция базовых объектов, имеющих общее значение свойства, то есть все выполняющиеся или остановленные службы. Свойство Name отражает имя каждой группы. Свойство Count отражает число объектов в каждой группе.

 

Группирование служб по свойству Status
Экран 1. Группирование служб по свойству Status

Поскольку вывод Group-Object — другой объект, его можно использовать, как любые другие объекты в PowerShell. Например, рассмотрим команду:

Get-Command | Group Verb | Sort Count -Descending |
Select -First 5 Name,Count | Format-Table -Auto

Эта команда получает информацию о команде PowerShell, группирует ее по свойству Verb, сортирует сгруппированную информацию по свойству Count в убывающем порядке и выбирает первые пять записей. На экране 2 показаны примеры результатов. Вероятно, этот пример не самый интересный, но он отлично демонстрирует использование Group-Object в конвейерном выражении.

 

Группирование информации по свойству Verb
Экран 2. Группирование информации по свойству Verb

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

Get-Eventlog System -Newest 250 | Sort Source |
Group EntryType,Source | Out-GridView -OutputMode Single |
Select -ExpandProperty Group |
Format-Table -GroupBy Source -Property TimeGenerated,
Message -Wrap

Команда начинается с извлечения 250 последних записей из журнала системных событий. Элементы сортируются по свойству Source. Затем результаты группируются сначала по свойству EntryType, затем по свойству Source. Результаты направляются в Out-GridView, как показано на экране 3.

 

Группирование записей журнала системных событий по свойствам EntryType и Source
Экран 3. Группирование записей журнала системных событий по свойствам EntryType и Source

Остальная часть команды выполняется после того, как я выбираю элемент и нажимаю кнопку OK. Затем команда развертывает свойство Group, которое представляет собой коллекцию элементов журнала событий, и форматирует результаты, как показано на экране 4. Это возможно потому, что Out-GridView записывает объекты в конвейер в PowerShell 3.0.

 

Расширение свойства Group
Экран 4. Расширение свойства Group

Применение пользовательских свойств

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

Dir C:\Work | Group { ((Get-date) — $_.CreationTime).Days}

$_ представляет каждый объект в конвейере. В этом примере извлекаются все файлы из каталога C:\Work и группируются на основе вычисленного значения — общего числа дней, прошедших со времени создания файла. Примеры результатов показаны на экране 5.

 

Группирование объектов на основе значения, полученного из блока сценария
Экран 5. Группирование объектов на основе значения, полученного из блока сценария

Просмотр только итогов по группам

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

Dir C:\Scripts -File -Recurse | Group Extension -NoElement |
Sort Count -Descending

Как показано на экране 6, я написал много сценариев PowerShell. Этот метод очень удобен для выборки нужных данных.

 

Просмотр только итогов по группам
Экран 6. Просмотр только итогов по группам

Создание сгруппированной хэш-таблицы

Иногда полезно работать со сгруппированными данными более интерактивными способами. Простой способ добиться этого — преобразовать объект GroupInfo в хэш-таблицу. Хэш-таблица, или массив ассоциативных элементов (или объект Dictionary во времена VBScript), состоит из пары ключ/значение. При преобразовании объекта GroupInfo в хэш-таблицу свойство Name становится ключом хэш-таблицы, а значение представляет собой коллекцию сгруппированных объектов. При использовании этого метода рекомендуется исключить любые объекты, которые приведут к пустому значению.

Чтобы превратить объект GroupInfo в хэш-таблицу и просмотреть его содержимое, используйте следующую команду:

$svc = Get-Service | Group Status -AsHashTable -AsString
$svc

Обратите внимание на использование параметра –AsString в первой команде. Многие свойства объекта выглядят как строки, но в действительности они представляют собой числовые значения или перечисления. Если не использовать параметр –AsString, то работать с хэш-таблицей будет очень трудно. Сложно понять, какие свойства нужно обрабатывать как строки, поэтому рекомендуется всегда использовать данный параметр при создании хэш-таблицы. На экране 7 показаны примеры результатов этой команды.

 

Создание хэш-таблицы $svc и просмотр ее?содержимого
Экран 7. Создание хэш-таблицы $svc и просмотр ее?содержимого

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

$svc.Stopped

Пример результата показан на экране 8.

 

Использование хэш-таблицы $svc для просмотра всех остановленных служб
Экран 8. Использование хэш-таблицы $svc для просмотра всех остановленных служб

Ниже приводится другой пример создания сгруппированной хэш-таблицы и просмотра ее содержимого:

$events = Get-Eventlog System -Newest 500 |
Group Source -AsHashTable -AsString
$events

Эта команда получает последние 500 элементов, записанных в журнал системных событий, и создает хэш-таблицу на основе свойства Source записи журнала событий. Примеры результатов этой команды показаны на экране 9.

 

Создание хэш-таблицы $events и просмотр ее?содержимого
Экран 9. Создание хэш-таблицы $events и просмотр ее?содержимого

Теперь у нас имеется объект, $events, с которым можно интерактивно работать, чтобы без труда анализировать события из различных источников. Удобное качество хэш-таблицы заключается в возможности ссылаться на значение, используя ключ как свойство. Здесь пригодится заполнение нажатием клавиши TAB. Рассмотрим пример:

$events.'Microsoft-Windows-Ntfs'

Эта команда обращается к одному из разделов источника события (Microsoft-Windows-Ntfs) и отображает все соответствующие записи журнала событий, как показано на экране 10. Обратите внимание, что хотя свойства преобразованы в строки, некоторые имена содержат нестандартные символы и должны быть заключены в кавычки.

 

Использование ключа хэш-таблицы как свойства
Экран 10. Использование ключа хэш-таблицы как свойства

Пример из практики

Чтобы завершить знакомство с Group-Object, рассмотрим практический пример. В листинге показан сценарий, FileExtensionAgeHTML, для анализа папки и создания HTML-отчета, в котором файлы сгруппированы по расширению и возрасту.

Основные части сценария следующие:

  • Программный код во фрагменте A выполняет группировку по пользовательскому свойству, при котором, в сущности, удаляется ведущая точка из имени расширения.
  • Программный код во фрагменте B просматривает все файлы и формирует новые группы на основе возраста файла. Каждая возрастная группа файлов преобразуется в HTML-фрагмент.
  • Программный код во фрагменте C выполняет сборку всех фрагментов в один HTML-отчет.

Сценарий совместим с PowerShell 2.0 и более новыми версиями. FileExtensionAgeHTML — демонстрационный сценарий, поэтому в нем жестко заданы путь поиска и имя HTML-файла. Эти значения нужно изменить.

Понимание без громоздких сценариев

Возможность группировать объекты по некоторым критериям позволяет лучше понять среду. В прошлом такое понимание зачастую было невозможно без громоздких сценариев. Для Group-Object не имеют значения типы используемых объектов. Я привел пример с файлами, но не составит труда группировать пользовательские объекты Active Directory (AD) или веб-сайты IIS. Освоив использование Group-Object, вы сможете применять свои навыки для решения разнообразных задач.

Листинг. Сценарий FileExtensionAgeHTML

$head = @'
'@
# НАЧАЛО ФРАГМЕНТА A
$path = «C:\scripts»
$files = DIR $path -Recurse -File
$groupExt = $files | where {$_.extension} |
Group-Object {$_.Extension.Substring(1)}
# КОНЕЦ ФРАГМЕНТА A
# НАЧАЛО ФРАГМЕНТА B
# Create aging fragments.
$30days = $files |
where { $_.LastWritetime -ge (Get-Date).AddDays(-30) } |
Group-Object {if ($_.extension) {
$_.Extension.Substring(1)}} | Select Name,Count,
@{Name=«Size»;Expression={
($_.Group | measure-object Length -sum).sum}} |
Sort Count -Descending |
ConvertTo-HTML -Fragment -PreContent «
30 Days
»
$90days = $files |
where { $_.LastWritetime -le (Get-Date).AddDays(-30) -and
$_.LastWritetime -ge (Get-Date).AddDays(-90) } |
Group-object {if ($_.extension) {
$_.Extension.Substring(1)}} | Select Name,Count,
@{Name=«Size»;Expression={
($_.Group | measure-object Length -sum).sum}} |
Sort Count -Descending |
ConvertTo-HTML -Fragment -PreContent «
30-90 Days
»
$180days = $files |
where { ($_.LastWritetime -le (Get-Date).AddDays(-90)) -and
($_.LastWritetime -ge (Get-Date).AddDays(-180)) } |
Group-object {if ($_.extension) {
$_.Extension.Substring(1)}} | Select Name,Count,
@{Name=«Size»;Expression={
($_.Group | measure-object Length -sum).sum}} |
Sort Count -Descending |
ConvertTo-HTML -Fragment -PreContent «
90-180 Days
»
$1yr = $files |
where { ($_.LastWritetime -ge (Get-Date).AddDays(-356)) } |
Group-object {if ($_.extension) {
$_.Extension.Substring(1)}} | Select Name,Count,
@{Name=«Size»;Expression={
($_.Group | measure-object Length -sum).sum}} |
Sort Count -Descending |
ConvertTo-HTML -Fragment -PreContent «
365 Days
»
# КОНЕЦ ФРАГМЕНТА B
$summary = $groupExt | Select Name,Count,
@{Name=«Size»;Expression={
($_.Group | measure-object Length -sum).sum}} |
Sort Size -descending |
ConvertTo-HTML -Fragment -PreContent `
«
Report by File Extension $Path
»
# НАЧАЛО ФРАГМЕНТА C
# Create the HTML report.
ConvertTo-Html -head $Head `
-title «Extension Report for $Path» `
-PostContent `
($summary + $30days + $90days + $180days + $1yr) |
Out-file c:\work\extrpt.htm
# КОНЕЦ ФРАГМЕНТА C