1

Тема: VBScript: Получить список файлов в каталоге

Собственно, использую такой код:

Set Files = FSO.GetFolder(FolderPath).Files 
For Each File In Files 
 If LCase(fso.GetExtensionName(file)) = FilesExt Then 
  ...
 End If
Next

Проблема в том, что файлы выводятся в неотсортированном порядке. А хотелось бы, чтобы они шли по времени создания (сначала - более новые). Вот как быть? Либо у Set Files = FSO.GetFolder(FolderPath).Files есть какой-то параметр? Либо загонять все файлы в массив и потом их сортировать, либо вообще менять код на что-то другое?

Господа, как быть?

Спасибо.

2

Re: VBScript: Получить список файлов в каталоге

Проблема в том, что файлы выводятся в неотсортированном порядке.

На новых файловых системах — NTFS — файлы располагаются в алфавитном порядке имён.

Либо у Set Files = FSO.GetFolder(FolderPath).Files есть какой-то параметр?

Нет.

Либо загонять все файлы в массив и потом их сортировать

Да. Либо в RecordSet и там сортировать.

либо вообще менять код на что-то другое?

В рамках заявленной категории — особо не на что. А так — можно читать вывод «dir /o:…» через stdout объекта WshExec, можно вообще перейти на PowerShell, можно использовать объект Microsoft LogParser.

3

Re: VBScript: Получить список файлов в каталоге

sub insert(o ,byref x)
    i=0
    for each c in x 
        if c(0)>=o(0) then
            exit for
        end if
        i=i+1
    next
    x.insert i, o
end sub

set shell=createobject("shell.application")
set folder=shell.namespace(createobject("scripting.filesystemobject").GetFile(WScript.ScriptFullName).ParentFolder.Path & "\")
set sl=CreateObject("System.Collections.ArrayList")
set items=folder.items

typesort="size"
for each item in items
    insert array(eval("item." & typesort),item), sl
next
s=""
for each c in sl
    s=s & c(1).path & vbcrlf
next
wscript.echo s
Я конечно далек от мысли... (с)

4

Re: VBScript: Получить список файлов в каталоге

Используйте WMI и

"Select * from CIM_DataFile where ... ORDER BY ..."

Только не делайте слишком размытый критерий отбора - долго исполняется.

5

Re: VBScript: Получить список файлов в каталоге

Используйте WMI и

"Select * from CIM_DataFile where ... ORDER BY ..."

Не верю: WQL (SQL for WMI) (Windows).

6

Re: VBScript: Получить список файлов в каталоге

alexii пишет:

Не верю: ...

Таки да - ORDER BY не реализовано, извиняюсь за дезинформацию.

7

Re: VBScript: Получить список файлов в каталоге

ig.aruba, Вы, возможно, спутали с этим расширением: (SMS SDK 2.0) Extended WMI Query Language.

8

Re: VBScript: Получить список файлов в каталоге

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

Функция FilterFolderItems(oItems, ByVal Query) для сортировки и фильтрации переданных файлов или каталогов
oItems - Коллеция объектов Files или SubFolders, полученные через Scripting.FileSystemObject
Query - Запрос для отбора файлов или каталогов по необходимым критериям.

Ниже код с примерами и комментариями:


Option Explicit
Const TemporaryFolder = 2
Dim FileSystemObject, FolderPath, Folder, arrItems, oItem, strTmp
'Создаём объект для работы с файловой системой
Set FileSystemObject = CreateObject("Scripting.FileSystemObject")
'Получаем путь до каталога временных файлов (просто для примера).  
FolderPath = FileSystemObject.GetSpecialFolder(TemporaryFolder)
'FolderPath = "C:\Windows\Web\Wallpaper"
'Получаем объектное представление каталога.
Set Folder = FileSystemObject.GetFolder(FolderPath)

'*** Пример 1 ***
arrItems = FilterFolderItems(Folder.SubFolders, "ORDER BY Name")
strTmp = "Сортировка каталогов по имени в прямом направлении" & String(2,vbCrlf)
'Перебираем возвращённые элементы
For Each oItem in arrItems
    'Собираем в строку данные из массива
    strTmp = strTmp & oItem.Name & vbCrlf
Next
'Выводим результат сортировки
MsgBox strTmp, 0, "Пример 1"

'*** Пример 2 ***
arrItems = FilterFolderItems(Folder.SubFolders, "ORDER BY DateLastModified DESC")
strTmp = "Сортировка каталогов по дате последнего изменения в обратном порядке" & String(2,vbCrlf)
'Перебираем возвращённые элементы
For Each oItem in arrItems
    'Собираем в строку данные из массива
    strTmp = strTmp & FormatDateTime(oItem.DateLastModified,2) & String(2,vbTab) & oItem.Name & vbCrlf
Next
'Выводим результат сортировки
MsgBox strTmp, 0, "Пример 2"

'*** Пример 3 ***
arrItems = FilterFolderItems(Folder.Files, "Size < 100 ORDER BY Size DESC, DateLastModified DESC")
strTmp = "Выборка файлов размером менее 100 байт и сортировка по дате последнего изменения в обратном порядке" & String(2,vbCrlf)
'Перебираем возвращённые элементы
For Each oItem in arrItems
    'Собираем в строку данные из массива
    strTmp = strTmp & FormatDateTime(oItem.DateLastModified,2) & String(2,vbTab) & oItem.Size & "Б" & String(2,vbTab) & oItem.Name & vbCrlf
Next
'Выводим результат сортировки
MsgBox strTmp, 0, "Пример 3"

'Функция сортировки и фильтрации переданных файлов или каталогов
'oItems - Элементы Files или SubFolders (коллекции файлов или подкаталогов), полученные от Scripting.FileSystemObject
'Query - Запрос для отбора файлов или каталогов по необходимым критериям. 
Function FilterFolderItems(oItems, ByVal Query)
    'Константы для метода GetRows
    Const adGetRowsRest    = -1    'Флаг Recordset-а для указания кол-ва возвращаемых строк
    Const adBookmarkFirst = 1   'Флаг Recordset-а для возврата с первой записи
    Const adIDispatch = 9
    Const adDouble = 5
    Const adVarChar = 200
    Const adDate = 7
    Dim oADORec, oItem
    Set oADORec = CreateObject("ADODB.Recordset")
    'Создаём поля под размещаемые элементы
    With oADORec.Fields
        .Append "Item", adIDispatch
        .Append "Name", adVarChar, 255
        .Append "Type", adVarChar, 255
        .Append "Size", adDouble
        .Append "DateCreated", adDate
        .Append "DateLastAccessed", adDate
        .Append "DateLastModified", adDate
    End With
    'Открываем рекордсет для добавления данных
    oADORec.Open
    'Заполняем переданными элементами
    For Each oItem in oItems
        oADORec.AddNew
        oADORec("Item") = oItem
        oADORec("Type") = oItem.Type
        oADORec("Name") = oItem.Name
        oADORec("Size") = oItem.Size
        oADORec("DateCreated") = oItem.DateCreated
        oADORec("DateLastAccessed") = oItem.DateLastAccessed
        oADORec("DateLastModified") = oItem.DateLastModified
    Next
    'Делим запрос по параметру сортировки
    Query = Split(Query,"order by",-1,vbTextCompare)
    'Часть для фильтра передаём в фильтр
    oADORec.Filter = Trim(Query(0))
    'Часть для сортировки передаём в сортировку
    if Ubound(Query) > 0 Then oADORec.Sort = Trim(Query(1))
    'Обратно отдаём отфильтрованный и отсортированный массив исходных элементов
    'Подробнее на http://www.w3schools.com/ado/met_rs_getrows.asp
    if oADORec.EOF Then 
        FilterFolderItems = Array()
    Else
        FilterFolderItems = oADORec.GetRows(adGetRowsRest,adBookmarkFirst,"Item")
    End If
End Function

Подумал. Возможно более логичный вариант возвращать из функции сам Recordset.
Собрал функцию FolderItemsToRecordset(oItems)
oItems - Элементы Files или SubFolders (коллекции файлов или подкаталогов), полученные от Scripting.FileSystemObject

Заготовка №2


Option Explicit
Const TemporaryFolder = 2
Dim FileSystemObject, FolderPath, Folder, oRecords, strTmp
'Создаём объект для работы с файловой системой
Set FileSystemObject = CreateObject("Scripting.FileSystemObject")
'Получаем путь до каталога временных файлов (просто для примера).  
FolderPath = FileSystemObject.GetSpecialFolder(TemporaryFolder)
'FolderPath = "C:\Windows\Web\Wallpaper"
'Получаем объектное представление каталога.
Set Folder = FileSystemObject.GetFolder(FolderPath)
'*** Пример ***
Set oRecords = FolderItemsToRecordset(Folder.Files)
'Фильтруем элементы за последние 5 дней
oRecords.Filter = "DateLastModified > #" & Date()-5 & "# AND DateLastModified < #" & Date() & "#"
'Сортируем в обратном порядке по размеру
oRecords.Sort = "Size DESC"
strTmp = "Выборка файлов изменённых за последние 3 дня и сортировка по размеру в обратном порядке." & String(2,vbCrlf) & "Filter: " & oRecords.Filter & vbCrlf & "Sort: " & oRecords.Sort & String(2,vbCrlf)
'Перебираем записи
Do While Not oRecords.EOF
    strTmp = strTmp & FormatDateTime(oRecords("DateLastModified"),2) & String(2,vbTab) & oRecords("Size") & "Б" & String(1,vbTab) & oRecords("Extension") & String(1,vbTab) & oRecords("Name") & vbCrlf
    oRecords.MoveNext
Loop
'Выводим результат сортировки
MsgBox strTmp, 0, "Пример"
'Функция сортировки и фильтрации переданных файлов или каталогов
'oItems - Элементы Files или SubFolders (коллекции файлов или подкаталогов), полученные от Scripting.FileSystemObject
Function FolderItemsToRecordset(oItems)
    Const adVarChar = 200
    Const adDouble = 5
    Const adDate = 7
    Dim oADORec, oItem
    Set oADORec = CreateObject("ADODB.Recordset")
    'Создаём поля под размещаемые элементы
    With oADORec.Fields
        .Append "Name", adVarChar, 255
        .Append "Type", adVarChar, 255
        .Append "Extension", adVarChar, 10
        .Append "Size", adDouble
        .Append "DateCreated", adDate
        .Append "DateLastAccessed", adDate
        .Append "DateLastModified", adDate
    End With
    'Открываем рекордсет для добавления данных
    oADORec.Open
    Set FolderItemsToRecordset = oADORec
    'Заполняем переданными элементами
    For Each oItem in oItems
        oADORec.AddNew
        oADORec("Name") = oItem.Name
        oADORec("Type") = oItem.Type
        oADORec("Size") = oItem.Size
        oADORec("DateCreated") = oItem.DateCreated
        oADORec("DateLastAccessed") = oItem.DateLastAccessed
        oADORec("DateLastModified") = oItem.DateLastModified
        If TypeName(oItem) = "File" And InStr(1,oItem.Name,".") Then
            oADORec("Extension") = LCase(Trim(Mid(oItem.Name, InStrRev(oItem.Name, ".") + 1)))
        End If
    Next
    oADORec.MoveFirst
End Function
Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

9

Re: VBScript: Получить список файлов в каталоге

Xameleon, имеет смысл.

10

Re: VBScript: Получить список файлов в каталоге

alexii, имеет смысл не заниматься подобной хренью ? )

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

11

Re: VBScript: Получить список файлов в каталоге

Поместить в Коллекцию. Не откладывая на потом. А то будет как обычно .

12

Re: VBScript: Получить список файлов в каталоге

Господа, позвольте вставить свое слово.
На счет WMI вопрос довольно спорный, ибо возникает множественное "если". Например, что если недостаточно прав у пользователя и дыр и пыр. И потом, строчить опусы на счет сортировки файлов, что-то сродни научной статье, поэтому сразу же приведу пример.

var arr = [], day;
with (new ActiveXObject('Scripting.FileSystemObject')) {
  with (new Enumerator(GetFolder(MyFolder).Files)) {
    for (; !atEnd(); moveNext()) {
      with (new Date(item().DateCreated)) {
        day = getYear() + (getMonth().toString().length == 1 ? '0' + getMonth() : getMonth()) + 
                              (getDate().toString().length == 1 ? '0' + getDate() : getDate());
      }
      arr.push(day + ':' + item().Name);
    }
  }
}
arr.sort();
with (new Enumerator(arr)) {
  for (; !atEnd(); moveNext()) {
    WScript.echo(item().split(':')[1]);
  }
}

Пояснять что к чему, полагаю, не стоит, отмечу лишь что файлы сортируются от древнего к новому. Если требуется обратный порядок, можно попросту перевернуть массив.
http://forum.vingrad.ru/html/emoticons/pack/offtopic.gif
То же решение на PoSh'е выглядит как

PS E:\> ls | ? {$_.Mode -notmatch 'd'} | sort CreationTime

13

Re: VBScript: Получить список файлов в каталоге

greg zakharov, с первой частью сообщения согласен - WMI не лучший выбор для решения этой задачи, но на счёт второй части ответа не совсем понял. Да, вариант сортировки массива красивый, но гибкость при этом же страдает ? Через Recordset можно добиться больших возможностей для отбора.

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

14

Re: VBScript: Получить список файлов в каталоге

Вы знаете, спорить и утверждать относительно гибкости не стану. Это более, чем очевидно. Но все же в самом понятии "гибкость" скрывается большое количество нюансов, таких как удобство и простота, а потому наиболее оптимальным вариантом решения большинства рутинных задач, как та же сортировка файлов, на мой взгдяд не должна доставлять лишнюю головную боль системному администратору, в виду чего было бы неплохо создать библиотеку классов (а может даже сколотить целый фреймворк) с легким API, дабы при возникновении задачи, которая некогда была решена, не задаваться вопросом (если решение забыто) ее решением, - достаточно было бы заглянуть в документацию библиотеки, пара строк кода и все. Вот это, пожалуй, и есть настоящая гибкость. Впрочем, здесь весьма велик риск холивара, поэтому сразу же оговорюсь, что это лишь мое мнение и навязывать его кому бы то ни было я не собираюсь, да и Ваше мнение я уважаю.

15

Re: VBScript: Получить список файлов в каталоге

Понял. Согласен. Действительно, автор поста просил просто отсортировать по дате. Про гибкость и дополнительные усложнения речи не было. )

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

16 (изменено: wisgest, 2013-10-17 06:28:17)

Re: VBScript: Получить список файлов в каталоге

Xameleon пишет:

Да, вариант сортировки массива красивый, но гибкость при этом же страдает ?

1) Перед добавлением элементов в массив можно проверять их на наличие необходимого признака.
2) При упорядочивании массива можно вспомнить, что метод .sort() принимает в качестве аргумента функцию попарного сравнения элементов, что позволяет упорядочивать массивы объектов любой природы по любому правилу:

var FS = new ActiveXObject("Scripting.FileSystemObject");
var col = FS.GetFolder(MyFolder).Files;
var _enum = new Enumerator(col);
var arr = [];

for (; !_enum.atEnd(); _enum.moveNext()) arr.push(_enum.item());
arr.sort(function (A, B) {return A.DateCreated - B.DateCreated;});
for (var i = 0; i < arr.length; i++) WScript.Echo(arr[i].Name);
greg zakharov пишет:

Если требуется обратный порядок, можно попросту

…изменить значения, возвращаемые функцией сравнения, на противоположные по знаку.

17

Re: VBScript: Получить список файлов в каталоге

wisgest, посыпаю голову пеплом и ухожу в монастырь. ) VBScript в помойку ! Даёшь JavaScript в массы !

Чуть позже задумался - а как сделать выборку всех файлов с расширением "rar" и "zip" созданные за последние 5 дней и отсортировать в обратном порядке по дате создания и размеру ?

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

18

Re: VBScript: Получить список файлов в каталоге

wisgest пишет:

…изменить значения, возвращаемые функцией сравнения, на противоположные по знаку.

Так еще лучше, - код ужимается в разы!

19

Re: VBScript: Получить список файлов в каталоге

greg zakharov пишет:

. . .


var arr = [], day;
. . . 
arr.sort();
with (new Enumerator(arr)) {
  for (; !atEnd(); moveNext()) {
    WScript.echo(item().split(':')[1]);
  }
}

. . .

Не стоит к массивам применять совсем избыточную (в данном случае) структуру Enumerator, потому что массивы обрабатываются совершенно нативно:


for (var i = 0; i < arr.length; i++) {
    var v = arr[i];
    // любые действия с элементом массива
}
( 2 * b ) || ! ( 2 * b )

20

Re: VBScript: Получить список файлов в каталоге

Xameleon пишет:

как сделать выборку всех файлов с расширением "rar" и "zip" созданные за последние 5 дней и отсортировать в обратном порядке по дате создания и размеру ?

Если при сортировке по дате не учитывать единицы времени меньше суток, то:

var FS = new ActiveXObject("Scripting.FileSystemObject");
var col = FS.GetFolder(MyFolder).Files;
var _enum = new Enumerator(col);
var arr = [];

var item = null;
var ext = String();

var ms_in_day = 24*60*60*1000;
var time_limit = new Date() - 5 * ms_in_day;
for (; !_enum.atEnd(); _enum.moveNext()) {
  item = _enum.item();
  ext = FS.GetExtensionName(item.Name).toLowerCase();
  if ((ext == "zip" || ext == "rar") && item.DateCreated > time_limit)
    arr.push(item);
}
arr.sort(function (A, B) {
  return (
    Math.floor(B.DateCreated / ms_in_day) -
    Math.floor(A.DateCreated / ms_in_day) ||
    B.Size - A.Size);
});
for (var i = 0; i < arr.length; i++) WScript.Echo(arr[i].Name);

Если время создания файла учитывать до миллисекунды, то проще:

arr.sort(function (A, B) {
  return B.DateCreated - A.DateCreated || B.Size - A.Size;
});

21

Re: VBScript: Получить список файлов в каталоге

wisgest, благодарю ! ) Пойду переписывать старый скрипт. Пришло время оптимизации.

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

22

Re: VBScript: Получить список файлов в каталоге

Rumat пишет:

Не стоит к массивам применять совсем избыточную (в данном случае) структуру Enumerator, потому что массивы обрабатываются совершенно нативно.

Должно быть Вы с ходу пишете идеальный код или Вам невдомек, что пример был "налету"? - в последних как правило содержатся ошибки, а потому я сие недоразумение приметил и без Вас. Или Вам непременно нужно, чтобы я исправил свой пост?

23

Re: VBScript: Получить список файлов в каталоге

greg zakharov, вдомёк— это Ваши предположения. Относитесь спокойнее к критике выкладываемого кода.

24 (изменено: greg zakharov, 2013-10-17 20:26:18)

Re: VBScript: Получить список файлов в каталоге

alexii пишет:

...это Ваши предположения...

Позвольте! Во-первых, критика здесь не причем, если Вы действительно умеете судить объективно, ибо г-н wisgest предложил более конструктивный вариант, а не вставил словечко ради слова, как в случае с г-ном Rumata, во-вторых, повторюсь еще раз код писался сходу, а не проектировался заблаговренно, что не исключает возможность ошибок, - или Вы и здесь не согласны? Тем паче что я не грубил и не употреблял повышенных тонов, а просто просил сатисфакции: были ли слова г-на Rumata упреком в пользу исправления кода или же он просто не уразумел, что код был написан сходу. Ну, а если Вам все вышесказанные доводы кажутся неубедительными, то позвольте Вам заметить, что тот же "вдомек" можно адресовать и Вам, как предвзятое отношение к моей персоне, - если желаете "обмен любезностями", давайте перейдем в личку и обсудим "либерализм", ставшей причиной эскапада в мой адрес, там. Прошу прощения прочих за столь обильный оффтоп.

25

Re: VBScript: Получить список файлов в каталоге

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

26

Re: VBScript: Получить список файлов в каталоге

greg zakharov пишет:

То же решение на PoSh'е выглядит как

PS E:\> ls | ? {$_.Mode -notmatch 'd'} | sort CreationTime

А ведь реально красиво. Перенести бы эту компактность в WSH. Спасибо Вам за пример. Я как раз работаю над проектами: фреймворком для JScript и внедрением своего пользовательского "синтаксического сахара" в JS.

27

Re: VBScript: Получить список файлов в каталоге

Xameleon

Добрый день!
Очень интересно внедрить Заготовку №2
Подскажите, пожалуйста, как изменить размер Рекордсета на неограниченное?
В данном варианте выдает только 5 значений.

Хочю иногда обращатся в папке в ней 165к файлов и создавать таблицу с гиперссылками для Excel
PowerQuery обновляет список за 10 минут.

Модуль на FSO без ADO думаю будет работать дольше, массив на 2+ миллиона полей

Спасибо