Jump to content

Recursive File Find Function


avery
 Share

Recommended Posts

Hello au3 Gurus'

I normally code in PERL but slowly moving over to au3 for my projects.

In PERL I normally use

use File::Find;
Find(\&findFiles, $path2Search);

And that would give me everything (folders and files) under that path.

In au3 I've tried to code the logic myself by using FilefindFirst and then saying

If the item is a folder then to run the function for that folder an so forth but I

end up with endless loops and broken code. It makes my brain hurt. Can anyone please help

or have a UDF for this?

; Shows the filenames of all files in the current directory
$search = FileFindFirstFile("c:\abox\temp\*.*")

; Check if the search was successful
If $search = -1 Then
    MsgBox(0, "Error", "No files/directories matched the search pattern")
    Exit
EndIf

While 1
    $file = FileFindNextFile($search)
    If @error Then ExitLoop

    ConsoleWrite("var => " & $file & @CR)
WEnd

; Close the search handle
FileClose($search)

Respectfully,

Avery Howell

www.abox.orgAvery HowellVisit My AutoIt Websitehttp://www.abox.org
Link to comment
Share on other sites

I will look at the examples now. While I was still looking for alternate solutions I came across maybe using an external command:

C:\abox>dir /Q /S /B

Shows all files and does it very quickly. However, I have never tried to capture and parse the console output in au3 or if it's even possible.

Does anyone have a suggestion on the pro's and cons of using an external command like "dir" are?

Thanks for the fast replies. I will check out the recursive examples. I forgot to mention I did a search before I posted and didn't find anything that would suffice. I will click these links before I go any further. Thanks!

Avery Howell

Edited by avery
www.abox.orgAvery HowellVisit My AutoIt Websitehttp://www.abox.org
Link to comment
Share on other sites

Func _Find ($s, $d = @HomeDrive)
   If StringRight ($d, 1) <> "\" Then $d &= "\"
   Local $h = FileFindFirstFile ($d & "*")
   If $h = -1 Then Return 0
   while 1
      $t = FileFindNextFile ($h)
      If $t = $s Then Return $d & $t
      $t = $d & $t
      If @Error Then Return 0 * FileClose ($h)
      If StringInStr (FileGetAttrib ($t), "D") Then
         $tmp = _Find ($s, $t)
         If $tmp <> "0" Then Return $tmp
         ContinueLoop
      EndIf
   WEnd
   FileClose ($h)
   Return 0
EndFunc ; ==> _Find

Func _FindAll ($s, $d = @HomeDrive)
   If Not IsDeclared ("sRet") Then
      Global $FIND_RETURN = ""
   EndIf
   If StringRight ($d, 1) <> "\" Then $d &= "\"
   Local $h = FileFindFirstFile ($d & "*")
   If $h = -1 Then Return ""
   while 1
      $t = FileFindNextFile ($h)
      If @Error Then ExitLoop
      If $t = $s Then $FIND_RETURN &= @CRLF & $d & $t
      $t = $d & $t
      If StringInStr (FileGetAttrib ($t), "D") Then _FindAll ($s, $t)
   WEnd
   FileClose ($h)
   Return $FIND_RETURN
EndFunc ; ==> _FindAll

Are the ones I use (by me), A good alternative would be to use Cmd and read StdOut.

Local $sCommand = "dir C:\test.au3 /w/o/s/p"

MsgBox (0, "text files in drive C:", _RunCmd ($sCommand))

Func _RunCmd ($sCommand)
   If StringLeft ($sCommand, 1) = " " Then $sCommand = " " & $sCommand

   Local $nPid = Run (@Comspec & " /c " & $sCommand, "", @SW_Hide, 8), $sRet = ""
   If @Error then Return "ERROR:" & @ERROR
   ProcessWait ($nPid)
   While 1
    $sRet &= StdoutRead($nPID)
    If @error Or (Not ProcessExists ($nPid)) Then ExitLoop
   WEnd
   Return $sRet
EndFunc ; ==> _RunCmd

It takes a while to complete, and I have not bothered to pass the return yet.

Mat

Edited by Mat
Link to comment
Share on other sites

My Solution: http://www.autoitscript.com/forum/index.php?showtopic=33930&st=20

Thanks! Gotta Love the au3 community (:

@Mat: I didn't see your post until I already made this one, sorry. I will check out those. they look much smaller/shorter then the one I have. Thanks!

Credit goes to Smoke_N

Func _FileListToArrayEx($s_path, $s_mask = "*.*", $i_flag = 0, $s_exclude = -1, $f_recurse = True, $f_full_path = True)

    If FileExists($s_path) = 0 Then Return SetError(1, 1, 0)

    ; Strip trailing backslash, and add one after to make sure there's only one
    $s_path = StringRegExpReplace($s_path, "[\\/]+\z", "") & "\"

    ; Set all defaults
    If $s_mask = -1 Or $s_mask = Default Then $s_mask = "*.*"
    If $i_flag = -1 Or $i_flag = Default Then $i_flag = 0
    If $s_exclude = -1 Or $s_exclude = Default Then $s_exclude = ""

    ; Look for bad chars
    If StringRegExp($s_mask, "[\\/ :> <\|]") Or StringRegExp($s_exclude, "[\\/ :> <\|]") Then
        Return SetError(2, 2, 0)
    EndIf

    ; Strip leading spaces between semi colon delimiter
    $s_mask = StringRegExpReplace($s_mask, "\s*;\s*", ";")
    If $s_exclude Then $s_exclude = StringRegExpReplace($s_exclude, "\s*;\s*", ";")

    ; Confirm mask has something in it
    If StringStripWS($s_mask, 8) = "" Then Return SetError(2, 2, 0)
    If $i_flag < 0 Or $i_flag > 2 Then Return SetError(3, 3, 0)

    ; Validate and create path + mask params
    Local $a_split = StringSplit($s_mask, ";"), $s_hold_split = ""
    For $i = 1 To $a_split[0]
        If StringStripWS($a_split[$i], 8) = "" Then ContinueLoop
        If StringRegExp($a_split[$i], "^\..*?\..*?\z") Then
            $a_split[$i] &= "*" & $a_split[$i]
        EndIf
        $s_hold_split &= '"' & $s_path & $a_split[$i] & '" '
    Next
    $s_hold_split = StringTrimRight($s_hold_split, 1)
    If $s_hold_split = "" Then $s_hold_split = '"' & $s_path & '*.*"'

    Local $i_pid, $s_stdout, $s_hold_out, $s_dir_file_only = "", $s_recurse = "/s "
    If $i_flag = 1 Then $s_dir_file_only = ":-d"
    If $i_flag = 2 Then $s_dir_file_only = " :D "
    If Not $f_recurse Then $s_recurse = ""

    $i_pid = Run(@ComSpec & " /c dir /b " & $s_recurse & "/a" & $s_dir_file_only & " " & $s_hold_split, "", @SW_HIDE, 4 + 2)

    While 1
        $s_stdout = StdoutRead($i_pid)
        If @error Then ExitLoop
        $s_hold_out &= $s_stdout
    WEnd

    $s_hold_out = StringRegExpReplace($s_hold_out, "\v+\z", "")
    If Not $s_hold_out Then Return SetError(4, 4, 0)

    ; Parse data and find matches based on flags
    Local $a_fsplit = StringSplit(StringStripCR($s_hold_out), @LF), $s_hold_ret
    $s_hold_out = ""

    If $s_exclude Then $s_exclude = StringReplace(StringReplace($s_exclude, "*", ".*?"), ";", "|")

    For $i = 1 To $a_fsplit[0]
        If $s_exclude And StringRegExp(StringRegExpReplace( _
            $a_fsplit[$i], "(.*?[\\/]+)*(.*?\z)", "\2"), "(?i)" & $s_exclude) Then ContinueLoop
        If StringRegExp($a_fsplit[$i], "^\w:[\\/]+") = 0 Then $a_fsplit[$i] = $s_path & $a_fsplit[$i]
        If $f_full_path Then
            $s_hold_ret &= $a_fsplit[$i] & Chr(1)
        Else
            $s_hold_ret &= StringRegExpReplace($a_fsplit[$i], "((?:.*?[\\/]+)*)(.*?\z)", "$2") & Chr(1)
        EndIf
    Next

    $s_hold_ret = StringTrimRight($s_hold_ret, 1)
    If $s_hold_ret = "" Then Return SetError(5, 5, 0)

    Return StringSplit($s_hold_ret, Chr(1))
EndFunc
Edited by avery
www.abox.orgAvery HowellVisit My AutoIt Websitehttp://www.abox.org
Link to comment
Share on other sites

Have you tried using the native DOS command and shunting the output to a text file?

Something like:

#Include <Array.au3>
#Include <File.au3>

Global $sHDD, $sfilename, $aOutput
$sHDD = "C:\"
$sfilename = "HOSTS"
RunWait(@ComSpec & " /c " & "dir " & $sHDD & $sfilename & " /B /S > output.txt", @DesktopDir, @SW_HIDE)
_FileReadToArray(@DesktopDir & "\output.txt", $aOutput)
If FileExists(@DesktopDir & "\output.txt") Then FileDelete(@DesktopDir & "\output.txt")
_ArrayDisplay($aOutput)
Link to comment
Share on other sites

  • 1 month later...
whatever Edited by MvGulik

"Straight_and_Crooked_Thinking" : A "classic guide to ferreting out untruths, half-truths, and other distortions of facts in political and social discussions."
"The Secrets of Quantum Physics" : New and excellent 2 part documentary on Quantum Physics by Jim Al-Khalili. (Dec 2014)

"Believing what you know ain't so" ...

Knock Knock ...
 

Link to comment
Share on other sites

Here a recursive variant:

#include <Array.au3>
Dim $ergebnis[1]

$path = @WindowsDir
$file = ".log" ;to search for any file use "*"
;~ $ergebnis = File_Seeker($path, $file, $ergebnis, 0, 1) ;searchs for ".log" (exact search)
;~ $ergebnis = File_Seeker($path, $file, $ergebnis, 0, 0, 1, 0) ;searchs for "*.log*" recursively but dir names are excluded
;~ $ergebnis = File_Seeker($path, $file, $ergebnis, 0, 0, 0, 0) ;searchs for "*.log*" but not recursive and no dir names
$ergebnis = File_Seeker($path, $file, $ergebnis, 1) ;searchs for files with extension .log but dir names are excluded


If Not @error Then
    ;~ _ArraySort($ergebnis)
    _ArrayDisplay($ergebnis)
;~  Local $log
;~  For $i = 1 To UBound($ergebnis) - 1
;~      $log &= $ergebnis[$i] & @CRLF
;~  Next
;~  $file = FileOpen(@ScriptDir & "\Ergebnis.log", 1)
;~  FileWrite($file, $log)
;~  FileClose($file)
EndIf

Exit


;==================================================================================================
; Function Name:    File_Seeker($path, $file, $array)
; Description:          searchs for a file recursively on given drive
; Parameter(s):         $path   path where to search
;                               $file   filename to search for
;                               $array  to save the search results
;                               $ext    search for file extension, e.g. .mp3
;                               $exact  to search for exact file name (not case sensitive)
;                               $rec    to search recursively for files and folders
;                               $folder include also folder names to search pattern
; Return Value(s):  an array with the full path of the files, $array[0] = amount of found files
; Version:                  v0.85 Build 2010-11-30 Beta
; Author(s):                UEZ
;==================================================================================================
Func File_Seeker($path, $file, ByRef $aResults, $ext = 0, $exact = 0, $rec = 1, $folder = 0)
    Local $extended
    If $file <> "" And IsArray($aResults) And BitAND($ext, $exact) <> 1 Then
        If StringLeft($path, 2) = "\\" Or (StringRegExp(StringLeft($path, 1), "(?i:[A-Z])") And StringMid($path, 2, 1) = ":") Then
            If StringRight($path, 1) <> "\" Then $path &= "\"
            Local $seek, $suffix = ""
            Local $search = FileFindFirstFile($path & "*")
            If $search = -1 Then
                Return SetError(2)
            EndIf
            While 1
                $seek = FileFindNextFile($search)
                $extended = @extended
                If @error Then ExitLoop
                If $extended And $rec Then File_Seeker($path & $seek & "\", $file, $aResults, $ext, $exact, $rec, $folder)
                If $exact And Not $ext Then
                    If $seek = $file Then _ArrayAdd($aResults, $path & $seek)
                Else
                    If $ext Then
                        If StringRegExp($seek, "(?i:\" & $file & "+$\b)") And StringLeft($file, 1) = "." Then
                            If ($extended And $folder) Or Not $extended Then _ArrayAdd($aResults, $path & $seek)
                        EndIf
                    Else
                        If $file = "*" Then
                            If ($extended And $folder) Or Not $extended Then _ArrayAdd($aResults, $path & $seek)
                        Else
                            If StringInStr($file, ".") Then
                                If StringRegExp($seek, "(?i:\" & $file & ")+") Then
                                    If ($extended And $folder) Or Not $extended Then _ArrayAdd($aResults, $path & $seek)
                                EndIf
                            Else
                                If StringRegExp($seek, "(?i:" & $file & ")+") Then
                                    If ($extended And $folder) Or Not $extended Then _ArrayAdd($aResults, $path & $seek)
                                EndIf
                            EndIf
                        EndIf
                    EndIf
                EndIf
            WEnd
            FileClose($search)
            $aResults[0] = UBound($aResults) - 1
            Return SetError(0, 0, $aResults)
        Else
            Return SetError(3)
        EndIf
    Else
        Return SetError(1)
    EndIf
EndFunc

Regards,

UEZ

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to comment
Share on other sites

  • 4 weeks later...

UEZ:

Thanks for the code... Unfortunately, I can not get this to work recurrsively...

I did not want to run this against my entire c:\, so I created a c:\2 folder. In there I created Several Sub Folders, and Additional Sub Folders below that.

When I run this script against the c:\2\ folder it only returns 1 file from the c:\2\ folder, and nothing else. (Should return 3 files from each folder)

Problem is, I dont see in the code where the problem might be...

------------------------------------------------------------

Just figured it out. The Version of AutoIt I am using is 3.2.2.0... Somewhere between 3.2.2.0 and 3.3.2.0 the FileFindNext function changed and now sets @extended..

Thanks!

(I could cancel the post, but maybe it will help others.)

Link to comment
Share on other sites

I just updated the code from post#10 because I had some bugs with the RegEx function :mellow:

Please test it again.

Some examples are at the top of the code how to use it!

Thanks,

UEZ

PS: it is still beta and more testing is needed!!!

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...