В статье «Команды PowerShell для DNS», опубликованной в предыдущем номере журнала, я обещал рассказать о важной команде ForEach-Object. Однако это обширная тема, и в качестве вводной части давайте сначала поговорим о «конвейере» Powershell. Тема конвейера уже затрагивалась в моих статьях, но она требует более тщательного изучения. Чтобы оценить возможности конвейера, рассмотрим команду Where-Object.
Where-Object имеет псевдонимы where и «?» (вопросительный знак). Эта команда представляет собой универсальный фильтр, применимый практически к каждой get-команде и позволяющий извлекать заданное подмножество из выходных данных. Предположим, нам требуется найти все отключенные учетные записи в домене bigfirm.com. Как мы уже знаем, объект пользователя Active Directory (AD) имеет множество встроенных свойств, в частности, свойство Enabled («Включен») логического типа (то есть принимающее только два значения – $true или $false). Мы знаем, что вывести список отключенных пользователей, то есть пользователей, чье свойство Enabled имеет значение $false, можно с помощью запроса
get-aduser -filter {Enabled -eq $false}
У get-aduser имеется встроенный фильтр. Однако не каждая get-команда имеет собственные возможности фильтрации выходных данных. Например, существует команда get-process, которая позволяет вывести список активных процессов. Предположим, что требуется вывести только процессы, у которых открыто свыше 1500 дескрипторов. Чтобы получить список свойств get-команды, подадим ее выходные данные по конвейеру на вход get-member (псевдоним gm):
get-process | gm
Результатом будет следующее:
HandleCount Property int HandleCount {get;}
Таким образом, get-process имеет свойство HandleCount, которое представляет собой число открытых дескрипторов данного процесса. Фильтр, однако, отсутствует, поэтому для решения нашей задачи предлагается построить запрос с использованием Where. Синтаксис запроса в наиболее общей форме применения приведен ниже.
get-некоторый объект | where {некоторое выражение, возвращающее значение «true» или «false»}
Более конкретно, в данном случае мы ищем следующее:
get-process | where {значение свойства HandleCount передаваемых по конвейеру процессов > 1500}
Приведенная выше запись иллюстрирует основную идею, но составлена не по строгим правилам синтаксиса PowerShell. Во-первых, в PowerShell для обозначения «больше» используется не символ >, а -gt. Другие операторы сравнения: -ge, -lt, -le, -eq и -ne. Во-вторых, в PowerShell содержимое конвейера обозначается $_. Далее, имена переменных, то есть мест временного хранения сверхоперативных данных в оперативной памяти, должны начинаться с $. В PowerShell есть предопределенные переменные, например $false и $true. Если бы я устанавливал правила, то существовала бы еще переменная по имени $pipeline, автоматически сохраняющая текущее содержимое конвейера. Однако разработчики PowerShell решили назвать такую переменную $_. Таким образом, ближе к формальному синтаксису PowerShell, приведенный выше запрос будет выглядеть так:
get-process | where {(свойство HandleCount содержимого $_) -gt 1500}
В отношении PowerShell применимо определение «объектно-ориентированная система». Это означает, что в то время как многие программы и оболочки воспринимают только простые информационные разряды (например, HandleCount, ProcessName, PagedMemorySize), новые объектно-ориентированные системы, такие как PowerShell, способны понимать комплексные фрагменты данных, называемые объектами. В данном случае get-process порождает объекты типа Process, обладающие определенными свойствами, такими как HandleCount, ProcessName, PagedMemorySize и т.д. Это иерархическая структура, где на верхнем уровне находится объект, а на нижнем – его свойства. Свойства сами по себе могут быть объектами, в чем мы убедимся в ходе дальнейшего изучения PowerShell.
В нашем случае мы обращаемся не ко всему содержимому конвейера ($_), а только к его свойству HandleCount. На «языке объектов» это записывается следующим образом: $_.HandleCount (имя объекта, точка, имя свойства), и наш запрос принимает вид:
get-process | where {$_.HandleCount -gt 1500}
Этот пример иллюстрирует, как с помощью Where и встроенной переменной $_ можно без дополнительного программирования создать фильтр для команды, не имеющей собственного фильтра. Еще один пример: предположим, что у get-aduser также отсутствует рабочий параметр -filter. В таком случае задачу вывода только отключенных учетных записей пользователей можно было бы решить следующим образом:
get-aduser –filter * | where {$_.Enabled –eq $false}
Даже если вас не интересует предстоящий рассказ о ForEach, я гарантирую, что достоинства Where вы оцените.