Jump to content

[SOLVED] Only specific 'level' folders using _FileListToArrayRec?

Recommended Posts

Hi all, 

I'm trying to make a listing of all files in subdirectories on a specific level in a folder structure. Let me explain, visually, this will help things a lot.

I have a folder structure like this:

|--- SUBDIR 1
|      |---- SUBDIR 1.1
|               |----- SUBDIR 1.1.1
|                          |---- File1.ext
|               |----- SUBDIR 1.1.2
|                          |---- File2.ext
|               |----- SUBDIR 1.1.3
|                          |---- File2.ext
|               |----- SUBDIR 1.1.4
|                          |---- File2.ext
|               |----- SUBDIR 1.1.5
|                          |---- File2.ext
|      |---- SUBDIR 1.2
|               |----- SUBDIR 1.2.1
|                   .....
|      |---- SUBDIR 1.3

I use _FileListToArrayRec twice:
- once to make an array of the specific directories I should be working at: I need all files on the x.x level, so it will go just until that depth using a negative integer for $iRecur
- once again to create an array of all files found under that directory and its subdirectories (level x.x.x\files...)

What happens now is that _FileListToArrayRec will always include all levels before the maximum depth is reached. The result would look like this

Row 0   15
Row 1   Root\Subdir 1
Row 2   Root\Subdir 2
Row 3   Root\Subdir 3
Row 4   Root\Subdir 1\Subdir 1.1
Row 5   Root\Subdir 1\Subdir 1.2
Row 6   Root\Subdir 1\Subdir 1.3

Needless to say that when my second function iterates over this array, it will find all files twice. Once on the x level, once again on the x.x level. There is no way for me not to use the recursive option in the second iteration, since the files are actually in a subdirectory there.

Where are the wizards of programming logic here? Since I can't seem to find a comprehensible or easily implementable solution for this issue.

Thanks in advance and kind regards,


Edited by jantograaf
Link to post
Share on other sites

Alright, I'm not marking this solved yet, but there might be a solution I just thought of.

Maybe I just need to do the first _FileListToArrayRec twice. Once on the level needed, once on a level just above that. And then write a little loop that searches and deletes all the lines of the second array from the first array? 🧐

Just thinking out loud here...

Link to post
Share on other sites

Okay, so this solved the issue. Nevermind. Just keeping this here for reference so other people can see the solution I thought best 🙂

If $ArrayDepth <= -1 Then
    ;First we fill an array with all the folders one level up
    $LevelUpFolderArray = _FileListToArrayRec($DCMFolder,"*",$FLTAR_FOLDERS,$ArrayDepth + 1,$FLTAR_SORT,$FLTAR_FULLPATH)
    ;If this provides a result (it always should, since this is only run when the $FolderArray contains any lines)
    If IsArray($LevelUpFolderArray) Then
        ;Using an DeleteCounter to keep track of the rows where we left off deleting
        ;Doing this since _BinarySearch did not work for me but the arrays ARE sorted
        Local $ArrayDeleteCounter = 0
        Local $FoundInRow = 0
        For $i = 1 To $LevelUpFolderArray[0]
            ;For each line in $LevelUpFolderArray, delete the corresponding line in $FolderArray
            $FoundInRow = _ArraySearch($FolderArray,$LevelUpFolderArray[$i],$ArrayDeleteCounter)
            If $FoundInRow > 0 Then
                ;Set the position to this found item
                $ArrayDeleteCounter = $FoundInRow
                ;Delete the row
                ;Adjust the element count in the FolderArray
                $FolderArray[0] = $FolderArray[0] - 1
                ;Reset the FoundInRow-variable
                $FoundInRow = 0


Edited by jantograaf
Included solution code...
Link to post
Share on other sites
  • Developers


You could also build your own recursive UDF to get this done. Something like this should be close:

Func FindFilesAtLevel($filepath, $targetLevel = 0, $filemask = "", $level = 1)
    If StringRight($filepath, 1) <> "\" Then $filepath &= "\"
    Local $hSearch = FileFindFirstFile($filepath & "*.*")
    If $hSearch = -1 Then
        ; ConsoleWrite("No files/directories matched the search pattern:" & $filepath & "*.*" & @CRLF)
        Return False
    Local $sFileName = "", $iResult = 0
    While 1
        $sFileName = FileFindNextFile($hSearch)
        If @error Then ExitLoop

        ; directory found higher level -> go one level deeper
        If ($level < $targetLevel Or $targetLevel = 0) And StringInStr(FileGetAttrib($filepath & $sFileName), "D") Then
            FindFilesAtLevel($filepath & $sFileName, $targetLevel, $filemask, $level + 1)

        ; File found  at appropriate level -> do what needs to be done
        If ($level = $targetLevel Or $targetLevel = 0) And Not StringInStr(FileGetAttrib($filepath & $sFileName), "D") And StringRegExp($sFileName, $filemask) Then
            ConsoleWrite('@@ FileName = ' & $filepath & $sFileName & @CRLF) ;### Debug Console
    ; Close the search handle.
EndFunc   ;==>FindFilesAtLevel

Which could be used as follows:

ConsoleWrite("! Example 1: List all file in c:\temp and subdirectories" & @CRLF)
$FindRoot = "c:\temp"
FindFilesAtLevel($FindRoot)        ; List all files from FindRoot

ConsoleWrite("! Example 2: List all file in c:\temp\*\" & @CRLF)
$FindRoot = "c:\temp"
FindFilesAtLevel($FindRoot, 2)

ConsoleWrite("! Example 2b: List all files in c:\temp\*\*" & @CRLF)
$FindRoot = "c:\temp"
FindFilesAtLevel($FindRoot, 3)

ConsoleWrite("! Example 3: List all *.JPG files in c:\temp\ and subdirectories" & @CRLF)
$FindRoot = "c:\temp"
$FindFiles = ".?\.jpg"      ; RegEx filter, use "" for all files
FindFilesAtLevel($FindRoot, 0, $FindFiles)

ConsoleWrite("! Example 3b: List all *.JPG files in c:\temp\*\*" & @CRLF)
$FindRoot = "c:\temp"
$FindFiles = ".?\.jpg"      ; RegEx filter, use "" for all files
FindFilesAtLevel($FindRoot, 3, $FindFiles)

Example 2 is what you are looking for I guess. ;) 


Edited by Jos

SciTE4AutoIt3 Full installer Download page   - Beta files       Read before posting     How to post scriptsource   Forum etiquette  Forum Rules 
Live for the present,
Dream of the future,
Learn from the past.

Link to post
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
  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By TheAutomator
      I'm writing a recursive decent parser in Autoit!
      The programming language i'm making is called HighLevel.
      I'm doing this for learning purposes, because it's fun and because I can implement it into my other project:
      Fullscreen Console With custom programming language!
      It's not easy...

      In Autoit you don't have objects like in Java or Visual Basic, so I had to figure out a way to still convert the code to an abstract syntax tree.
      I used nested array's and array based dictionary's instead of objects.
      The code is still very dirty and I need to make a lot of modifications but if you're careful with testing you'll see what it can do already.
      Console window
      Because this code eventually will get implemented into my console project I crafted a nice little console window (with a custom sci-fi looking theme, yeah i was a little bored haha).
      {ESC} is your panic button for now, it terminates the script completely.
      If you get an error while opening a script the text will turn red.
      To minimize it press the blue button, to close it use the red one, to drag the gui just grab it on one of the sides.
      The console window will display what you write to it with your "HighLevel-script" and some additional information:

      How to test it:
      Download: HighLevel.Au3, Debug.Au3 (includes a function to display nested arrays for debugging), GUI.bmp (for the console)
      Compile the Autoit code to EXE.
      The GUI.bmp must be in the same folder as the EXE file!
      Write a HighLevel-script (text file) and drag it into the compiled autoit-exe.
      The custom made little console window will pop up in the left top corner of your screen and your HighLevel-script (the text file) will be interpreted and executed.
      The Language:
      exit script:     Abort      show / hide the console:     Show     Hide      write to/clear the console:     Write 'this is a ''string''!'     Clear variables: test_var_1 = 123 some_list = ['a', 5, true] some_list[1] = 3 math = 1 + 2 * 3 / 4 - -5 & test_var beep (under construction):     Beep F, optD wait X seconds:     Wait X      Messages:     Message 'Hello World!'      move/click the mouse:     Move X, Y     Click      send keys (under construction):     Send 'HighLevel', True      if's:     If false     ElseIf true         # this part will run     Else     End subs:     Sub X         # do stuff     End     Call X      for loops:     For X = 1 to 10         # X iterates     End Values:     Input 'Give me input'     Random     YesNo 'yes or no' operators:     + - * / & > = ! < ( ) And Not Or  
      Example script:
      # my first HighLevel script message 'Hello World!' message 'Lets write to the console...' clear # clear the console... list = ['a', 16, true] for i = 0 to 2     write list[i]     wait 1 end sub test     if YesNo 'would you like to quit?'         message 'Goodbye!'         abort     else         write 1 + 2 * 3 & ' math!'     end end call test  
      test script.HighLevel
    • By TryWare90Days
      Can anybody please help about how to exclude subfolders with the _FileListToArrayRec function?

      According to the F1 helpfile this should create an array including subfolders, where subfolders like Include should be excluded:
          ; And now ignoring the "Include" folder #include <Array.au3>     $aArray = _FileListToArrayRec($sAutoItDir, "*||include", $FLTAR_FILESFOLDERS, $FLTAR_RECUR, $FLTAR_SORT)     _ArrayDisplay($aArray, "No 'Include' folder") But it doesn't work if I change it to:
          ; And now ignoring the "Windows" folder #include <Array.au3>     $aArray = _FileListToArrayRec("C:\", "*||Windows", $FLTAR_FILESFOLDERS, $FLTAR_RECUR, $FLTAR_SORT)     _ArrayDisplay($aArray, "No 'Windows' folder")
      But this is what I really want to do: Create an array only with all filenames including full path, for all root and subfolders, sorted, but without system and hidden files, and without the following root and subfolders: C:\ $Recycle.Bin, C:\ Program Files, C:\ Program Files (x86) , C:\Users, C:\Windows
      #include <Array.au3>
      Global $aArray = _FileListToArrayRec("C:\", "*||$Recycle.Bin||" & Chr(34) & "Program Files" & Chr(34) & "||" & Chr(34) & "Program Files (x86)" & Chr(34) & "||Users||Windows", $FLTAR_FILES+$FLTAR_NOSYSTEM+$FLTAR_NOHIDDEN, $FLTAR_RECUR, $FLTAR_FASTSORT, $FLTAR_FULLPATH)
      _ArrayDisplay($aArray, "_ArrayDisplay is only showing the first 65.525 rows of the $aArray")
      ;    Please note: $aArray[0] shows 284.868 rows created.
      Please note, that my code above can be compiled without any syntax errors, and can run without any syntax errors, but the created array doesn't exclude any of the wanted subfolders.
    • By 232showtime
      is there a limitation for _FileListToArrayRec? I tried to a do _FileListToArrayRec in C: Directory and it takes forever for _ArrayDisplay
    • By jguinch
      In a folder with some files that have no extension (for example the hosts file in C:\Windows\System32\driversetc), FileListToArrayRec does not return the same thing than _FileListToArray if I use *.* as filter :
         _FileListToArray("C:\Windows\System32\driversetc", "*") : returns all files
         _FileListToArray("C:\Windows\System32\driversetc", "*.*") : returns all files
         _FileListToArrayRec("C:\Windows\System32\driversetc", "*") : returns all files
         _FileListToArrayRec("C:\Windows\System32\driversetc", "*.*") : returns only files with extensions
      It seems logic that FileListToArrayRec and FileListToArray should have the same behaviour with the same filter, no ?
      It's not important at all for me, but maybe developers will want to ensure that these two functions react in the same way...
      Edit : sorry if this post is not in the good section. Please move it if needed.
    • By RichE
      Hi Guys/Gals,
      I'm using the AD UDF to interogate our AD (the computers section, which we have under an OU of managed), what I'm trying to do (and failing horribly) is recurse through the sub OU's and place them inside a treeview container.
      e.g Access |
                        |- Students
      but I can't get it to go past the first sub OU, and it's creating duplicates...
      my code is below
      #cs ---------------------------------------------------------------------------- AutoIt Version: Author: myName Script Function: Template AutoIt script. #ce ---------------------------------------------------------------------------- ; Script Start - Add your code below here #include <GUIConstantsEx.au3> #include <ListViewConstants.au3> #include <ProgressConstants.au3> #include <TreeViewConstants.au3> #include <WindowsConstants.au3> #include <AD.au3> $ADToolbox = GUICreate("ADToolbox", 1245, 789, 192, 124) $Root = GUICtrlCreateTreeView(8, 8, 241, 769, BitOR($GUI_SS_DEFAULT_TREEVIEW,$WS_BORDER)) $Itemspane = GUICtrlCreateListView("", 256, 8, 801, 681) $taskprog = GUICtrlCreateProgress(256, 696, 801, 25, BitOR($PBS_SMOOTH,$PBS_MARQUEE,$WS_BORDER)) GUISetState(@SW_SHOW) _AD_Open() Global $aOUs = _AD_GetAllOUs("OU=Computers,OU=Managed,DC=SCHOOL,DC=LOCAL", "", 0) If @error > 0 Then MsgBox(48, "Active Directory Functions", "No OUs could be found") Else $iCount = 2 Do $sOU = "ou=" & StringReplace($aOUs[$iCount - 1][0], "\", ",ou=") & "," & $sAD_DNSDomain $trunk = stringsplit($sOU, ",") if $trunk[0] > 4 Then $leafcount = 0 $branchno = 0 do if $branchno = 0 then if $trunk[5] <> "DC=Local" and $trunk[5] <> "DC=SCHOOL" then $branch = GUICtrlCreateTreeViewItem($trunk[5], $root) $branchno = 1 $leafcount = $leafcount + 1 else $leafcount = $leafcount + 1 EndIf Else $count = 0 $leafname = 0 do if $leafname = 0 Then if $trunk[6] <> "DC=Local" and $trunk[6] <> "DC=SCHOOL" then $leaf = GUICtrlCreateTreeViewItem($trunk[6], $Branch) $leafname = 1 $count = $count + 1 else $count = $count + 1 $leafname = 0 EndIf endif until $count = $trunk[0] $leafname=0 $branchno = 0 $count = 0 EndIf until $leafcount = $trunk[0] $leafcount = 0 $branchno = 0 EndIf $iCount=$iCount+1 until $iCount = $aOUs[0][0] EndIf While 1 $nMsg = GUIGetMsg() Switch $nMsg Case $GUI_EVENT_CLOSE Exit EndSwitch WEnd thanks in advance
  • Create New...