Оболочку на прокачку: советы по оптимизации команд и скриптов Powershell 2.0PowerShell — очень удобный инструмент администратора, предоставляющий практически безграничные возможности по настройке серверов, виртуальных машин, а также сбору информации об их состоянии. Он достаточно прост: можно быстро писать скрипты, не вникая в детали. Но как и любой язык программирования, PS имеет свои тонкости и нюансы, не владея которыми, нельзя эффективно его использовать. Берем только нужноеКомандная оболочка оперирует множеством параметров объектов, к отбору которых необходимо подходить взвешенно, определяясь с их дальнейшей необходимостью. Ведь каждый вызванный объект увеличивает количество памяти, требуемое для его хранения. Если взять больше, то в определенный момент получим ошибку "System. OutOfMemoryException". То есть сначала извлечь все параметры объекта и ненужные объекты, а затем отфильтровать то, что действительно необходимо, — плохая идея. Использование лишних выборок существенно увеличивает время исполнения скрипта и повышает требования к системным ресурсам. Лучше сразу взять то, что планируется обрабатывать, или выводить дальше. Для примера проверь время исполнения двух команд:
Вторая выполнится примерно в два раза быстрее, а полученный результат будет одинаков. В больших скриптах при большом количестве данных разница в скорости выполнения будет весьма ощутимой. Теперь ситуация, которая не менее редка в сценариях PowerShell. Есть список объектов, и нужно произвести с ними некоторые действия.
Этот пример можно переписать несколько иначе:
В первом случае мы вначале присваиваем значение переменной, а затем считываем. Использование каналов (pipelines, "|") и командлета ForEach-Object во втором примере позволит избежать избыточного хранения большого количества данных, так как они будут обрабатываться сразу, по мере поступления. В итоге вторая команда выполнится быстрее, а ресурсов потребует меньше. При работе с командлетами ActiveDirectory не забываем импортировать нужный модуль:
Аналогичная ситуация, только не используется явно заданная переменная: Здесь все равно вначале извлекаются все команды, которые сохраняются в переменной (что загружена в память) и затем последовательно выполняются элементы. Однако время работы команды и затраты ресурсов будут все же на порядок больше, чем при использовании каналов. Но не все так гладко. На простых примерах можно прийти к выводу, что от использования оператора foreach лучше отказаться, на самом деле внутренняя оптимизация PS иногда приводит к тому, что в операциях чтения foreach показывает лучшую производительность. Кроме этого, foreach предпочтителен, если объект уже имеется в памяти, например сохранен в переменной, то есть нет нужды его извлекать, а надо просто обработать. В некоторых случаях необходимо получить некоторые свойства и обработать их дважды, но по-разному, или сохранить в файл и просмотреть в консоли. Можно конечно, вызвать команду дважды (будь внимателен, например Get-Process, вызванный дважды, покажет разный результат), или сохранить значение в переменной. Но в PS есть еще одна интересная возможность: направить вывод в два потока. Для этой цели используется командлет TeeObject. Например, получаем список процессов, сохраняем в файл и выводем на консоль: Так как второй получатель не указан, то вывод данных производится на консоль. При желании можно обработать данные любым удобным способом:
Чтобы сохранить второй поток в файл, используем командлет Out-File:
В качестве входного параметра командлет Tee-Object может принимать другой объект, на который следует указать при помощи ключа "-inputObject". Читаем файлыДля чтения или разбора файлов используются командлеты GetContent, Select-String, которые проходят файл построчно и возвращают объект. С файлами большого размера могут быть проблемы, но их легко решить, используя дополнительные параметры. Например, в Get-Content можно указать количество строк, считываемых за раз и передаваемых далее по конвейеру (по умолчанию все). Например, по 100 строк:
Соответственно увеличение этого числа ускоряет процесс чтения, но и увеличивает необходимые объемы памяти. Причем при использовании Read, скорее всего, потребуется вставка конвейера "| ForEach-Object ($_) |", чтобы в последующем возможно было обработать всю запись. К слову, команда:
выполнится примерно в 3 раза быстрее, чем:
Командлет Get-Content лишь читает файлы, остальная обработка отдана на откуп другим командлетам. Например, Select-String может читать файлы или брать данные из канала, отбирая информацию по шаблону. Например, переберем все скрипты PS в текущем каталоге в поисках подстроки «PowerShell»:
Для примера просмотри вывод, казалось бы, подобной команды: Главное отличие — отсутствие имени файла в выводе результата при использовании where, ведь в этом случае выводятся только совпадения, а не объекты. По умолчанию ищутся все вхождения образца, но в некоторых случаях необходимо найти все строки, где образец отсутствует. Например, вместо того, чтобы искать все сообщения об ошибках, предупреждения (Warning, Failed и т.п.), проще убрать из вывода Success. При помощи дополнительного параметра Select-String "–notMatch" это сделать проще, а скрипт будет работать быстрее: Одна строчка в выводе часто не дает достаточно информации о событии, здесь на помощь приходит параметр "–context", который позволяет получить необходимое количество строк до и после совпадения. Например, выведем две строки из журналов до и после события со статусом Failed: По умолчанию поиск регистронезависим, чтобы научить Select-String понимать регистр, используем "-caseSensitive". В некоторых обзорах, в том числе и написанных сертифицированными Microsoft, командлет SelectString часто сравнивается с юниксовыми утилитами grep/egrep. Благо, реализаций grep для Windows сегодня более чем предостаточно: GnuWin32 (gnuwin32.sf.net), Windows grep (wingrep.com), GNU Grep For Windows (steve.org.uk/Software/grep), два варианта Grep For Windows (grepforwindows.com, pages.interlog.com/~tcharron/grep.html) и многие другие. По результатам прогонов grep существенно выигрывает по скорости выполнения у Select-String.
Но при его использовании дальнейшую обработку данных необходимо производить самостоятельно, ведь на выходе мы получаем «сырые» данные, а не объекты .Net. Если же в этом нет необходимости, то вполне достаточно использовать и grep. Напомню, что в Windows есть утилита findstr.exe, позволяющая находить нужные строки в файлах, но ее функционал жутко урезан, поэтому использование grep предпочтительнее. В контексте чтения файлов стоит вспомнить и о массивах. В PS вообще упрощена работа с переменными, строками, массивами и хеш-таблицами, нужный тип присваивается автоматически (проверяется GetType(). FullName), размер устанавливается динамически. В итоге работать с ними удобно, нет необходимости в дополнительных проверках. Проверяем параметры массива:
Для примера код:
выполнится более чем в 10 раз быстрее, по сравнению с:
Выражаемся регулярноВ PS реализован механизм Perl-подобных регулярных выражений, что позволяет при необходимости легко найти иголку в стоге сена. Если быть точнее, то PS является оболочкой и использует все, что заложено в технологии .NET Framework (класс System.Text.RegularExpressions.Regex). Для поиска совпадения используется параметр "-match" и его варианты "–cmatch" (case-sensitive, регистрозависимый) и "-imatch" (case insensitive, регистронезависимый). Например, нам нужен список IP-адресов, полученных при помощи ipconfig. Проще простого:
В качестве параметра в PS принимается регулярное выражение, которое может содержать все принятые знаки — *, ?, +, \w, \s, \d и так далее. Проверим правильность почтового адреса:
Теперь, если почтовый адрес не принадлежит домену synack.ru и не попадает под шаблон (то есть содержит запрещенные знаки), то пользователь получит сообщение об ошибке (о командлете Write-Error читай в мини-статье «Форматируем вывод»). Не буду останавливаться на подробном разборе и перечислении всех возможных параметров, используемых в регулярных выражениях, это достаточно емкая тема (см. статью «Регулярные выражения Perl» www.xakep.ru/post/19474). Кстати, на сегодня доступны специальные утилиты, помогающие составлять регулярное выражение под требуемую оболочку. Например, RegexBuddy (regexbuddy.com/powershell.html) или RegexMagic (regexmagic.com). Кроме поиска совпадения, в PS реализована еще одна ценная возможность — замена содержимого по шаблону, для чего используется оператор «-replace» (а также «-ireplace» и «-creplace»). Шаблон для замены выглядит так: Например, прочитаем файл и заменим все строки "Warning", на "!!!Warning":
Если второй параметр не указан, совпавшая запись будет просто удалена. Как и в Perl, захваченные в первой части выражения символы сохраняются в специальных переменных, которые могут быть использованы при замене. Так $0 соответствует всему совпавшему тексту, $1 — первое совпадение, $2 — второе и так далее. То есть предыдущее выражение можно переписать так:
Цигиль-цигильТеперь, собственно, как и при помощи чего проверять эффективность написанного скрипта. Разработчики Microsoft не стали усложнять нам жизнь, и в PS из коробки включен специальный командлет Measure-Command, позволяющий замерить время выполнения команды или скрипта. Из командной строки PS реализован прямой доступ к командам оболочки CMD, объектам COM, WMI и .NET, поэтому очень удобно определять разницу во времени исполнения самых разных утилит. Просто вводим запрос в строке приглашения. Для примера произведем два замера: Отсюда видно, что нэйтивные команды в PS выполняются значительно быстрее. ЗаключениеPowerShell сделан очень удобным и функциональным, на нем просто писать скрипты, выбирать и форматировать данные. Но за удобство приходится расплачиваться медлительностью написанных скриптов. Надеюсь, приведенные советы по оптимизации помогут тебе в изучении этой оболочки. Links
Форматируем выводПри большом количестве данных их визуальный анализ становится затруднительным: например, очень трудно найти сообщения об ошибке во множестве записей. Но в PS довольно просто выделить вывод цветом, что удобнее для восприятия. Для этого используется командлет Write-Host, которому в качестве параметра передаем два параметра: В результате получим список процессов на белом фоне зелеными буквами. К сожалению, Write-Host никак не различает вывод других утилит. То есть если в выводе другого командлета присутствует, например Error, о цветовой раскраске такого сообщения необходимо позаботиться самостоятельно. Работа с журналами сообщенийПосле настройки систем и сервисов роль админа сводится к наблюдению за их правильной работой и отслеживанию текущих параметров. В PS заложен целый ряд *-Eventlog командлетов, позволяющих легко считать записи в журнале событий как на локальной, так и удаленной системе. Данные легко сортируются и отбираются по нужным критериям. Например, чтобы вывести только последние события из журнала безопасности на двух компьютерах, используем параметр "Nevest" с указанием числового аргумента: Теперь выведем только события, имеющие определенный статус: А вот так можно собрать все данные об успешной регистрации пользователей (события с EventID=4624):
Логокопатель WindowsВ PS v2.0 CTP3 появился командлет Get-WinEvent, который в некоторых случаях предоставляет более удобный формат доступа к данным. Получим список провайдеров, отвечающих за обновления: В зависимости от установленных ролей и компонентов, список будет разным, но нас интересует провайдер для Windows Update. Теперь смотрим установленные обновления:
В итоге мы можем достаточно просто получить любую информацию о состоянии системы. |