На одном из недавних занятий по Windows PowerShell, которые я веду, некоторым учащимся было очень трудно мысленно представить себе конвейер PowerShell. Должен признать, что точное наглядное представление конвейера невозможно, поэтому учащимся, лучше воспринимающим информацию зрительно, может быть очень трудно точно понять происходящее. Когда я добрался до концепции функций фильтрации, работающих непосредственно в конвейере, все еще больше усложнилось, а на лицах некоторых учащихся было написано полное непонимание.
Чтобы помочь им разобраться, на следующее занятие я взял коробку, несколько наклеек с именами и шары для настольного тенниса. (Никто не утверждал, что среда Windows PowerShell® не может быть занятной.) Для демонстрации я решил использовать следующую командную строку:
Get-Process | Where { $_.VM –gt 1000 } | Sort VM
–descending | Export-CSV C:\Procs.csv
Будьте командлетом
С помощью наклеек с именами, наклеек «Привет, меня зовут …», которые вы всегда носили на встречах выпускников школы и других кошмарных мероприятиях, я присвоил всем учащимся имена командлетов. Я объяснил, что шары для настольного тенниса представляют объекты процесса Windows® (точнее, объекты процесса System.Diagnostics.Process), и спросил учащихся, по имени какого командлета можно сказать, что он, вероятнее всего, создает объекты процесса.
Изучив наклейки друг друга, учащиеся пришли к очевидному решению – Get-Process. Так была показана одна из главных причин, по которой мне нравятся имена командлетов в Windows PowerShell: обычно они очень понятны.
Итак, учащийся, изображающий командлет Get-Process, взял все шары и кинул их в картонную коробку. Коробка представляла конвейер PowerShell, а действия учащегося были очень сходны с операциями командлета. Командлет создает один или несколько объектов, после чего сбрасывает их в конвейер.
Затем очередь переходит к следующему командлету в конвейере. В этом примере учащийся, представляющий командлет Where-Object, поднял шары и поочередно изучил каждый из них, проверяя, превышает ли значение свойства VM 1 000. В PowerShell «VM» означает виртуальную память, и для данного занятия я на всех шарах маркером написал определенный объем виртуальной памяти.
Все шары (или процессы) со значением свойства VM 1000 или больше помещались обратно в коробку (иногда называемую конвейером), а шары с меньшим значением были выкинуты в мусорную корзину и больше никогда не использовались. (В действительности все было не так, я достал их, чтобы использовать на будущих занятиях.)
После этого учащийся, изображающий командлет Sort-Object, расставил все шары в коробке по порядку на основе свойства VM. Должен признать, что эту часть упражнения я продумал не так хорошо — расставить шары, чтобы они не раскатывались по всему помещению, было очень непросто! По-видимому, для следующего занятия мне придется найти кубики для настольного тенниса или иметь под рукой несколько картонных коробок для яиц.
Наконец, учащийся, представляющий Export-CSV, поднял шары и записал данные в файл CSV. В материальном мире это означает запись свойств процесса на перекидном плакате, который мы считали файлом CSV.
Функция фильтрации
Закончив с простым конвейером, мы решили рассмотреть более сложные функции фильтрации. Я предложил функцию фильтрации и командную строку, показанные на рис. 1.
Рис. 1. Пример функции фильтрации и командной строки
Function Do-Something {
BEGIN { }
PROCESS {
$obj = New-Object PSObject
$obj | Add-Member NoteProperty "TimeStamp" (Get-Date)
$obj | Add-Member NoteProperty "ProcessName" ($_.Name)
Write-Output $obj
}
END {}
}
Get-Process | Do-Something | Format-Table *
Прежде всего я хочу вкратце рассмотреть назначение функций фильтрации. Идея состоит в том, что функция содержит три блока сценариев, которые называются BEGIN, PROCESS и END.
При использовании функции в конвейере первым выполняется блок сценариев BEGIN. Этот сценарий может выполнять какие-либо задачи установки, например открытие подключения к базе данных.
Затем блок PROCESS выполняется единожды для каждого из переданного функции объекта. В блоке сценария PROCESS есть специальная переменная $_, автоматически заполняемая текущим объектом конвейера. Поэтому если передано 10 объектов, блок сценариев PROCESS выполняется 10 раз.
Наконец, после выполнения всех переданных объектов выполняется блок сценариев END, который выполняет все необходимые задачи очистки, такие как закрытие подключения к базе данных.
Если какой-либо объект в этих блоках сценариев написан с помощью Write-Output, он сохраняется в конвейере для следующего командлета. Все остальные объекты удаляются.
Поскольку мы начали с командлета Get-Process, последний учащийся собрал все шары (мы сделали небольшой перерыв, и шары каким-то образом оказались разбросаны по всему помещению) в коробку-конвейер. После этого пришла очередь учащегося, представляющего функцию фильтрации «Do-Something».
Сначала он выполнил блок сценариев BEGIN. Выполнение пустого блока не заняло много времени. Затем он собрал все объекты конвейера (шары для настольного тенниса) и начал их поочередно рассматривать. Это было довольно весело, поскольку шаров было около десяти, и он пытался удержать их все на коленях, наверное, вам стоило на это посмотреть.
Так или иначе, он брал по одному объекту процесса и выполнял его блок сценариев PROCESS. Этот блок сценариев стал причиной создания им нового пользовательского объекта. (Для этого я использовал специальные желтые шары для пинг-понга, которые, думаю, не трудно найти в местном магазине спортивных товаров.) На новых шарах учащийся написал текущую дату и время, а также имя объекта процесса, который он в тот момент рассматривал.
Затем этот новый пользовательский объект был записан в конвейер, то есть он положил шар в коробку, а исходный шар объекта процесса был выброшен. Он проделал это для всех шаров объектов процесса в коробке, всего около 12. По завершении процесса все белые шары были заменены желтыми шарами пользовательских объектов. Наконец, он выполнил блок сценариев END, который был пустой и не занял много времени.
Последним была очередь учащегося Format-Table. Он взял все содержимое коробки (на тот момент в ней были желтые «пользовательские» объекты) и приступил к созданию таблицы, используя оба свойства, написанные на каждом шаре. Результатом стала написанная на перекидном плакате таблица с двумя столбцами, TimeStamp и ProcessName, и примерно десятью строками.
Думайте материально
Это упражнение позволило всем учащимся понять конвейер и функции фильтрации. Шары для настольного тенниса великолепно заменили объекты, позволив абстрагироваться, когда речь шла только о PowerShell/
Все смогли пронаблюдать работу командлетов с объектами, размещение результатов в конвейере и их использование следующим командлетом для дальнейшей работы. Последовательность события в функции фильтрации была более очевидной, как и метод поочередной работы блока сценариев PROCESS с входными объектами.
Таким образом были показаны способы создания нового пользовательского объекта и его помещения в конвейер. Более того, это позволило показать преимущества создания пользовательских объектов по сравнению с простым текстом — новые пользовательские объекты могли быть использованы другими командлетами, такими как Format-Table, обеспечивая достаточную гибкость использования и представления данных.
Практическое применение
Учащиеся задали очевидный вопрос о том, как только что продемонстрированный конвейер и функции фильтрации могут использоваться на практике. Ответ очень простой: я создал несколько примеров для недавней конференции (примеры можно загрузить с веб-узла scriptinganswers.com/essentials/index.php/2008/03/25/techmentor-2008-san-francisco-auditing-examples).
Один пример предназначен для перечисления нескольких свойств класса Win32_UserAccount в инструментарии WMI с нескольких компьютеров. Если имена компьютеров перечислены в файле C:\Computers.txt, выполнить задачу позволит следующая простая команда:
Gwmi win32_useraccount –comp (gc c:\computers.txt)
Проблема состоит в том, что в классе Win32_UserAccount отсутствует свойство, определяющее, с какого компьютера пришел данный экземпляр, поэтому полученный список будет бесполезной кучей учетных записей множества компьютеров. Я разрешил проблему путем создания нового пользовательского объекта, включающего имя исходного компьютера, и выбора нужных свойств класса. Код этого пользовательского объекта показан на рис. 2.
Рис. 2. Использование пользовательского объекта для сбора имен компьютеров и выбора свойств
function Get-UserInventory {
PROCESS {
# $_ is a computer name
$users = gwmi win32_useraccount -ComputerName $_
foreach ($user in $users) {
$obj = New-Object
$obj | Add-Member NoteProperty Computer $_
$obj | Add-Member NotePropertyPasswordExpires ($user.PasswordExpires)
$obj | Add-Member NoteProperty Disabled ($user.Disabled)
$obj | Add-Member NotePropertyFullName ($user.FullName)
$obj | Add-Member NoteProperty Lockout ($user.Lockout)
$obj | Add-Member NoteProperty Name ($user.Name)
$obj | Add-Member NotePropertyPasswordRequired ($user.PasswordRequired)
$obj | Add-Member NotePropertyPasswordChangeable ($user.PasswordChangeable)
}
}
}
Get-Content c:\computers.txt |
Get-UserInventory |
where { $_.PasswordRequired -eq $False } |
selectComputer,Name |
Export-Csv c:\BasUsersBad.csv
Последняя строка команды передает все имена компьютеров функции фильтрации, которая создает пользовательские объекты. Далее я отфильтровал всех пользователей, кроме тех, чье свойство PasswordRequired имеет значение False (идея заключаются в создании отчета аудита проблемных учетных записей). После этого я просто сохранил свойства Computer и Name, чтобы итоговый отчет был списком имен компьютеров и учетных записей, требующих особого внимания из-за паролей с неограниченным сроком действия. Функция фильтрации делает этот отчет для нескольких компьютеров возможным, поскольку добавляет имя исходного компьютера к результату, ограничивая свойства только необходимыми.
Хотя существуют и другие сходные пути выполнения этой задачи, данный подход, вероятно, является самым простым. Кроме того, он демонстрирует основные концепции и методы.
Оставьте время для игр
Даже если вы успешно работаете с конвейером, еще есть то, что необходимо изучить. Мышление в терминах физических объектов поможет лучше понять выполняемые действия.
Поэтому в следующий раз, когда вы столкнетесь с трудностями в концепции Windows PowerShell, попробуйте абстрагироваться от компьютера и воспроизвести задачу, используя в качестве иллюстрации повседневные предметы. Я скромно рекомендую держать для этого под рукой небольшой пакет с шарами для настольного тенниса (или кубиками, если вы сможете их найти).
Командлет месяца: Out-File
Сколько раз вы перенаправляли выходные данные командлета в файл с помощью символа >? Примерно вот так: Get-Process > Processes.txt. Знали ли вы, что в действительно вы используете скрытый командлет Out-File? Вот команда, выполняющая точно такую же функцию: Get-Process | Out-File Processes.txt.
Конечно, для этого требуется больше ввода с клавиатуры, зачем же утруждать себя вводом полного командлета Out-File? Одна из причин состоит в том, что командлет Out-File более ясен при чтении. Кто либо, обучающийся, скажем, шесть месяцев, может посмотреть на один из ваших сценариев и спросить, что означает символ > (это, в конце концов, устаревшее мышление).
Командлет Out-File, напротив, делает довольно очевидным то, что будет файл будет создан и записан. Более того, Out-File предоставляет параметр -append (почти то же самое, что >>), а также параметры -force и -noClobber, позволяющие контролировать перезапись существующих файлов. Наконец, ввод Out-File обеспечивает доступ к параметру -whatif. Этот удобный параметр позволяет просмотреть результаты работы командлета Out-File без действительного выполнения самой работы! Это отличный способ безопасной проверки сложной командной строки.