Эй, сценарист!Игры в наперстки с помощью оболочки

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

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

Согласно древней поговорке, даже самый крохотный воробей не может упасть на землю так, чтобы это не заметили на небесах. Нет, Scripting Guys конечно не предлагают читателям заняться сшибанием воробьев с небес. (Хотя, если кто-нибудь желает позаботиться о идиотских воронах, орущих на крыше в семь утра по субботам, то это совсем другое дело. [Нашему редактору сценариев нравятся вороны, так что мы уверены в том, что под «позаботиться» сценарист из Scripting Guys, пишущий эту статью имеет в виду «обеспечить их благополучие». – Эд.]) В любом случае, приятно знать, что кого-то волнует твое падение и что, каким бы мелким и незначительным ты не казался, все равно кто-то где-то присматривает за тобой. [Даже если это просто редактор. – Эд.]

То, что верно для воробьев, похоже, верно и для написания сценариев системного администрирования. Можно спокойно сказать, что в мире сценариев основная часть внимания прикована к большим шишкам: Windows PowerShellTM, VBScript, WMI, ADSI и даже FileSystemObject. То, что эти технологии притягивают наибольшее внимание, вполне понятно, в конце концов, они позволяют делать много всего интересного и полезного. Но это не значит, что они являются единственными технологиями, доступными авторам сценариев. Отнюдь нет.

Возьмем, к примеру, объект оболочки. Хотя объект оболочки может и не быть настолько же известен или разрекламирован, как Windows PowerShell, он, тем не менее, важен – и полезен.

Существует значительная вероятность того, что рано или поздно возникнет необходимость написать сценарий системного администрирования, использующий объект оболочки. Это ведет к очевидному вопросу: что именно можно сделать с помощью объекта оболочки? Оказывается, много всего интересного и часть этих вещей (таких как управление дисковыми квотами и отображение индикатора выполнения) уже освещена в руководстве по написанию сценариев для Microsoft® Windows® 2000 (microsoft.com/technet/scriptcenter/guide). В данном выпуске нашей рубрики мы собираемся показать вам еще несколько милых маленьких фокусов, которые можно проделать с объектом оболочки и о возможности проделать которые вообще, а тем более используя объект оболочки, читатели, вероятно, не знают.

Изменение даты последнего изменения файла

Многие из читателей, вероятно, смотрят на предшествующий заголовок и думают: «Постойте. Ведь менять дату последнего изменения с помощью сценария нельзя, по крайней мере, не с помощью VBScript». На что мы можем сказать только одно: откуда вообще взялась такая мысль?

Ох, верно; мы, Scripting Guys, вероятно сказали это в прошлом, не так ли? Что ж, как оказалось, мы ошибались. Возмутительно! Менять дату последнего изменения с помощью VBScript можно. Все, что для этого нужно – использовать объект оболочки.

Примечание. Не трудитесь спрашивать, как Scripting Guys могли тут ошибиться, после стольких лет это должно быть довольно очевидно. Вот куда более интересный вопрос: как нам вообще удается хоть когда-либо быть правыми?

Итак, необходимо поменять дату последнего изменения файла, да? Просто используйте этот сценарий:

Set objShell = _
  CreateObject("Shell.Application")

Set objFolder = _
  objShell.NameSpace("C:\Scripts")
Set objFolderItem = _
  objFolder.ParseName("Dr_Scripto.jpg")

objFolderItem.ModifyDate = _
  "01/01/2008 8:00:00 AM"

Как можно заметить, в этом нет ничего сложного. Сначала создается экземпляр объекта Shell.Application. (Кстати, не спутайте Shell.Application с Wscript.Shell, объектом оболочки сервера сценариев Windows. Объект оболочки с котором мы работаем сегодня – это объект оболочки Windows.) После того как наш экземпляр объекта оболочки заработал, мы используем метод пространства имен для привязки к папке C:\Scripts, а затем своеобразно названный метод ParseName для привязки к конкретному файлу внутри этой папки. В данном случае мы привязываем к изображению JPEG с именем файла Dr_Scripto.jpg:

Set objFolderItem = _
  objFolder.ParseName("Dr_Scripto.jpg")

И что мы делаем после привязки к самому файлу? Мы просто назначаем новую дату и время свойству ModifyDate:

objFolderItem.ModifyDate = _
  "01/01/2008 8:00:00 AM"

Вот и все, что нужно сделать.

Знаете, вы правы. Это довольно круто, не так ли? После запуска сценария дата последнего изменения файла будет установлена на 1 января 2008 года, 8:00. (Если пожелаете, можно открыть папку C:\Scripts в Windows Explorer, чтобы убедиться в этом самому.)

Само собой, кое кто из читателей может думать: «Круто. Я могу поменять дату последнего изменения файла. Но зачем мне вообще может понадобиться менять дату последнего изменения файла?». Ну, достаточно часто пользователи используют эту дату как своего рода систему контроля версий; если, скажем, в системе болтается несколько копий сценария, один из способов найти официальную версию – проверить дату последнего изменения. Проверив дату последнего изменения, можно сказать, является ли определенная копия сценария его неизмененной изначальной версией.

Примечание. Да, конечно, кто-нибудь может запустить только что продемонстрированный сценарий и изменить эту дату. Но, помимо подписывания кода после каждой операции, нет такого подхода, который кто-нибудь не сумел бы обойти. Если предположить, что коллеги – честные люди, с которыми можно делиться сценариями, этот подход разумен.

В какой-то момент может возникнуть потребность в обновлении – и стандартизации – даты последнего изменения для всех сценариев. Как проделать это? Предполагая, что все сценарии находятся в папке C:\Scripts, достаточно выполнить следующий сегмент кода:

Set objShell = _
  CreateObject("Shell.Application")

Set objFolder = _
  objShell.NameSpace("C:\Scripts")
Set colItems = objFolder.Items

For Each objItem In colItems
    objItem.ModifyDate  = _
      "01/01/2008 8:00:00 AM"
Next

Как можно заметить, этот сценарий начинается очень похоже на первый показанный нами сценарий. Но в данном случае, после привязки к папке C:\Scripts, мы не используем ParseName для привязки к отдельному файлу в этой папке. Вместо этого мы используем следующую строку кода, чтобы возвратить собрание всех файлов, найденных в этой папке:

Set colItems = objFolder.Items

После получения собрания мы устанавливаем цикл For Each для перебора всех элементов в нем. Внутри цикла For Each, мы используем следующую удобную строку кода, чтобы изменить значение свойства ModifyDate первого файла в собрании на 1 января 2008 года, 8:00:

objItem.ModifyDate  = _
  "01/01/2008 8:00:00 AM"

А затем мы просто продолжаем выполнение цикла и повторяем процесс на следующем файле в собрании. После того как все сказано и сделано, все файлы в папке C:\Scripts (ну, за исключением скрытых файлов) будут иметь идентичную дату последнего изменения. Видите, как просто? Ну и кто там сказал, что менять дату последнего изменения с помощью VBScript нельзя?

Ох, да.

К слову об заблуждении...

В доисторическую эпоху, когда Scripting Guys впервые начали работать со сценариями системного администрирования, не было никакой возможности получить доступ к расширенной информации о файле, присоединяемой к файлу в файловой системе NTFS. Например, если щелкнуть правой кнопкой мыши на файл .wav, а затем щелкнуть свойства, можно увидеть информацию, подобную указанной на рис. 1.

Рис. 1 Сводка свойств файла

Рис. 1** Сводка свойств файла **

Как извлечь значения, вроде скорости потока или размера аудио образца, используя сценарий? Никак. Ну, если не использовать для этого объект оболочки. (Или Windows Desktop Search 3.0, если эта программа загружена и установлена.) Где-то в процессе к объекту оболочки была добавлена способность извлекать всю расширенную информацию о свойствах файла, но Scripting Guys каким-то образом просмотрели данный факт. Как следствие, мы говорили читателям: «Использовать сценария для определения скорости потока файла .wav невозможно», хотя, как показывает код на рис. 2, на деле это возможно.

Figure 2 Используйте сценарий, чтобы найти скорость потока файла .wav

Set objShell = CreateObject("Shell.Application")

Set objFolder = objShell.NameSpace("C:\Scripts")
Set objFolderItem = objFolder.ParseName("J0388563.wav")

For i = 0 to 33
    strHeader = objFolder.GetDetailsOf(objFolder.Items, i)
    strValue = objFolder.GetDetailsOf(objFolderItem, i)
    If strValue <> "" Then
        Wscript.Echo strHeader & vbTab & strValue
    End If
Next

Как работает этот сценарий? Ну, начинается и он точно так же, как первый, показанный нами: мы создаем экземпляр объекта Shell.Application и привязываем его к папке C:\Scripts, затем используем метод ParseName для привязки к рассматриваемому файлу (в данном случае, J0388563.wav).

С этого момента все становится несколько хитрее. Для начала мы устанавливаем цикл For Next, выполняющийся с 0 до 33, поскольку файлы поддерживают до 31 расширенного свойства, которым даны номера в индексе с 0 до 33. (Отметьте, что есть несколько номеров, таких как 27 и 28, которые входят в индекс, но не содержат значений свойств.) В действительности файлы поддерживают свойства, показанные на рис. 3.

Figure 3 Свойства файла

Индекс Свойство
0 Имя
1 Размер
2 Тип
3 Дата изменения
4 Дата создания
5 Дата доступа
6 Атрибуты
7 Состояние
8 Владелец
9 Автор
10 Заголовок
11 Тема
12 Категория
13 Страниц
14 Комментарии
15 Авторские права
16 Артист
17 Название альбома
18 Год
19 Номер дорожки
20 Жанр
21 Протяженность
22 Скорость потока
23 Защищен
24 Модель камеры
25 Дата снимка
26 Размеры
29 Название серии
30 Описание программы
32 Размер аудио образца
33 Частота дискретизации

Так что у нас есть внутри цикла For Next? В первую очередь мы исполняем следующую строку кода:

strHeader = _
  objFolder.GetDetailsOf(objFolder.Items, i)

Здесь мы используем метод GetDetailsOf для извлечения имени свойства 0 (помним, что наш цикл работает с 0 до 33). Затем мы сохраняем это имя в переменной, именуемой strHeader, а также используем данную строку кода для извлечения значения свойства элемента 0, сохраняя возвращенную информацию в переменной, именуемой strValue:

strValue = _
  objFolder.GetDetailsOf(objFolderItem, i)

Поняли, в чем смысл? Элемент 0 – это свойство имени. Так что при первом проходе через цикл strHeader будет равен имени. В то же время реальное имя (то есть значение свойства имени) для первого файла – J0388563.wav. Следовательно, при первом проходе через цикл strHeader будет равен J0388563.wav. В таком изложении это не кажется таким уж сложным, верно?

Как читатели, вероятно, знают, не все типы файлов поддерживают все расширенные свойства. (Например, файл .jpg не будет иметь свойств, вроде названия альбома или частоты дискретизации.) Следовательно, в следующей строке кода мы проверяем, не является ли возвращенное значение пустой строкой:

If strValue <> "" Then

Если оно является таковой, мы просто переходим к началу цикла и повторяем процесс со следующим расширенным свойством. Если значение – не пустая строка, мы выводим имя и значение свойства, вот так:

Wscript.Echo strHeader & vbTab & strValue

Что мы имеем в итоге? Нечто подобное следующему:

Имя    j0388563.wav
Размер    169 KB
Тип    Wave Sound
Дата изменения   1/19/2004 8:56 AM
Дата создания    3/6/2006 2:02 PM
Дата доступа   12/3/2007 10:41 AM
Атрибуты      A
Состояние  Online
Владелец   FABRIKAM\kenmyer
Скорость потока        90kbps
Размер аудио образца       4 bit
Частота дискретизации       11 kHz

Здорово.

У нас уже заканчивается время (и объем в журнале), но для тех, кто ищет еще более простой способ возвращения наиболее актуальных значений свойств, мы рекомендуем сценарий на рис. 4 вместо предыдущего.

Figure 4 Простой способ возвращения значений свойств

Const colInfoTip = -1

Set objShell = CreateObject("Shell.Application")

Set objFolder = objShell.NameSpace("C:\Scripts")
Set objFolderItem = _
  objFolder.ParseName("01. Out On The Weekend (Album Version).wma)")

Wscript.Echo objFolder.GetDetailsOf(objFolderItem, colInfoTip)

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

Рис. 5 Свойства, показанные в подсказке

Рис. 5** Свойства, показанные в подсказке **

Артист: Neil Young
Название альбома: Harvest
Год: 1972
Номер дорожки: 1
Протяженность: 0:04:35
Тип: Windows Media Audio file
Скорость потока: 256kbps
Защищен: Yes
Размер: 5.31 MB 

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

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

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

Март 2008: Загадочный сценарий

В этом месяце головоломка является шифрограммой – каждая буква в ней заменена другой буквой. (Мы оставили все цифры и символы как есть.) Каждая замененная буква представлена одной определенной буквой. Например, все буквы a могут быть заменены на b, все b на z и так далее. Вот простой пример:

tdsjqu dfoufs

расшифровывается так:

script center

Расшифровка в данном случае была выполнена путем замены каждой буквы на букву, предшествующую ей в алфавите: t на s, d на c, и т.д.

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

Желаю удачи!

Головоломка

kqyjs edfjkzyrlyg = 1
kqyjs edyq = 2
kqyjs edjqtstqmj = 2

jzs quwzekzd = ktzfszquwzks("zekzd.faadlkfslqy")
quwzekzd.iljludz = stcz

jzs quwmqtxuqqx = quwzekzd.mqtxuqqxj.frr
jzs quwmqtxjozzs = quwmqtxuqqx.mqtxjozzsj(1)

quwmqtxjozzs.kzddj(1, 1) = "kfs"
quwmqtxjozzs.kzddj(1, 2) = "rqg"
quwmqtxjozzs.kzddj(1, 3) = "ufs"
quwmqtxjozzs.kzddj(1, 4) = "faz"

jzs quwtfygz = quwzekzd.fkslizkzdd.zysltztqm
quwtfygz.jqts quwtfygz, edfjkzyrlyg, , , , , , edyq, , , edjqtstqmj

ANSWER:

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

Ответ. Загадочный сценарий, март 2008 года

Вот ключ для расшифровки сценария:

Actual Letter   a   b   c   d   e   f   g   h   i   j   k
Coded Letter    f   u   k   r   z   b   g   o   l   w   x

Actual Letter   l   m   n   o   p   q   r   s   t   u   v   w   x   y   z
Coded Letter    d   n   y   q   a   h   t   j   s   c   i   m   e   v   p

Const xlAscending = 1
Const xlNo = 2
Const xlSortRows = 2

Set objExcel = CreateObject("Excel.Application")
objExcel.Visible = True

Set objWorkbook = objExcel.Workbooks.Add
Set objWorksheet = objWorkbook.Worksheets(1)

objWorksheet.Cells(1, 1) = "Cat"
objWorksheet.Cells(1, 2) = "Dog"
objWorksheet.Cells(1, 3) = "Bat"
objWorksheet.Cells(1, 4) = "Ape"

Set objRange = objExcel.ActiveCell.EntireRow
objRange.Sort objRange, xlAscending, , , , , , xlNo, , , xlSortRows

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

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