В прошлом месяце я показывал несколько способов использования Windows PowerShell для незамедлительного решения администраторских задач без написания каких бы то ни было сценариев. Но хоть Windows PowerShell и превосходна в роли интерактивной оболочки, выжать из неё максимум и автоматизировать более сложные задачи можно только с использованием простого, но мощного языка сценариев.
Вы можете задаться вопросом – а нужен ли был Майкрософт ещё один язык сценариев? В конце концов, она уже создала KiXtart, процессор сценариев входа, да и Visual Basic® Scripting Edition (VBScript). Ответ, однако же, "да". Майкрософт был нужен ещё один язык сценариев. Я объясню почему.
Язык Windows PowerShell™ должен был быть простым и интуитивно понятным, так, чтобы администраторы могли овладеть им без глубокого обучения. Он также должен был быть достаточно гибким, чтобы вобрать всю мощную функциональность, которую Windows PowerShell предоставляет пользователю.
Поскольку Windows PowerShell основана на Microsoft® .NET Framework, синтаксис её сценариев должен был быть совместим с .NET. Создавая этот язык, разработчики остановились на синтаксисе, в целом представляющим собой очень упрощённый C# (произносится "си-шарп"; один из языков, поставляемых с .NET Framework). Почему не оставить синтаксис, подобный VBScript? Отличия языка Windows PowerShell от VBScript не настолько уж велики, но при этом PowerShell даёт определённый толчок в изучении программирования под .NET Framework благодаря синтаксису, напоминающему C#. Если вы решите перейти на Visual Studio® и начать создавать приложения на C#, полученное благодаря сценариям Windows PowerShell знание синтаксиса вам пригодится.
Одна из самых важных частей языка сценариев Windows PowerShell — да и любого другого языка сценариев — его конструкции. Это элементы языка, позволяющие выполнить логическое сравнение и предпринять различные действия в зависимости от его результата, или дающие возможность повторять одну или несколько команд снова и снова.
Размышляя логически
Логические сравнения лежат в основе многих языков сценариев, и Windows PowerShell не исключение. При любом сравнении оцениваются два значения или объекта и определяется результат - True (истина) или False (ложь). К примеру, можно задаться вопросом: "Срок действия пароля этого пользователя истекает сегодня?". Результатом будет либо True, если две даты совпадают, либо False, если они различны. Я пишу True и False с большой буквы, поскольку это термины Windows PowerShell.
Вот небольшой пример логического сравнения, которое можно предпринять при помощи Windows PowerShell:
PS C:\> $a = 1
PS C:\> $b = 2
PS C:\> $a -eq $b
False
Здесь я создаю переменную с именем $a и присваиваю ей значение 1. В Windows PowerShell имена переменных всегда начинаются со знака доллара, так что их легко отличить. Знак "=" называется оператором присваивания, поскольку используется для присваивания значения. Затем я создаю вторую переменную, $b, и присваиваю ей значение 2. А затем происходит и само логическое сравнение — я прошу Windows PowerShell сравнить содержимое $a и $b при помощи оператора равенства -eq. PowerShell производит сравнение, определяет, что значения не равны и отображает результат: False.
Операторы в Windows PowerShell несколько отличаются от операторов других языков сценариев и даже от операторов C#. В большинстве языков оператор "=" не только присваивает значения, но и выполняет сравнение на равенство. В Windows PowerShell эта неоднозначность устранена введением отдельного оператора для каждого действия. На рисунке 1 показаны операторы сравнения Windows PowerShell и соответствующие им операторы из других языков, с которыми вы можете быть уже знакомы (например VBScript).
У операторов сравнения есть и другая интересная особенность. Взгляните на этот пример:
PS C:\> $a = "TechNet"
PS C:\> $b = "technet"
PS C:\> $a -eq $b
True
По умолчанию операторы сравнения нечувствительны к регистру, так что содержащая заглавные буквы строка "TechNet" оказывается равна "technet". Это весьма удобно, потому как в большинстве администраторских задач регистр букв не имеет значения. Но когда это не так, можно произвести и чувствительное к регистру сравнение, добавив букву "c" к оператору:
PS C:\> $a -ceq $b
False
Если вы забудете, какой тип сравнения используется по умолчанию, можно по тому же принципу добавить к оператору букву "i", тем самым явно указав нечувствительность к регистру:
PS C:\> $a -ieq $b
True
Всё, что нужно запомнить — это что операторы сравнения всегда возвращают одно из двух значений: True или False.
Принимая решения
Узнав, как составлять логические выражения, можно начать использовать их в конструкциях. Первая из них позволяет Windows PowerShell принять решение, основываясь на сравнении. Она называется конструкцией If, и у неё есть несколько вариантов. Вот простейший:
PS C:\> if ($a -eq $b) {
>> Write-Host "They are equal"
>> }
>>
They are equal
Здесь стоит отметить несколько интересных моментов. Прежде всего, переменные $a и $b всё ещё содержат значения "TechNet" и "technet" соответственно. Конструкция начинается с ключевого слова If. За ней, в скобках, идёт логическое сравнение, которое мне надо было произвести. Затем фигурная скобка, обозначающая начало "условного кода" — кода, который будет выполнен, если результатом сравнения будет True. Из предыдущего примера вы знаете, что это сравнение возвращает True, так что условный код очевидно будет выполнен. Печатаю свой условный код, Write-Host "They are equal", и нажимаю клавишу ВВОД. Наконец, закрывающая фигурная скобка отмечает конец секции с условным кодом. После неё нажимаю клавишу ВВОД дважды. Второе нажатие, на пустой строке, даёт анализатору понять, что я закончил код и хочу выполнить его.
Обратите внимание, что эта конструкция запускается не из файла сценария. Я просто печатал её в командной строке Windows PowerShell. Вот что делает этот продукт уникальным в мире сценариев Windows: сценарии можно создавать как интерактивно, так и помещая их в файл для долговременного хранения.
Как только я ввёл открывающую фигурную скобку и нажал ВВОД, Windows PowerShell отобразила приглашение >>. Так она сообщает: "Я вижу, что вы внутри конструкции, и жду, когда вы начнёте печатать то, что её составляет". После ввода закрывающей скобки и двойного нажатия ВВОД, она выполнила конструкцию, определив, что логическое сравнение возвращает True, и выполнив условный код. Это видно по тому, что "They are equal" появляется на экране прежде чем PowerShell возвращается к обычному приглашению командной строки. Использование Windows PowerShell для интерактивного написания сценариев позволяет быстро протестировать отрывки кода перед их включением в основной сценарий, что упрощает как отладку, так и обучение.
Следует отметить, что оболочка отнюдь не требует каждый раз нажимать клавишу ВВОД. Например, предыдущий сценарий можно переписать так:
PS C:\> if ($a -eq $b) { Write-Host "They are equal" }
They are equal
Поскольку я напечатал всё в одной строке, Windows PowerShell не потребовалось отображать специальное приглашение >>. Конструкция была выполнена, как только я нажал ВВОД в конце строки. Из чего следует, что выполнять её было можно сразу? Из того, что она была завершена — закрывающая фигурная скобка была на месте.
Выше я упомянул, что у конструкции If есть и другие варианты. Вот законченный пример, более характерный для файла сценариев PS1, чем для оболочки:
if ($a -eq $b) {
Write-Host "They are equal"
} elseif ($a -lt $b) {
Write Host "One is less than the other"
} else {
Write Host "One is greater than the other"
}
Конструкция начинается точно так же, с ключевого слова If. Но здесь, если первое сравнение возвращает False, выполняется второе сравнение, обозначенное ключевым словом Elseif. Если же и оно вернёт False, тогда последнее ключевое слово — Else — обеспечит выполнение соответствующего ему кода.
Повторяясь
Windows PowerShell содержит несколько конструкций для выполнения кода до тех пор, пока некоторое условие не станет равным True или False. Программисты называют их циклами. А один из самых полезных циклов позволяет даже перечислять объекты в коллекции и выполнять одну или несколько строк кода для каждого объекта. Он вполне удачно назван foreach, и выглядит следующим образом:
PS C:\> $names = get-content "c:\computers.txt"
PS C:\> foreach ($name in $names) {
>> Write-Host $name
>> }
>>
don-pc
testbed
Всё начинается с обращения к командлету Get-Content с целью получить содержимое созданного мною файла c:\computers.txt, в который я поместил имена компьютеров, по одному на каждую строку. Windows PowerShell трактует каждую строку как объект, а файл, таким образом, представляется коллекцией этих объектов. Сохранена она в переменной $names. С помощью ключевого слова Foreach я перечисляю элементы коллекции $names, используя переменную $name для хранения текущего объекта при каждом выполнении цикла. Код самого цикла находится в фигурных скобках. Каждое имя, взятое из файла, я хочу вывести в командную строку. И, как видно из результата выполнения, следующего за конструкцией, именно это Windows PowerShell и делает. Преимущества для администраторских задач очевидны: можно, например, запросто создать список серверов и использовать Windows PowerShell для получения информации от каждого сервера по очереди.
Примеры из жизни
Составим что-нибудь полезное из логических сравнений и конструкций If и foreach. Я хочу быстро проверить состояние службы сообщений у каждого из группы серверов. На большинстве серверов эта служба должна быть остановлена, так что я не хочу получить список серверов, где она и правда остановлена. Мне нужны только сервера, где служба сообщений запущена, и именно в их отношении я должен что-то предпринять.
Я знаю, что мог бы использовать командлет Get-Service для получения нужной информации с локального компьютера. Но к удалённому компьютеру он получить доступ не может. К счастью, ту же информацию можно получить с помощью WMI (Windows Management Instrumentation — инструментарий управления Windows), используя командлет Get-WMIObject, позволяющий работать с удалённым компьютером. Получается следующий сценарий:
$names = get-content c:\computers.txt
foreach ($name in $names) {
$svc = get-wmiobject win32_service '
-computer $name -filter "name='messenger'"
if ($svc.started -eq $True ) {
write-host "$name has Messenger running"
}
}
Заметили символ ' в конце третьей строки? Он сообщает Windows PowerShell, что следующая строка — продолжение предыдущей. Это удобно в случаях вроде этого, когда вся строка целиком не поместилась бы на журнальной полосе без переноса. Обратите также внимание на сравнение $svc.started с $True в конструкции If. $True — особая переменная в Windows PowerShell, которая представляет логическое значение True. Обратная ей переменная $False представляет логическое False. В целом, я мог бы написать чуть более короткий код:
$names = get-content c:\computers.txt
foreach ($name in $names) {
$svc = get-wmiobject win32_service '
-computer $name -filter "name='messenger'"
if ($svc.started) {
write-host "$name has Messenger running"
}
}
Ведь условие конструкции If должно быть просто True или False. Обычно True или False получаются в результате сравнения двух значений, как в первом варианте сценария. Но раз свойство Started может принимать значения только True или False, нет никакой нужды дополнительно сравнивать его с True или False.
Один полезный инструмент
Таков вот он — простой, удобный инструмент администратора, включающий различные конструкции для выполнения текущих задач. Набираете ли вы сценарий интерактивно в Windows PowerShell или сохраняете его в PS1-файл для последующего использования, вы одинаково удобно можете проверить состояние служб на различных компьютерах, что великолепно демонстрирует пользу конструкций в автоматизации задач администрирования.