Эй, сценарист!Работа с прохладцей тоже бывает вознаграждена!

Программисты корпорации Майкрософт

Загрузить исходный код для этой статьи: HeyScriptingGuy2008_06.exe (150KB)

В течение сотен лет люди считали, что тяжкий труд уже сам по себе является наградой; что истинное удовлетворение получается в результате каждодневного честного труда; что истинное счастье приходит от достижения, а не от движения; ну, в общем, вы поняли! Трудись упорно, не покладая рук (вспомните Золушку!), и когда-нибудь ты будешь вознагражден.

Заметим: правда ли, что если ты упорно трудишься, то не только станешь счастливей, но и будешь однажды вознагражден за свои старания? Почему вы задаете подобные вопросы?

Действительно, Золушке крупно повезло: в конце концов, если ты живешь со сварливой мачехой и парочкой ее отвратительных дочерей, у тебя будут все причины трудиться в поте лица. Но что если вы не столь удачливы? Что если у вас нет сварливой мачехи и двух отвратительных сводных сестер? Как вы предполагаете прожить долгие дни в изнурительной, неблагодарной работе? Каким еще образом сможете вы найти истинное счастье?

Ладно, если вы всерьез решили стереть пальцы до костей, программисты корпорации Майкрософт предлагают вам попробовать написать сценарий, который выводит данные в красивом ясном табличном формате, как показано на рис. 1.

Рис. 1 Табличный вывод

Рис. 1** Табличный вывод **(Щелкните изображение, чтобы увеличить его)

Как мы уже сказали, Золушка очень повезло: ей приходилось убирать весь дом снизу доверху и, закончив уборку, забираться в очаг, чтобы возиться там в золе и саже. Но даже Золушка задумалась бы о том, стоит ли писать сценарий, который мог бы отображать данные в табличном формате. Сидеть в золе и саже — это одно. Писать сценарий, который выводил бы данные в табличном формате, — это совсем другое дело.

Заметим: если вы не должны писать этот сценарий, сидя в очаге, это — в значительной степени одно и то же.

Почему же Золушка раздумала бы делать эту работу? Потому что это чертовски трудно. Единственным способом представить данные в табличном формате в VBScript — сделать примерно так.

  • Начните с определения максимального размера каждого столбца. Например, вы захотите отображать название, используя для этого 52 знакоместа.
  • Затем вычислите количество символов во фрагменте данных. Например, отображаемое имя «Adobe LM Service» имеет 16 символов.
  • Если отображаемое имя превосходит 52-знаковый предел, определите количество символов, которое необходимо отрезать от конца строки, чтобы вместить его в отведенное место. Если отображаемое имя не превышает 52 символа, определите, сколько пробелов необходимо добавить к концу строки, чтобы длина строки составила бы точно 52 знака.
  • Повторите эти действия со следующей порцией данных. И со следующей. Ну и так далее.

Если вы помните историю с Золушкой, то, вероятно, знаете, что ваша добрая крестная-фея заявится незванной, превратит мышь в системного администратора (не комментируем, каких трудов это стоит), а затем эта мышь напишет вам этот сценарий. Но, вероятно, вам не следует рассчитывать на это. Скорее всего, вы будете в одиночестве.

Посмотрите на это оптимистично: что ж, вы будете самым удовлетворенным человеком в мире. Это потому, что вы — самый большой труженик на земле. Самый-самый.

Разумеется, некоторые, возможно, захотят выторговать себе возможность не работать столь упорно. Если вы принадлежите к таким людям, возможно, вы подумаете: «Слава Богу, что есть на свеье Сценаристы; они расскажут мне, как превратить мышь в системного администратора, а затем — как заставить эту мышь отформатировать мои выходные данные в табличном виде». Да, но, увы, у нас есть дурные новости: ребята из группы Сценаристов не имеют ни малейшего представления о том, как превратить мышь в системного администратора. (Хотя мы можем превратить системного администратора в мышь, если вам это хоть как-нибудь поможет). Мы не знаем, как вы сможете заставить кого-нибудь писать для вас сценарии, не говоря уже о тех, которые выводят данные в табличной форме.

Но это не важно. Коль скоро вы работаете под Windows® XP или Windows Server® 2003 (а среди вас таких много), вам не нужно иметь мышь, которая писала бы за вас сценарии. (Увы, нам очень жаль, но это не относится к Windows Vista®). Взамен этого, вы можете сделать это сами, особенно не утруждая себя, благодаря объекту Microsoft.CmdLib. Взгляните на пример сценария, показанный на рис. 2.

Figure 2 Создание табличного отображения

Dim arrResultsArray()
i = 0

Set objCmdLib = _
  CreateObject("Microsoft.CmdLib")
Set objCmdLib.ScriptingHost = _
  WScript.Application

arrHeader = Array("Display Name", _
  "State", "Start Mode")
arrMaxLength = Array(52, 12, 12)
strFormat = "Table"
blnPrintHeader = True
arrBlnHide = Array(False, False, False)

strComputer = "."

Set objWMIService = GetObject("winmgmts:\\" _
  & strComputer & "\root\cimv2")

Set colServices = objWMIService.ExecQuery _
  ("Select * FROM Win32_Service")

For Each objService In colServices
  ReDim Preserve arrResultsArray(i)
  arrResultsArray(i) = _
  Array(objService.DisplayName, _
    objService.State,objService.StartMode)
  i = i + 1
Next
objCmdLib.ShowResults arrHeader, _
  arrResultsArray, arrMaxLength, _
  strFormat, blnPrintHeader, arrBlnHide

Если вы заинтересовались, Microsoft.CmdLib – это объект COM, который поставляется в составе Windows XP и Windows Server 2003. Этот объект имеет целый ряд свойств; чтобы получить дополнительные сведения о них, наберите в командной строке cmdlib.wsc /? и прочтите комментарии в файле сценария, который появится на экране. Сейчас нас интересует только одно свойство Microsoft.CmdLib: возможность выводить данные в табличном формате.

Это приводит к очевидному вопросу: как вы отображаете данные в табличном виде? Давайте посмотрим, сможем ли мы объяснить это. Как видно, пример нашего сценария начинается с определения динамического массива, который называется arrResultsArray:

Dim arrResultsArray()

В типовом сценарии данные данные отображаются на экране сразу же после их выборки. Но почему вы считаете, что Сценаристы делают все обычным образом? В этом сценарии мы не собираемся выводить данные сразу после их выборки. Напротив, мы собираемся поместить все извлеченные данные в массив, а затем дать команду, чтобы Microsoft.CmdLib отформатировал и вывел эти данные.

Другими словами, поэтому-то мы и начали с создания динамического массива (то есть массива, размер которого может быть изменен в ходе выполнения сценария). После определения массива мы присваиваем переменной-счетчику i значение 0; мы будем использовать эту переменную для отслеживания текущего размера массива.

Если вам интересно, мы присваиваем переменной i значение 0, потому что первый элемент массива всегда имеет порядковый номер 0. Соответственно, после того как мы добавляем в массив первый элемент, это будет элемент 0, а не элемент 1.

Затем нам нужно инициализировать экземпляр объекта Microsoft.CmdLib; для этого используем вот эти две строки кода:

Set objCmdLib = _
CreateObject("Microsoft.CmdLib")
Set objCmdLib.ScriptingHost = WScript.Application

И это приводит нас к следующему небольшому фрагменту программного кода:

arrHeader = Array("Display Name", "State", "Start Mode")
arrMaxLength = Array(52, 12, 12)
strFormat = "Table"
blnPrintHeader = True
arrBlnHide = Array(False, False, False)

Здесь мы устанавливаем несколько параметров настройки вывода. В первой строке мы указываем значения для массива с именем arrHeader; как вы догадались, это заголовки столбцов нашей таблицы выходных данных. В данном примере сценария мы собираемся извлечь данные о службах, запущенных на компьютере, а также отобразить значения свойств DisplayName, State и Start Mode для каждой службы.

Неудивительно, что мы указали для нашего массива имена столбцов Display Name, State и Start Mode (хотя мы могли бы просто назвать наши столбцы A, B и C; Ларри, Мо и Кудряшка или еще как. Имена столбцов не обязательно должны совпадать с именами свойств).

Заметим: существует множество вариаций истории Золушки, включая такую, в которой названы имена ее сводных сестер. У диснеевской Золушки сводных сестер звали Дризелла и Анастасия, а это — первое и второе имена нашего редактора!

Во второй строке программного кода мы присваиваем значения другому массиву, называемому arrMaxLength. Этот массив содержит размер каждого столбца таблицы. В нашем примере мы хотим использовать 52 знака для столбца 1 (отображаемое имя службы); затем мы хотим отвести по 12 знаков для отображения состояния службы и режима запуска. (Microsoft.CmdLib будет вставлять пробелы между столбцами автоматически). Если нам нужно, чтобы наши столбцы имели размер 52, 12 и 12 символов (а именно это мы и делаем), мы используем код, который выглядит так:

arrMaxLength = Array(52, 12, 12)

Третья строка указывает формат вывода данных. Мы хотим, чтобы данные отображались в виде таблицы; поэтому мы устанавливаем значение strFormat (переменной, которая определяет тип вывода), разумеется, Table. Предположим, что вместо этого мы захотели бы отображать данные в виде списка данных, разделенных запятыми. В этом случае мы бы указали формат как CSV, то есть:

strFormat = "CSV"

В сою очередь, мы могли бы получить выходные данные примерно в таком виде, как показано здесь:

"Display Name","State","Start Mode"
"Adobe LM Service","Stopped","Manual"
"Adobe Active File Monitor V4","Stopped","Manual"
"Alerter","Stopped","Manual"
"Application Layer Gateway Service","Running","Manual"
"Apple Mobile Device","Running","Auto"
"Application Management","Stopped","Manual"

Заметим, что Microsoft.CmdLib не только ставит запятые между выводимыми значениями, но также заключает каждый элемент списка вывода в кавычки. Эта функция гораздо лучше, чем мы могли думать вначале. В конце концов, чтобы проделать это вручную, вам необходимо было бы использовать программу, подобную приведенной ниже:

Wscript.Echo Chr(34) & objService.DisplayName & Chr(34) & "," & Chr(34) & objService.State & Chr(34) & "," & Chr(34) & objService.StartMode & Chr(34)

Ужас.

Что, простите? Вам не нравятся таблицы, но вам также не нравится и формат CSV? Ладно, в таком случае попробуйте установить формат вывода как List, и вы получите на выходе примерно следующее:

Display Name: Adobe LM Service
State:        Stopped
Start Mode:   Manual
Display Name: Adobe Active File Monitor V4
State:        Stopped
Start Mode:   Manual

Но мы отвлеклись. (Что мы склонны делать при малейшей возможности). После определения выходного формата мы присваиваем значение True переменной blnPrintHeader:

blnPrintHeader = True

Мы собираемся использовать blnPrintHeader, чтобы дать указание объекту Microsoft.CmdLib напечатать заголовки столбцов. А что если нам не надо печатать заголовки столбцов? Прекрасно; в этом случае присвойте переменной blnPrintHeader значение False:

blnPrintHeader = False

Наконец, у нас есть эта строка кода:

arrBlnHide = Array(False, False, False)

Когда придет время отображения данных, объект Microsoft.CmdLib даст вам возможность показать или скрыть столбец. Чтобы скрыть столбец (то есть чтобы подавить вывод значений определенного свойства), установите значение True; чтобы отобразить столбец, установите значение False.

В нашем примере вывода мы собираемся отображать значения для свойств DisplayName, State и StartMode, поэтому мы должны указать в нашем массиве значения False, False, False. Что если мы хотим отображать DisplayName, скрыть State и отобразить StartMode? В этом случае мы должны использовать следующую строку кода:

arrBlnHide = Array(False, True, False)

Помните: False используется для отображения столбца, а True — для того, чтобы скрыть его.

С этого места мы готовы выводить данные — или были бы готовы, если бы они у нас были. (Стали бы Сценаристы писать сценарий, который прерывал бы вывод данных, чтобы затем потратить кучу времени, отлаживая этот сценарий, лишь для того, чтобы обнаружить, что они не побеспокоились о том, чтобы сначала извлечь хоть какие-нибудь данные? Ну конечно же нет; и вообще, почему вы это сказали?)

Помня об этом, выполним следующее действие, которое будет заключаться в привязке к службе WMI (Windows Management Instrumentation) на локальном компьютере; а затем используем метод ExecQuery для извлечения сведений обо всех службах, установленных на этом компьютере:

strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")

Set colServices = objWMIService.ExecQuery ("Select * FROM Win32_Service")

Наверное, мы должны упомянуть о том, что вы не ограничены работой только с данными WMI. Как вы могли предположить, Microsoft.CmdLib может работать со всеми типами данных. WMI — это лишь удобный способ получения получения наборов данных.

После получения набора данных о службах мы определяем цикл For Each, который последовательно обрабатывает все службы набора. Тем не менее, вместо вывода значений свойств каждой службы мы используем следующий код:

ReDim Preserve arrResultsArray(i)
arrResultsArray(i) = Array(objService.DisplayName, objService.State,objService.StartMode)
i = i + 1

Что делается в этой части программы? Итак, в строке 1 мы изменяем размер нашего массива, используя команду ReDim Preserve не только для изменения размера массива, но также для предохранения всех существующих данных в этом массиве. (Без ключевого слова Preserve размер массива изменится, но при этом все данные, находящиеся в этом массиве, будут стерты).

Какого размера массив создается? Во время первого выполнения цикла мы создаем массив размера 0; другими словами, мы создаем массив, содержащий один элемент. А как мы будем знать наверняка, что мы создаем массив размером 0? Потому что мы использовали переменнную счетчика i, и значение этой переменной равно 0.

Итак, если мы используем переменную i для отображения размера массива, не значит ли это, что мы всегда будем устанавливать размер, равный 0? Да, это так, за исключением того, что всякий раз, проходя по циклу, мы увеличиваем значение i на 1; как вы видите, это происходит в строке 3 нашего фрагмента кода.

Это заставляет нас беспокоиться только о единственной строке кода:

arrResultsArray(i) = Array(objService.DisplayName, objService.State,objService.StartMode)

Здесь мы просто берем значения интересующих нас свойств – DisplayName, State и StartMode – и добавляем их в массив arrResultsArray. Заметим, что мы не добавляем значения свойств по отдельности; напротив, мы добавляем их как массив значений. Согласитесь, это немного необычно: это означает, что каждый элемент массива arrResultsArray представляет собой другой массив; именно так работает Microsoft.CmdLib.

Конечно, это может заставить вас волноваться снова. «Массив массивов? Как же я смогу получить доступ к отдельной величине в массиве, состоящем из массивов?» Не паникуйте. Это просто: позвольте Microsoft.CmdLib позаботиться об этом за вас.

Обратите внимание: нам очень жаль, но доступ ко всем отдельным данным в массиве массивов — это почти единственное, что Microsoft.CmdLib может сделать для вас. Однако уборка комнаты злой мачехи, стряпня, а также шитье одежды для злобных сводных сестр будут предусмотрены в следующей версии объекта.

Фактически, мы можем выводить наши данные — в ясном табличном формате — просто вызывая метод ShowResults и передавая ему все массивы и переменные, которые мы ранее настроили в этом сценарии:

objCmdLib.ShowResults arrHeader, arrResultsArray, arrMaxLength, strFormat, blnPrintHeader, arrBlnHide

Как все это будет выглядеть? Это будет выглядеть как красиво отформатированный вывод, который мы хотели получить, когда рассматривали рисунок 1.

Неплохо, а? Вы сделали прекрасно смотрящийся вывод данных и при этом почти не затратили усилий. (А ваш следующий сценарий будет еще проще, потому что все, что вам нужно сделать, — это внести самые незначительные изменения в первый сценарий).

Теперь следует признать, вы не будете получать того же удовлетворения, что получала Золушка, трудясь, как раб, день и ночь только затем, чтобы представить сценарий вывода данных в табличной форме. И, честно говоря, вам не придется выходить замуж за принца, хотя это и не относится к делу. Но это кажется малой ценой, которую платишь для того, чтобы получить столь хороший вывод сценария VBScript, особенно когда вы решите, какими бывают принцы в наши дни.

И кто знает: может быть, вы так или иначе сами станете принцем. В конце концов, даже августейшие особы нуждаются в информации обо всех службах, установленных на их компьютерах, не так ли?

Головоломка-сценарий доктора Скрипто.

Задача на этот месяц потребует не только умения решать головоломки, но и навыков создания сценариев.

Июнь 2008 г. PowerShell Pathway

Во всех этих головоломках соедините буквы по горизонтали, вертикали и диагонали, чтобы сложить имя командлета Windows PowerShell. Каждая буква должна использоваться только один раз. Приведем пример.

Командлет, составленный в этой головоломке, — New-Alias. Теперь ваша очередь. Вот еще три головоломки:

ANSWER:

Головоломка-сценарий доктора Скрипто.

Ответы: PowerShell Pathway, июнь 2008 г.

Read-Host

Set-AuthenticodeSignature

Программисты корпорации Майкрософт работают... ну хорошо, получают зарплату в корпорации Майкрософт. Когда они не играют в бейсбол, не тренируют бейсбольную команду, не смотрят бейсбол и не занимаются другими делами, они ведут проект Script Center в журнале TechNet Magazine. Веб-узел проекта находится по адресу www.scriptingguys.com..

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