Windows PowerShellПоэтапное создание сценариев

Дон Джонс (Don Jones)

В предыдущих статьях я особое внимание обращал на то, что Windows PowerShell — это оболочка. Она создана для интерактивного использования, в отличие от оболочки cmd.exe (или командной строки), с которой вы, возможно, уже знакомы. Но тем не менее Windows PowerShell поддерживает язык сценариев — более функциональный, чем пакетный язык cmd.exe. В то же время

он предоставляет такие же, если не большие, возможности, что и языки, сходные с VBScript. Благодаря тому что Windows PowerShell™ является оболочкой интерактивной, изучение языка сценариев значительно упрощается. Сценарии можно разрабатывать в интерактивном режиме внутри оболочки, что позволяет писать их поэтапно — строку за строкой — и сразу видеть результаты работы.

Подобная техника создания сценариев также упрощает отладку. Поскольку результаты работы сценария видны сразу, его можно быстро исправить, если что-то пошло не так.

В этой статье я приведу пример интерактивного создания сценария в Windows PowerShell. Мы напишем сценарий, считывающий имена служб из текстового файла и устанавливающий для каждой службы режим запуска «Отключено».

На этом примере мне хотелось бы показать, что сценарий в Windows PowerShell можно создавать небольшими частями вместо того чтобы пытаться сразу написать его целиком. Любую административную задачу можно разбить на несколько составляющих и найти отдельное, независимое решение для каждой составляющей. Вы увидите, что Windows PowerShell позволяет без труда объединить эти сценарии в один. Я также намерен показать вам, что таким способом — создавая сценарий по частям — удобнее разрабатывать конечный сценарий.

Считывание имен из файла

Понять, как считывать данные из текстового файла в Windows PowerShell, непросто. Если запустить команду Help *file*, мы получим только справку по командлету Out-File, а он записывает текст в файл, а не считывает его. В справке по этой теме вообще ничего нет. Однако имена командлетов в Windows PowerShell следуют определенным правилам, и это можно взять на вооружение. Если в Windows PowerShell какие-то данные откуда-то считываются, имя командлета обычно начинается с Get. Запустим команду Help Get*, чтобы просмотреть список всех подобных командлетов. В нем мы видим имя Get-Content. Кажется, это то, что нам надо. Теперь запустим команду Help Get-Content и узнаем, для чего предназначен этот командлет (см. рис. 1). Похоже, мы можем использовать его для выполнения поставленной задачи.

Рис. 1. Запуск команды Help Get-Content для получения дополнительной информации

Рис. 1.** Запуск команды Help Get-Content для получения дополнительной информации **(Щелкните изображение, чтобы увеличить его)

Оболочка Windows PowerShell практически любой элемент обрабатывает как объект, и текстовые файлы не являются исключением. С технической точки зрения, текстовый файл — это набор строк; каждая строка представляет собой что-то вроде независимого объекта. То есть, если создать текстовый файл C:\services.txt и записать в него имена служб (каждое имя располагается на отдельной строке), в Windows PowerShell можно будет считывать имена служб по одному при помощи командлета Get-Content. Поскольку цель данного примера — показать, что сценарии можно писать интерактивно, начнем с того, что просто запустим командлет Get-Content, указав имя созданного текстового файла, и посмотрим, что получится в результате.

PS C:\> get-content c:\services.txt
messenger
alerter
PS C:\>

Как и ожидалось, оболочка Windows PowerShell считывает записанные в файле имена и выводит их на экран. Разумеется, просто вывести имена на экран для решения поставленной задачи недостаточно, но зато теперь мы уверены в том, что командлет Get-Content работает так, как мы предполагали.

Изменение службы

Теперь нужно изменить режим запуска службы. Опять же, начнем с того, что найдем нужный командлет. Запускаем команду Help *Service*. Получаем довольно небольшой список. Из всех содержащихся в нем командлетов только Set-Service, похоже, нам подходит. Протестируем его, чтобы понять, как этот командлет работает, прежде чем включать его в сценарий. Запустив команду Help Set-Service, узнаем, как должен работать командлет, и небольшой тест подтверждает полученную информацию.

PS C:\> set-service messenger -startuptype
    disabled
PS C:\>

Объединение частей

Теперь нужно свести воедино способность считывать имена служб из файла и работу командлета Set-Service — тут в дело вступает конвейерная обработка, применяемая в Windows PowerShell. Она позволяет использовать данные, полученные в результате работы одного командлета, в качестве входных данных для другого. По конвейеру передаются объекты целиком. Если на конвейер поступает набор объектов, каждый из них проходит конвейерную обработку независимо от остальных объектов. Это означает, что данные, полученные в результате работы командлета Get-Content — они, как мы помним, представляют собой набор объектов — могут быть поданы на вход командлета Set-Service. Поскольку командлет Get-Content передает набор объектов, каждый объект — строка текста — поступает на вход командлета Set-Service независимо от остальных. В результате командлет Set-Service выполняется для каждой строки нашего текстового файла. Команда выглядит следующим образом.

PS C:\> get-content c:\services.txt | 
 set-service -startuptype disabled
PS C:\>

При выполнении команды происходит следующее:

  1. Выполняется командлет Get-Content, считываются все данные из файла. Каждая строка файла воспринимается как отдельный объект, а все строки вместе представляют собой набор объектов.
  2. Этот набор объектов передается командлету Set-Service.
  3. Конвейер запускает командлет Set-Service для каждого объекта, поступившего на вход. При каждом запуске командлета Set-Service объект, поступивший на вход, используется в качестве первого параметра командлета, то есть имени службы.
  4. Выполняется командлет Set-Service. В качестве первого параметра в нем используется объект, поступивший на вход, а также учитываются все остальные указанные параметры — в данном случае это параметр startuptype.

Надо отметить, что поставленная задача выполнена, хотя полноценный сценарий так и не написан. Сделать то же самое в оболочке Cmd.exe было бы непросто, а код на языке VBScript содержал бы десяток строк. В оболочке же Windows PowerShell для решения этой задачи требуется всего лишь одна строка. Работа, конечно, еще не закончена. Если вы обратили внимание, при выполнении созданной команды мы не получаем никакого отчета о состоянии выполнения задачи. Трудно понять, было ли вообще что-то сделано — мы только видим, что ошибок не возникло. Но теперь, изучив необходимые функции, мы можем обратить внимание на внешнюю сторону процесса и перейти собственно к созданию сценария.

Windows PowerShell Prompt Here от Майкла Мерголо

Один из популярных инструментов Microsoft® PowerToys для Windows® (и один из моих любимых) — средство Open Command Window Here. Оно входит в набор инструментов Microsoft PowerToys для Windows XP и в набор Windows Server® 2003 Resource Kit Tools. Если оно установлено, можно, щелкнув правой кнопкой мыши папку или диск в проводнике Windows, открыть окно команд, причем данная папка или диск оказываются текущими.

Пока я изучал Windows PowerShell, мне пришло в голову, что хорошо бы иметь подобную функцию и для этой оболочки. Я взял файл INF из программы установки Open Command Window Here (из набора Windows Server 2003 Resource Kit Tools) — cmdhere.inf — и изменил его. В результате получилось контекстное меню Windows PowerShell Prompt Here. Файл INF для него можно найти в соответствующей записи в блоге, на основании которой и был написан материал для этой врезки (ее адрес: leeholmes.com/blog/PowerShellPromptHerePowerToy.aspx). Чтобы установить средство, щелкните правой кнопкой мыши файл INF и выберите «Установить».

Когда я создавал версию этого средства для Windows PowerShell, я обнаружил, что в исходном файле есть ошибка: при удалении средства Open Command Window Here в контекстном меню остается неработающий пункт. Я исправил исходный файл cmdhere.inf, его обновленную версию можно найти в той же записи в блоге.

Преимуществом для обоих упомянутых средств PowerToys является то, что эти пункты меню настраиваются в разделах реестра, связанных с типами объектов Directory и Drive. Выполняется настройка так же, как и настройка пунктов контекстного меню для типов файлов. Например, если щелкнуть правой кнопкой мыши файл TXT в проводнике Windows, в контекстном меню, в самом верху, вы увидите несколько пунктов: «Открыть», «Печать», «Изменить». Чтобы понять, как настраиваются эти пункты меню, взглянем на куст реестра HKEY_CLASSES_ROOT.

Откройте редактор реестра и раскройте куст HKEY_CLASSES_ROOT. В нем содержатся разделы с именами, соответствующими типам файлов, например DOC, TXT и т. п. Щелкните раздел .txt. В нем в качестве значения по умолчанию указано txtfile (см. рис. A). Это тип объекта, связанный с файлом TXT. Прокрутите список разделов и раскройте раздел txtfile, в нем раскройте раздел shell — имена разделов в нем частично совпадают с названиями пунктов контекстного меню для файлов TXT. (Некоторых пунктов меню в этом списке нет, поскольку существуют и другие способы создания контекстных меню.) В каждом из разделов есть раздел command. Указанное в нем значение по умолчанию — это команда, которую Windows выполняет при выборе соответствующего пункта в контекстном меню.

Точно таким же образом в средствах вызова командной строки и Windows PowerShell настраиваются пункты контекстного меню Open Command Window Here и Windows PowerShell Prompt Here. С дисками и каталогами типы файлов не связаны, но для данных объектов в кусте реестра HKEY_CLASSES_ROOT есть соответствующие разделы Drive и Directory.

Рис. A. Пункты контекстного меню настраиваются в реестре

Рис. A.** Пункты контекстного меню настраиваются в реестре **(Щелкните изображение, чтобы увеличить его)

Майкл Мерголо (Michael Murgolo) — старший консультант по инфраструктуре в консалтинговой службе корпорации Майкрософт. Он специализируется на операционных системах, развертывании, сетевых службах, Active Directory, управлении системами, автоматизации и управлении обновлениями.

Интерактивное создание сценариев

Нам нужно, чтобы для каждой службы, указанной в файле C:\services.txt, выполнялось несколько командлетов. При этом будет выводиться имя службы и прочая информация, что позволит нам отслеживать ход выполнения сценария.

Прежде объекты передавались от одного командлета к другому при помощи конвейерной обработки. Но на этот раз будет использован способ, более напоминающий обычный сценарий — конструкция Foreach (я рассказывал о ней в предыдущем выпуске журнала). Конструкция Foreach может применяться к набору объектов. Для каждого объекта в наборе выполняется несколько командлетов. В конструкции назначается переменная, которой присваивается значение текущего объекта при каждом повторении цикла. К примеру, конструкция может начинаться так.

foreach ($service in get-content c:\services.txt)

Для считывания данных из текстового файла мы используем все тот же командлет Get-Content. При помощи конструкции Foreach мы обходим весь набор объектов, полученный в результате работы командлета Get-Content. Цикл выполняется один раз для каждого объекта, причем текущий объект помещается в переменную $service. Осталось только указать код, который должен выполняться в цикле. Для начала попробуем просто продублировать составленную нами команду, состоящую из одной строки (так мы создадим самый простой сценарий и убедимся, что он действует точно так же как и предыдущая наша команда).

PS C:\> foreach ($service in get-content c:\services.txt) {
>> set-service $service -startuptype disabled
>> }
>>
PS C:\>

Здесь мы видим кое-что необычное. Обратите внимание: первая строка конструкции Foreach заканчивается открывающей фигурной скобкой. Все, что пишется в фигурных скобках, считается телом цикла и выполняется один раз для каждого объекта из набора на входе. После того как мы ввели открывающую скобку и нажали клавишу ВВОД, оболочка Windows PowerShell изменила строку приглашения на >> (см. рис. 2). Это означает, что мы начали вводить некую конструкцию и оболочка ожидает ее завершения. Затем мы ввели командлет Set-Service. Теперь в качестве первого параметра мы использовали переменную $service, поскольку ей присваивается текущее имя службы, считанное из текстового файла. В следующей строке мы ввели закрывающую фигурную скобку, завершив тем самым конструкцию. После того как мы нажали клавишу ВВОД дважды, оболочка Windows PowerShell выполнила код.

Рис. 2. Оболочка Windows PowerShell показывает, что начата конструкция

Рис. 2.** Оболочка Windows PowerShell показывает, что начата конструкция **(Щелкните изображение, чтобы увеличить его)

Код на самом деле выполняется сразу же. Введенный код похож на сценарий, но в действительности Windows PowerShell работает с ним «вживую», ни в каком текстовом файле код не сохраняется. Теперь введем все то же самое еще раз, но добавим строку кода, выводящую имя текущей службы на экран.

PS C:\> foreach ($service in get-content c:\services.txt) {
>> set-service $service -startuptype disabled
>> "Disabling $service"
>> }
>>
Disabling messenger
Disabling alerter
PS C:\>

Обратите внимание: то, что нужно вывести на экран, заключено в двойные кавычки. Кавычки в Windows PowerShell указывают на то, что определенная часть кода является строкой текста, а не еще одной командой. Когда используются именно двойные, а не одинарные кавычки, Windows PowerShell проверяет заключенную в них строку текста на предмет наличия в ней переменных. Если переменная обнаруживается, вместо нее подставляется фактическое значение, поэтому при выполнении кода на экран выводится имя текущей службы.

Но и этот код еще не является сценарием.

До сих пор оболочка Windows PowerShell использовалась интерактивно — это отличный способ проверить результаты нашей работы. Однако в определенный момент набирать все строки кода заново надоедает. Именно поэтому Windows PowerShell также может выполнять сценарии. В действительности, оболочка обращается со сценарием как с самым обыкновенным текстом: она, грубо говоря, просто считывает текст из файла сценария и построчно его «вводит» — точно так же, как если бы мы вводили строки кода вручную. Из этого следует, что все, что мы уже написали, можно вставить в файл сценария. Создадим в «Блокноте» файл сценария disableservices.ps1 и вставим в него следующие строки.

foreach ($service in get-content c:\services.txt) {
 set-service $service -startuptype disabled
 "Disabling $service"
 }

Сохраним файл в папке C:\test. Теперь попробуем запустить его в Windows PowerShell.

PS C:\test> disableservices
'disableservices' is not recognized as a cmdlet, function, operable program, or
<script file.
At line:1 char:15
+ disableservices <<<<
PS C:\test>

Так. В чем ошибка? В оболочке Windows PowerShell мы находимся в папке C:\test, но сценарий в ней оболочка не обнаруживает. Почему? Из-за ограничений системы безопасности Windows PowerShell не выполняет сценарии непосредственно из текущей папки: это сделано для того, чтобы предотвратить возможность подмены команд операционной системы. Скажем, нельзя создать сценарий dir.ps1 и заставить оболочку выполнять его вместо стандартной команды dir. Чтобы запустить сценарий из текущей папки, нужно указать относительный путь.

PS C:\test> ./disableservices
The file C:\test\disableservices.ps1 cannot be loaded. 
The execution of scripts is disabled on this system. 
Please see "get-help about_signing" for more details.
At line:1 char:17
+ ./disableservices <<<<
PS C:\test>

Что дальше? Сценарий по-прежнему не запускается. Путь указан правильно, однако Windows PowerShell выдает сообщение о том, что сценарии в ней не могут выполняться. Дело в том, что по умолчанию выполнение сценариев в Windows PowerShell отключено. Это еще одна мера системы безопасности, предупреждающая возникновение проблем, с которыми многие из нас сталкивались при использовании VBScript. Вредоносные сценарии не могут запускаться в оболочке Windows PowerShell, потому что по умолчанию сценарии в ней вообще не могут запускаться. Чтобы запустить сценарий, нужно в явном виде изменить политику выполнения.

PS C:\test> set-executionpolicy remotesigned

Политика выполнения RemoteSigned позволяет запускать сценарии с локального компьютера без подписи — для запуска загружаемых сценариев подпись требуется. Лучше использовать политику AllSigned, которая позволяет запускать только сценарии, подписанные с использованием сертификата, выданного доверенным издателем, но у нас под рукой нет сертификата, а без него невозможно подписать сценарий, поэтому в данной ситуации правильнее всего выбрать политику RemoteSigned. Теперь еще раз попробуем запустить сценарий.

PS C:\test> ./disableservices
Disabling messenger
Disabling alerter
PS C:\test>

Нужно отметить, что политика выполнения RemoteSigned — хороший, но не превосходный выбор. Есть более удачное решение. С точки зрения безопасности было бы лучше получить сертификат для подписи сценариев, подписать созданный сценарий в оболочке Windows PowerShell при помощи командлета Set-AuthenticodeSignature и установить политику выполнения AllSigned, являющуюся гораздо более надежной.

Существует еще одна политика выполнения — Unrestricted — использования которой следует избегать. Эта политика позволяет беспрепятственно выполнять на компьютере любые сценарии, даже вредоносные сценарии, запускаемые удаленно, что подвергает большому риску безопасность вашей системы, поэтому я рекомендую не использовать политику Unrestricted ни при каких обстоятельствах.

Незамедлительная проверка результатов

Возможность интерактивного создания сценариев в Windows PowerShell позволяет быстро создавать прототипы сценариев и даже просто их небольшие составляющие. Результаты работы можно сразу же проверить, благодаря чему вы можете легко оптимизировать сценарий так, чтобы получить именно тот результат, который вам нужен. Завершив оптимизацию, вы можете перенести код в файл PS1 и сохранить его для удобства использования в будущем. И помните: лучше всего подписывать файлы PS1, чтобы можно было установить политику выполнения AllSigned в оболочке Windows PowerShell — наиболее безопасную политику выполнения сценариев.

Дон Джонс (Don Jones) — ведущий программист SAPIEN Technologies, соавтор книги Windows PowerShell: TFM (см. www.SAPIENPress.com). Ему можно написать по адресу don@sapien.com.

© 2008 Корпорация Майкрософт и компания CMP Media, LLC. Все права защищены; полное или частичное воспроизведение без разрешения запрещено.