Одна из причин замечательной гибкости PowerShell и, возможно, трудностей в освоении продукта заключается в отсутствии монолитных команд, выполняющих шесть различных действий. Вместо этого предусмотрены простые команды, которые можно объединить в конвейерное выражение. У каждой команды есть одно назначение. Одна из часто используемых команд — Group-Object, для которой применяется псевдоним Group.
Как видно из названия, команда Group-Object помещает объекты в группы на основе какого-либо свойства. Это может быть существующее или пользовательское свойство. Также далее будут показаны специальные приемы, например просмотр только общего числа сгруппированных объектов и создание сгруппированной хэш-таблицы.
Использование существующих свойств объектов
Как правило, существующее свойство используется для группирования объектов. Group-Object записывает новый объект в конвейер. Рассмотрим команду:
Get-Service | Group Status
Хотя эта команда начинается со служб, в конце конвейера — объект Microsoft.PowerShell.Commands.GroupInfo. Большинство его свойств можно увидеть в примере на экране 1. Свойство Group — коллекция базовых объектов, имеющих общее значение свойства, то есть все выполняющиеся или остановленные службы. Свойство Name отражает имя каждой группы. Свойство Count отражает число объектов в каждой группе.
Экран 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 в конвейерном выражении.
Экран 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.
Экран 3. Группирование записей журнала системных событий по свойствам EntryType и Source |
Остальная часть команды выполняется после того, как я выбираю элемент и нажимаю кнопку OK. Затем команда развертывает свойство Group, которое представляет собой коллекцию элементов журнала событий, и форматирует результаты, как показано на экране 4. Это возможно потому, что Out-GridView записывает объекты в конвейер в PowerShell 3.0.
Экран 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 показаны примеры результатов этой команды.
Экран 7. Создание хэш-таблицы $svc и просмотр ее?содержимого |
Созданную хэш-таблицу можно использовать для самых разнообразных задач. Например, чтобы увидеть все остановленные службы, выполните команду:
$svc.Stopped
Пример результата показан на экране 8.
Экран 8. Использование хэш-таблицы $svc для просмотра всех остановленных служб |
Ниже приводится другой пример создания сгруппированной хэш-таблицы и просмотра ее содержимого:
$events = Get-Eventlog System -Newest 500 | Group Source -AsHashTable -AsString $events
Эта команда получает последние 500 элементов, записанных в журнал системных событий, и создает хэш-таблицу на основе свойства Source записи журнала событий. Примеры результатов этой команды показаны на экране 9.
Экран 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