Давайте рассмотрим возможности использования данного объекта на примере сценария Sorter.vbs. В этом сценарии объект Penton.VBSort применяется для сортировки данных в одном файле и последующей их записи в другой файл. Поскольку в Sorter.vbs предусмотрена поддержка стандартного ввода и стандартного вывода, его также можно использовать для сортировки перенаправленных откуда-либо текстовых строк.

Работа с файлом Sorter.vbs

Так как для работы сценария Sorter.vbs необходим объект Penton.VBSort, перед тем как запускать этот сценарий, нужно зарегистрировать на своем компьютере компонент VBSort.wsc. Также следует помнить, что сценарий Sorter.vbs должен запускаться через сервер CScript.

Синтаксис строки запуска Sorter.vbs выглядит следующим образом:

[cscript] Sorter.vbs [filename]
[/o:outputfile] [/d] [/i] [/r]

Если CScript не является сервером сценариев, используемым в операционной системе по умолчанию, то в начале команды следует указывать ключевое слово cscript. Аргумент filename представляет собой имя файла, содержимое которого нужно отсортировать. Сценарий считывает содержимое этого входного файла в массив, после чего выполняет сортировку массива с помощью метода Sort объекта Penton.VBSort. Если имя входного файла не указывается, Sorter.vbs будет сортировать строки, поступающие через стандартный ввод.

С помощью ключа /o:outputfile указывается выходной файл, в который Sorter.vbs будет записывать отсортированные строки. При использовании ключа /o, необходимо учитывать, что выходной файл с указанным именем не должен существовать в момент запуска сценария, поскольку сценарий не сможет перезаписать существующий файл. Если ключ /o не используется, Sorter.vbs передает результаты в стандартный поток вывода (т.е. в окно командной строки). Если в имени входного или выходного файла имеются пробелы, это имя должно быть заключено в двойные кавычки ("). С помощью ключа /d задается сортировка по убыванию (descending), т.е. от Z до A и от 9 до 0. При использовании ключа /i игнорируется регистр букв (т.е. символы A и a считаются эквивалентами). Ключ /r включает режим случайной (random) сортировки (т.е. строки будут размещены в случайном порядке). Ключи /o, /d, /i и /r нечувствительны к регистру и могут указываться в любом порядке.

Как отмечалось выше, если по умолчанию в системе используется не сервер сценариев CScript, то команда запуска сценария должна начинаться с ключевого слова cscript. Но когда вы используете ключевое слово cscript, вам необходимо указать в команде полный путь к сценарию Sorter.vbs, если он находится не в текущем каталоге. Чтобы избежать подобных неудобств, можно задать Cscript в качестве сервера сценариев, используемого по умолчанию. Для этого, прежде чем запускать сценарий, выполните следующую команду:

cscript //h:cscript //nologo //s

Предположим, что на вашей системе сервер CScript используется по умолчанию, и вы хотите с помощью сценария Sorter.vbs выполнить сортировку данных (игнорируя регистр) в файле Myfile.txt и записать отсортированные данные в файл Mysortedfile.txt. В этом случае можно воспользоваться командой, приведенной ниже:

Sorter.vbs Myfile.txt /o:Mysortedfile.txt /i

Если требуется использовать Sorter.vbs для считывания данных из стандартного потока ввода, а не из входного файла, то существует один важный нюанс, о котором следует знать. Предположим, например, что сначала мы хотим сформировать список учетных записей домена с помощью команды Dsquery, а затем отсортировать полученные данные с помощью Sorter.vbs. Для считывания данных из стандартного потока ввода в Sorter.vbs используется свойство StdIn объекта Wscript. Поэтому теоретически, если CScript является сервером сценариев, используемым по умолчанию, мы можем сортировать данные из стандартного потока ввода без использования ключевого слова cscript. Например, приведенная ниже команда должна работать корректно:

Dsquery User -limit 1000 | Sorter.vbs /i

Однако на самом деле эта команда завершится, либо вообще не выведя никаких данных, либо отобразит сообщение об ошибке следующего содержания: The process tried to write to a nonexistent pipe («Процесс предпринял попытку записи в несуществующий канал»).

У этой странности есть одно объяснение, которое состоит в том, что когда командный процессор cmd.exe выполняет сценарий, используя ассоциации файлов, он некорректно создает потоки стандартного ввода и вывода. Поэтому как бы то ни было, обходным путем для исключения подобных ситуаций является использование в команде запуска ключевого слова cscript в тех случаях, когда мы перенаправляем ввод в сценарии Windows Script Host (WSH). Соответственно, для рассмотренного выше примера с Dsquery правильная команда запуска должна выглядеть так:

Dsquery User -limit 1000 | cscript Sorter.vbs /i

Внутреннее устройство сценария Sorter.vbs

Итак, мы знаем, как запускать сценарий Sorter.vbs, теперь давайте рассмотрим, что происходит после того, как мы это сделали. Как показано в Листинге 1, в начале сценария объявляются три константы (имя сценария и два значения ошибок интерфейса Win32 API), после чего вызывается процедура Main, которая является сердцем сценария. В начале этой процедуры объявляются ее внутренние переменные и создается ссылка на коллекцию WScript.Arguments через переменную Args. Далее выполняется проверка, имеется ли в строке запуска аргумент /?. Если этот аргумент есть, процедура Main вызывает процедуру Usage. Процедура Usage выводит краткое справочное сообщение об использовании сценария и завершает его выполнение вызовом метода Wscript.Quit. Далее вызывается функция ScriptHost, с помощью которой проверяется, через какой сервер выполняется данный сценарий. Если выясняется, что сценарий запущен не через сервер cscript.exe, процедура Main вызывает процедуру Die, которая выводит соответствующее сообщение об ошибке и завершает работу сценария с ненулевым кодом возврата.

После этого процедура Main проверяет, может ли она создать экземпляр объекта Penton.VBSort. Если эта попытка завершается неудачно, то выводится сообщение об ошибке, и сценарий завершает выполнение с ненулевым кодом возврата, для чего вновь используется процедура Die (см. Листинг 1, фрагмент с меткой A).

Следующая задача, которую выполняет процедура Main - анализ аргументов командной строки. Если в командной строке имеется неименованный аргумент (т.е. аргумент, имя которого начинается не с символа прямого слеша "/"), то процедура Main использует его в качестве имени входного файла. Для проверки существования файла используется метод FileExists объекта FileSystemObject. Если данный файл не существует, сценарий завершает работу через процедуру Die. В противном случае предпринимается попытка открытия этого файла как объекта TextStream. Если эта попытка завершается неудачей, сценарий прекращает выполнение с выводом сообщения об ошибке.

Если в строке запуска нет неименованных аргументов, то процедура Main считает, что необходимо выполнить сортировку стандартного ввода. В этом случае в качестве входного файла используется свойство WScript.StdIn.

Выполнив оценку аргументов командной строки, процедура Main далее определяет, указан ли в командной строке ключ /o. Если этот ключ существует и в нем задано имя файла, то выполняется проверка существования данного файла. Если он не существует, Main завершает работу сценария с ошибкой и ненулевым кодом возврата. В противном случае выполняется попытка открытия этого файла как объекта TextStream. Если эта попытка завершается неудачей, сценарий прекращает выполнение с выводом сообщения об ошибке. Если же ключ /o не указан или указан, но не содержит имени файла, то Main предполагает, что следует использовать стандартный вывод, и в этом случае вместо выходного файла будет применяться свойство WScript.StdOut.

Далее процедура Main проверяет наличие в командной строке ключей /d, /i и /r и устанавливает соответствующие значения свойств Descending, IgnoreCase и Random объекта Penton.VBSort. После этого процедура Main готова считывать входные данные в массив.

Для отключения встроенного обработчика ошибок VBScript в процедуре Main используется инструкция On Error Resume Next, далее с помощью функции VBScript Split данные, считываемые из входного файла, преобразуются в массив. В качестве разделителя элементов массива данная функция использует символ новой строки, который представлен константой vbNewLine. Как показано во фрагменте, обозначенном меткой B, при возникновении ошибки процедура Main сохраняет значение свойства Number объекта Err (для идентификации номера ошибки) и включает встроенный обработчик ошибок VBScript с помощью инструкции On Error GoTo 0.

При отсутствии ошибок (т.е. если переменная Result содержит значение 0) процедура Main для выполнения сортировки массива вызывает метод Sort объекта Penton.VBSort. После того как сортировка массива выполнена, производится запись строк массива в выходной файл, для чего используется цикл For... Next, как показано во фрагменте, обозначенном меткой C. В конце работы процедура Main с помощью метода Close закрывает входной и выходной объекты TextStream.

Пример полезного применения

Файл Sorter.vbs демонстрирует практическое использование возможностей объекта Penton.VBSort в инструментальных средствах командной строки. Данный сценарий также может служить простым примером применения объекта Penton.VBSort в собственных сценариях.

 


Листинг 1. Sorter.vbs

Option Explicit
' НАЧАЛО КОММЕНТАРИЯ
' Объявления констант сценария.
' КОНЕЦ КОММЕНТАРИЯ
Const SCRIPT_NAME = "Sorter.vbs"
' НАЧАЛО КОММЕНТАРИЯ
' Объявления констант Win32 API.
' КОНЕЦ КОММЕНТАРИЯ
Const ERROR_FILE_NOT_FOUND = 2
Const ERROR_ALREADY_EXISTS = 183

Main()

Sub Usage()
WScript.Echo "Sorts an input file, or standard input, and writes it to an output file or" _
& vbNewLine & "standard output." & vbNewLine & vbNewLine _
& "Usage: " & SCRIPT_NAME & " [] [/O:] [/D] [/I] [/R]" _
& vbNewLine & vbNewLine _
& " is the input file to sort. If not specified, uses standard input." _
& vbNewLine & vbNewLine _
& " is the output file. If not specified, uses standard output." _
& vbNewLine & vbNewLine _
& "/D performs a descending (Z-A and 9-0) sort." & vbNewLine & vbNewLine _
& "/I ignores case when sorting." & vbNewLine & vbNewLine _
& "/R performs a random sort (lines are rearranged in random order)."
WScript.Quit
End Sub

' НАЧАЛО КОММЕНТАРИЯ
' Завершение работы сценария с сообщением об ошибке и кодом возврата.
' КОНЕЦ КОММЕНТАРИЯ
Sub Die(ByVal ErrorMessage, ByVal ReturnCode)
WScript.Echo ErrorMessage
WScript.Quit ReturnCode
End Sub

' НАЧАЛО КОММЕНТАРИЯ
' Определение имени используемого сервера сценариев.
' КОНЕЦ КОММЕНТАРИЯ
Function ScriptHost()
ScriptHost = LCase(Mid(WScript.FullName, Len(WScript.Path) + 2))
End Function

Sub Main()
Dim Args, VBSort, Result, FSO, FN, TSInput, TSOutput, Arr, N
Set Args = WScript.Arguments
If Args.Named.Exists("?") Then Usage()
If ScriptHost() <> "cscript.exe" Then _
Die "You must run this script with the CScript host.", 1

On Error Resume Next
Set VBSort = CreateObject("Penton.VBSort")

' НАЧАЛО КОММЕНТАРИЯ
' Фиксируем номер ошибки из свойства Number объекта Err.
' КОНЕЦ КОММЕНТАРИЯ
Result = Err
On Error GoTo 0
' НАЧАЛО ФРАГМЕНТА A
If Result <> 0 Then _
Die "Unable to create the Penton.VBSort object.", Result
' КОНЕЦ ФРАГМЕНТА A

Set FSO = CreateObject("Scripting.FileSystemObject")
If Args.Unnamed.Count > 0 Then
FN = Args.Unnamed(0)
If Not FSO.FileExists(FN) Then
Die "File not found - " & FN, ERROR_FILE_NOT_FOUND
Else
On Error Resume Next
Set TSInput = FSO.OpenTextFile(FN)
If Err Then _
Die "Error 0x" & Hex(Err) & " opening input file", Err
On Error GoTo 0
End If
Else
Set TSInput = WScript.StdIn
End If

If Args.Named("O") <> "" Then
FN = Args.Named("O")
If FSO.FileExists(FN) Then
Die "File already exists - " & FN, ERROR_ALREADY_EXISTS
Else
On Error Resume Next
Set TSOutput = FSO.OpenTextFile(FN, 2, True)
If Err Then
TSInput.Close
Die "Error 0x" & Hex(Err) & " opening output file", Err
End If
On Error GoTo 0
End If
Else
Set TSOutput = WScript.StdOut
End If

' НАЧАЛО КОММЕНТАРИЯ
' Если в командной строке имеются аргументы /d, /i и /r,
' свойствам Descending, IgnoreCase и Random
' присваиваются соответствующие значения.
' КОНЕЦ КОММЕНТАРИЯ
VBSort.Descending = Args.Named.Exists("D")
VBSort.IgnoreCase = Args.Named.Exists("I")
VBSort.Random = Args.Named.Exists("R")

' НАЧАЛО ФРАГМЕНТА B
On Error Resume Next
Arr = Split(TSInput.ReadAll, vbNewLine)
' НАЧАЛО КОММЕНТАРИЯ
' Фиксируем номер ошибки из свойства Number объекта Err.
' КОНЕЦ КОММЕНТАРИЯ
Result = Err
On Error GoTo 0
' КОНЕЦ ФРАГМЕНТА B

' НАЧАЛО ФРАГМЕНТА C
If Result = 0 Then
' НАЧАЛО КОММЕНТАРИЯ
' Вызываем метод Sort компонента VBSort.
' КОНЕЦ КОММЕНТАРИЯ
VBSort.Sort Arr

For N = 0 To UBound(Arr)
TSOutput.WriteLine Arr(N)
Next
End If
' КОНЕЦ ФРАГМЕНТА C

TSInput.Close
TSOutput.Close
End Sub