Что нового

_FileDirList - Поиск файлов с использованием команды DIR

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,486
Вот решил выложить отдельно.
Позволяет искать файлы по нескольким маскам, с возможностью сортировки результата:

Код:
; #FUNCTION# ====================================================================================================
; Name...........:	_FileDirList
; Description....:	Search files and\or folders in a specified path (uses system Dir command)
; Syntax.........:	_FileDirList($sPath [, $sFileMask = "*" [, $iFlag = 0 [, $iSubDir = 1 [, $iSort = 0]]]])
; Parameters.....:	$sPath     - Path to search the file.
;					$sFileMask - [Optional] Filter to use, default is "*". Search the Autoit3 helpfile for the word "WildCards" For details.
;					$iFlag     - [Optional] Specifies whether to return files folders or both:
;                                                                                               $iFlag = 0 - Files and folders (default)
;                                                                                               $iFlag = 1 - Only files
;                                                                                               $iFlag = 2 - Only folders
;					$iSubDir   - [Optional] Specifies whether to search in subfolders or not:
;                                                                                               $iSubDir = 1 - Search in subfolders (default). Returns full pathes.
;                                                                                               $iSubDir = 0 - Search without subfolders (only in $sPath). Returns filenames only.
;					$iSort     - [Optional] Specifies whether to sort the output list (in alphabetic order) or not (default is 0 - do not sort).
;					
; Return values..:	Success    - An array with the following elements:
;                                                                      $aArray[0] = Number of Files\Folders returned
;                                                                      $aArray[1] = 1st File\Folder
;                                                                      $aArray[2] = 2nd File\Folder
;                                                                      $aArray[3] = 3rd File\Folder
;                                                                      $aArray[n] = nth File\Folder
;					Failure    - 0
;                      @Error:    1 = Path not found or invalid
;                                 2 = No File(s) Found
;
; Author.........:	G.Sandler (CreatoR), amel27.
; Modified.......:	
; Remarks........:	This function uses system Dir command, to speed up the search.
; Related........:	
; Link...........:	
; Example........:	
; ===============================================================================================================
Func _FileDirList($sPath, $sFileMask = "*", $iFlag = 0, $iSubDir = 1, $iSort = 0)
	Local $sOutBin, $sOut, $aOut, $aMasks, $sRead, $hDir, $sAttrib, $sFiles
	
	If Not StringInStr(FileGetAttrib($sPath), "D") Then
		Return SetError(1, 0, 0)
	EndIf
	
	If $iSubDir = 1 Then
		$sAttrib &= ' /S'
	EndIf
	
	If $iSort = 1 Then
		$sAttrib &= ' /O:N'
	EndIf
	
	Switch $iFlag
		Case 1
			$sAttrib &= ' /A-D'
		Case 2 
			$sAttrib &= ' /AD'
		Case Else 
			$sAttrib &= ' /A'
	EndSwitch
	
	$sOut = StringToBinary('0' & @CRLF, 2)
	$sPath = StringRegExpReplace($sPath, '\\+$', '')
	$sFileMask = StringRegExpReplace($sFileMask, '^;+|;+$', '')
	$sFileMask = StringRegExpReplace($sFileMask, ';{2,}', ';')
	$aMasks = StringSplit($sFileMask, ';')
	
	For $i = 1 To $aMasks[0]
		If StringStripWS($aMasks[$i], 8) = "" Then
			ContinueLoop
		EndIf
		
		$sFiles &= '"' & $sPath & '\' & $aMasks[$i] & '"'
		
		If $i < $aMasks[0] Then
			$sFiles &= ';'
		EndIf
	Next
	
	$hDir = Run(@ComSpec & ' /U /C DIR ' & $sFiles & ' /B' & $sAttrib, @SystemDir, @SW_HIDE, 6)
	
	While ProcessExists($hDir)
		$sRead = StdoutRead($hDir, False, True)
		
		If @error Then
			ExitLoop
		EndIf
		
		If $sRead <> "" Then
			$sOut &= $sRead
		EndIf
	Wend
	
	$aOut = StringRegExp(BinaryToString($sOut, 2), '[^\r\n]+', 3)
	
	If @error Or UBound($aOut) < 2 Then
		Return SetError(2, 0, 0)
	EndIf
	
	$aOut[0] = UBound($aOut)-1
	Return $aOut
EndFunc


И пример:

Код:
#include <Array.au3>

$iTimer = TimerInit()
$aReturn = _FileDirList(@DesktopDir, "*.au3;*.txt")
ConsoleWrite(TimerDiff($iTimer) & @LF)

_ArrayDisplay($aReturn)
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,323
CreatoR,
Может имеет смысл еще добавить проверку на папку?
Код:
;...
If Not StringInStr(FileGetAttrib($sPath), 'D') Then
	Return SetError(1, 0, 0)
EndIf
;...
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,323
CreatoR,
Вы не объявили в локальных переменных $aMasks и вылазит ошибка, если использовать
Код:
Opt('MustDeclareVars', 1)
 

AZJIO

Меценат
Меценат
Сообщения
2,894
Репутация
1,196
Обнаружил интересное поведение...
Уже известно, что при маске *.xls в результат попадает *.xlsx и другие *.xls*
Если указать имя файла, например "*пример*.xls", то файлы подпадающие под маску "*пример*.xlsx" уже не будут найдены.
 

bic

Знающий
Сообщения
46
Репутация
14
Зависает функция.

Код:
#include <_FileDirList.au3>
Opt("TrayIconDebug", 1) ;0=не показать отладку, 1=показать отладку

$i = 0

While 1
	_FileDirList('C:\PerfLogs\')
	$i += 1
	ConsoleWrite($i & @CRLF)
	Sleep(100)
WEnd


TrayIconDebug показывает, что зависание происходит на 78-й строке.
Причем зависает так, что из скрипта, который использовал эту функцию, выйти нельзя, только убить процесс. Зависает всегда через разное время.
Методом научного тыка исправил эту проблему, поставив в цикл задержку в 10 мс. По многочисленным тестам, работает исправно

Код:
While 1
        $sRead = StdoutRead($hDir, False, True)

        If @error Then
            ExitLoop
        EndIf

        If $sRead <> "" Then
            $sOut &= $sRead
        EndIf

		Sleep(10)
    Wend
 
Автор
CreatoR

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,486

bic

Знающий
Сообщения
46
Репутация
14
Функция намертво зависает. Целевая директория может быть пустой. Это не имеет значения.
А по другому исправить эту ошибку я не знаю как.
 
Автор
CreatoR

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,486
bic [?]
Функция намертво зависает. Целевая директория может быть пустой. Это не имеет значения.
А что ты этим примером пытаешься добиться?
 

bic

Знающий
Сообщения
46
Репутация
14
Пример - мониторинг директории через определенное время. А в консоли видно, что скрипт завис. Причем ни интервал, ни наличие файлов роли не играют.
Я просто подсказал решение от зависания функции. Может кому пригодится.
Или ты считаешь такое поведение функции нормальным?
 

joiner

Модератор
Локальный модератор
Сообщения
3,557
Репутация
628
проверил, не заметил зависания. цикл отрабатывается полностью без спотык. особенно на пустой папке :smile:
в основном процессор напрягается cmd.exe. можно заметить в диспетчере задач. и предлагаемая пауза не снижает потребляемости ресурсов. но любой поиск тянет на себя ресурс..в любой программе. хочешь быстрее значит жди. хочешь без напряга на систему, то будет в несколько раз дольше искать
в твоем случае основной напряг вызывает основной цикл, в котором ты запустил поиск
 
Автор
CreatoR

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,486
bic [?]
Пример - мониторинг директории через определенное время.
Плохой пример.
Для этого есть другие методы.

в консоли видно, что скрипт завис
Проблема скорее всего в процессе cmd...

Я просто подсказал решение от зависания функции
Лучше заменить While 1 на While ProcessExists($hDir).
 

AZJIO

Меценат
Меценат
Сообщения
2,894
Репутация
1,196
Сейчас проверил аналогичную функцию от KaFu с маской
Код:
(@WindowsDir, "s*.???.*")

выдало 1631 результат, проверил _FileDirList, результат такой же. Остаётся непонятным вопрос, в маске находятся две точки, а результат содержит некоторые элементы совсем без точек.
 

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
Остаётся непонятным вопрос, в маске находятся две точки, а результат содержит некоторые элементы совсем без точек.

Как бы... http://autoit-script.ru/index.php/topic,4901.0.html
"." - соответствует любому одиночному символу, кроме символа новой строки
 

AZJIO

Меценат
Меценат
Сообщения
2,894
Репутация
1,196
C2H5OH
Не хочу хвастаться, но эта справка была написана после того, как я написал свою справку в RegExp, которую вы видите в официальной русской справке. Ну и второе, маска в _FileDirList не является регулярным выражением, в курсе что такое wildcards? Думаю по этому правилу в файле не может быть 2 расширения, то бишь две точки, отсюда вытекает что вторая точка либо игнорируется либо ищет по двум маскам (не тестил).
 

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
AZJIO,
да, я уже понял что речь не о регулярных выражениях.
Но у меня тут такие фокус команда DIR показывает...
Ща потестю немного, напишу.


Добавлено:
Сообщение автоматически объединено:

Вот так хитро работает DIR

DOS
[box title=DOS] Volume in drive C has no label
Volume Serial Number is
Directory of C:\DOCS\DDD

213276~1 LIT 56 25.06.12 15:06
213289~1 DAT 27 25.06.12 17:23
213289~1 LIT 56 25.06.12 15:06
213328~1 DAT 27 25.06.12 17:23
213328~1 LIT 56 25.06.12 15:06
213666~1 LIT 56 25.06.12 15:06
213675~1 DAT 27 25.06.12 17:23
213675~1 LIT 56 25.06.12 15:06
213676~1 LIT 56 25.06.12 15:06
213700~1 DAT 27 25.06.12 17:23
213700~1 LIT 56 25.06.12 15:06
213829~1 DAT 27 25.06.12 17:23
213829~1 LIT 56 25.06.12 15:06
213829~2 DAT 27 25.06.12 17:23
213829~2 LIT 56 06.03.12 16:43
213829~3 DAT 27 25.06.12 17:23
213829~3 LIT 56 25.06.12 15:06
213829~4 LIT 56 25.06.12 15:06
213830~1 DAT 27 25.06.12 17:23
213830~1 LIT 56 25.06.12 15:06
213A66~1 LIT 56 25.06.12 15:06
213A76~1 LIT 56 25.06.12 15:06
213C72~1 LIT 56 25.06.12 15:06
213E66~1 LIT 56 25.06.12 15:06
24 file(s) 1112 bytes
1023932928 bytes free[/box]

XP
[box title=XP] Том в устройстве C не имеет метки.
Серийный номер тома:

Содержимое папки C:\DOCs\ddd

25.06.2012 15:06 56 212663303.lit
25.06.2012 17:22 27 213289791.dat
25.06.2012 15:06 56 213289791.lit
25.06.2012 17:22 27 213328710.dat
25.06.2012 15:06 56 213328710.lit
25.06.2012 15:06 56 212663300.lit
25.06.2012 17:22 27 213675926.dat
25.06.2012 15:06 56 213675926.lit
25.06.2012 15:06 56 212663304.lit
25.06.2012 17:22 27 213700639.dat
25.06.2012 15:06 56 213700639.lit
25.06.2012 17:22 27 213829790.dat
25.06.2012 15:06 56 213829790.lit
06.03.2012 16:43 56 213829792.lit
25.06.2012 17:22 27 213829795.dat
25.06.2012 15:06 56 213829795.lit
25.06.2012 17:22 27 213829796.dat
25.06.2012 15:06 56 213829796.lit
25.06.2012 17:22 27 213830738.dat
25.06.2012 15:06 56 213830738.lit
25.06.2012 15:06 56 212663301.lit
25.06.2012 15:06 56 212663305.lit
25.06.2012 15:06 56 212661236.lit
25.06.2012 15:06 56 212663302.lit
24 файлов 1 112 байт
0 папок 27 871 862 784 байт свободно[/box]

Под ХР DIR 213*.* даёт в списке файлы, которые начинаются на 212. Та же самая команда в DOS в том же каталоге, даёт фантастический результат, который объясняет почему эти файлы показываются по такому шаблону. Но почему происходит такая конвертация - загадка. Файлы реально называются так как показано в ХР-шном списке.
Может и с точками похожая ерунда?
 

Ромка Пупков

Новичок
Сообщения
25
Репутация
1
используя Вашу функцию в коде, требуется использовать конструкцию

Код:
$_eng = _FileDirList(@ScriptDir, '*conflicted copy*', 1)
For $i = 1 To $_eng[0]
	FileDelete($_eng[$i])
Next


однако, если файлов по маске найти не удалось, то вылезает ошибка

Код:
"P:\DropboxPortableAHK\Dropbox\ChromePortable\test.au3" (6) : ==> Subscript used on non-accessible variable.:
For $i = 1 To $_eng[0]
For $i = 1 To $_eng^ ERROR
->15:21:21 AutoIt3.exe ended.rc:1

и код дальше не выполняется. проблема решилась комментированием строк

Код:
If @error Or UBound($aOut) < 2 Then
        Return SetError(2, 0, 0)
EndIf

мне кажется, нужно изменить их
 

erlik

Продвинутый
Сообщения
317
Репутация
84
Используйте _RecFileListToArray.
Для сравнения:
Код:
_FileDirList:             30 тыс файлов - 14985 мск
_RecFileListToArray: 30 тыс файлов - 2352 мск.

_FileDirList:             100 тыс файлов - 30406 мск
_RecFileListToArray: 100 тыс файлов - 8352 мск.
Вывод вполне очевиден - лично я давно променял первую на вторую.
 
Верх