I want to do the following:
Prompt user to choose a folder
Loop through folder (and subfolders if they exist)
Get all .xlsx files
Get specific column from those files (all have the same structure) and combine data from that column
I get all subfolders and all files but I get 5 times as much as I should.
L column is where I get all my data and Insert into Identical Master File (into L column).
I have 5 files - I should get 5 items in the last column, I simply add new folder in it, and same files(copied), so now I should get 10 items in the last column, instead I get 50.
Sub LoopThroughFolder()
Dim MyFile As String, Str As String, MyDir As String, Wb As Workbook
Dim Rws As Long, Rng As Range, r As Range
Set Wb = ThisWorkbook: Wb.Sheets(2).Range("L:L").ClearContents
Dim FSO As Object, fld As Object, Fil As Object
Dim wbkCS As Workbook
Dim FolderPath As String
Dim fsoFile As Object
Dim fsoFol As Object
Dim fileName As String
Dim sWb As Workbook
Dim MatchingColumn As Range
Dim MatchingRowNb As Long
MsgBox "Choose a folder: "
Application.DisplayAlerts = False
With Application.FileDialog(msoFileDialogFolderPicker)
.InitialFileName = "C:\Users\"
.AllowMultiSelect = False
If .Show <> -1 Then
MsgBox "No folder selected! Exiting script."
Exit Sub
End If
FolderPath = .SelectedItems(1)
End With
If Right(FolderPath, 1) <> "\" Then
FolderPath = FolderPath + "\"
End If
Set FSO = CreateObject("Scripting.FileSystemObject")
Set fld = FSO.GetFolder(FolderPath)
If FSO.FolderExists(fld) Then
For Each fsoFol In FSO.GetFolder(FolderPath).SubFolders
For Each fsoFile In fsoFol.Files
If Mid(fsoFile.Name, InStrRev(fsoFile.Name, ".") + 1) = "xlsx" Then
fileName = fsoFile.Name
Application.ScreenUpdating = False
MyDir = FolderPath 'fld
fileName = Dir(MyDir & "*.xlsx")
ChDir MyDir
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Do While fileName <> ""
Set sWb = Workbooks.Open(fileName)
With sWb.Worksheets(2)
Rws = .Cells(Rows.Count, 12).End(xlUp).Row
Set Rng = Range(.Cells(5, 1), .Cells(Rws, 12))
End With
With Wb.Worksheets(2)
Set MatchingColumn = .Range(.Cells(5, 1), .Cells(.Rows.Count, 1).End(xlUp))
For Each r In Rng.Rows
If r.Cells(1, 1).Value2 <> vbNullString Then 'Ignoring empty rows
If r.Rows.Hidden = False Then
'We find the row where the Ids matche
MatchingRowNb = Application.Match(r.Cells(1, 1).Value2, MatchingColumn, False)
'We add the current value in the cell with the new value comming from the other file
.Cells(4 + MatchingRowNb, 12).Value2 = .Cells(4 + MatchingRowNb, 12).Value2 + r.Cells(1, 12).Value2
End If
End If
Next
End With
sWb.Close SaveChanges:=True
Application.DisplayAlerts = True
fileName = Dir()
Loop
End If
Next
Next
End If
End Sub
You're using both FSO and Dir() to loop over the files, so that's why you're getting the same files over and over.
When your sub ends up doing a bunch of things (particularly when one thing is nested in another, and so on) then it's best to consider splitting it up, so you can concentrate on the one thing that's giving you problems, without all the other things "getting in the way".
Here's a stripped-down version to show what I mean. It works but for clarity doesn't have your file processing code.
Option Explicit
Sub LoopThroughFolder()
Dim Wb As Workbook, sWb As Workbook
Dim FolderPath As String
Dim colFiles As Collection, f
'get a folder
FolderPath = ChooseFolder()
If Len(FolderPath) = 0 Then
MsgBox "No folder selected: exiting"
Exit Sub
End If
'find all excel files in subfolders of that folder
Set colFiles = FileMatches(FolderPath, "*.xlsx")
If colFiles.Count = 0 Then
MsgBox "No xlsx files found"
Exit Sub
End If
Set Wb = ThisWorkbook
Wb.Sheets(2).Range("L:L").ClearContents
'loop over the files we found
For Each f In colFiles
Set sWb = Workbooks.Open(f.Path)
'process the file here
sWb.Close SaveChanges:=True
Next f
End Sub
Function ChooseFolder() As String
With Application.FileDialog(msoFileDialogFolderPicker)
.Title = "Choose a folder"
.InitialFileName = "C:\Users\"
.AllowMultiSelect = False
If .Show = -1 Then
ChooseFolder = .SelectedItems(1)
If Right(ChooseFolder, 1) <> "\" Then _
ChooseFolder = ChooseFolder + "\"
End If
End With
End Function
'Return a collection of file objects given a starting folder and a file pattern
' e.g. "*.txt"
'Pass False for last parameter if don't want to check subfolders
Function FileMatches(startFolder As String, filePattern As String, _
Optional subFolders As Boolean = True) As Collection
Dim fso, fldr, f, subFldr
Dim colFiles As New Collection
Dim colSub As New Collection
Set fso = CreateObject("scripting.filesystemobject")
colSub.Add startFolder
Do While colSub.Count > 0
Set fldr = fso.getfolder(colSub(1))
colSub.Remove 1
For Each f In fldr.Files 'get files in folder
If UCase(f.Name) Like UCase(filePattern) Then colFiles.Add f
Next f
If subFolders Then 'get subfolders for processing?
For Each subFldr In fldr.subFolders
colSub.Add subFldr.Path
Next subFldr
End If
Loop
Set FileMatches = colFiles
End Function
Related
The below mention code can successfully count the required tags in an XML files and also provides name of file and tag count in excel sheet. I have just one query that currently it only reads the folder individually. However if there are 300 folders in a parent folder i need to select each folder every time. Is there anyway if anyone can amend the code so that if there are 300 folders in a parent folder in read each and every file (XML) in all subfolders. This will be very helpful for me.
I have tried to do it my own but this is beyond my capacity.
Option Explicit
Sub process_folder()
Dim iRow As Long, wb As Workbook, ws As Worksheet, ar
Set wb = ThisWorkbook
Set ws = wb.Sheets(1)
ws.UsedRange.Clear
ws.Range("A1:C1") = Array("Source", "<Headline> Tag Count")
iRow = 1
' create FSO and regular expression pattern
Dim FSO As Object, ts As Object, regEx As Object, txt As String
Set FSO = CreateObject("Scripting.FileSystemObject")
Set regEx = CreateObject("VBScript.RegExp")
With regEx
.Global = True
.MultiLine = True
.IgnoreCase = True
.pattern = "<Headline>(.*)</Headline>"
End With
'Opens the folder picker dialog to allow user selection
Dim myfolder As String, myfile As String, n As Long
With Application.FileDialog(msoFileDialogFolderPicker)
.Title = "Please select a folder"
.Show
.AllowMultiSelect = False
If .SelectedItems.Count = 0 Then 'If no folder is selected, abort
MsgBox "You did not select a folder"
Exit Sub
End If
myfolder = .SelectedItems(1) & "\" 'Assign selected folder to MyFolder
End With
'Loop through all files in a folder until DIR cannot find anymore
Application.ScreenUpdating = False
myfile = Dir(myfolder & "*.xml")
Do While myfile <> ""
iRow = iRow + 1
ws.Cells(iRow, 1) = myfile
' open file and read all lines
Set ts = FSO.OpenTextFile(myfolder & myfile)
txt = ts.ReadAll
ts.Close
' count pattern matches
Dim m As Object
If regEx.test(txt) Then
Set m = regEx.Execute(txt)
ws.Cells(iRow, 2) = m(0).SubMatches(0) ' get date from first match
ws.Cells(iRow, 3) = m.Count
Else
ws.Cells(iRow, 2) = "No tags"
ws.Cells(iRow, 3) = 0
End If
myfile = Dir 'DIR gets the next file in the folder
Loop
' results
ws.UsedRange.Columns.AutoFit
Application.ScreenUpdating = True
End Sub
Use Subfolders property of the parent folder object.
Option Explicit
Sub process_folder()
Dim iRow As Long, wb As Workbook, ws As Worksheet, ar
Set wb = ThisWorkbook
Set ws = wb.Sheets(1)
ws.UsedRange.Clear
ws.Range("A1:B1") = Array("Source", "<Headline> Tag Count")
iRow = 1
' create FSO and regular expression pattern
Dim fso As Object, ts As Object, regEx As Object, txt As String
Set fso = CreateObject("Scripting.FileSystemObject")
Set regEx = CreateObject("VBScript.RegExp")
With regEx
.Global = True
.MultiLine = True
.IgnoreCase = True
.Pattern = "<Headline>(.*)</Headline>"
End With
'Opens the folder picker dialog to allow user selection
Dim myfolder, myfile As String, n As Long
Dim parentfolder As String, oParent
With Application.FileDialog(msoFileDialogFolderPicker)
.Title = "Please select a folder"
.Show
.AllowMultiSelect = False
If .SelectedItems.Count = 0 Then 'If no folder is selected, abort
MsgBox "You did not select a folder"
Exit Sub
End If
parentfolder = .SelectedItems(1) 'Assign selected folder to ParentFolder
End With
Set oParent = fso.getFolder(parentfolder)
' build collection or files
Dim colFiles As Collection
Set colFiles = New Collection
Call GetFiles(oParent, "xml", colFiles)
'Loop through all files in collection
Application.ScreenUpdating = False
For n = 1 To colFiles.Count
myfile = colFiles(n)
iRow = iRow + 1
ws.Cells(iRow, 1) = myfile
' open file and read all lines
Set ts = fso.OpenTextFile(myfile)
txt = ts.ReadAll
ts.Close
' count pattern matches
Dim m As Object
If regEx.test(txt) Then
Set m = regEx.Execute(txt)
ws.Cells(iRow, 2) = m(0).SubMatches(0) ' get date from first match
ws.Cells(iRow, 3) = m.Count
Else
ws.Cells(iRow, 2) = "No tags"
ws.Cells(iRow, 3) = 0
End If
' results
ws.UsedRange.Columns.AutoFit
Next
Application.ScreenUpdating = True
MsgBox colFiles.Count & " Files process", vbInformation
End Sub
Sub GetFiles(oFolder, ext, ByRef colFiles)
Dim f As Object
For Each f In oFolder.Files
If f.Name Like "*." & ext Then
colFiles.Add oFolder.Path & "\" & f.Name
End If
Next
' call recursively
For Each f In oFolder.subfolders
Call GetFiles(f, ext, colFiles)
Next
End Sub
Loop Through All Folders and Subfolders
In this post under the title Subfolder Paths to Collection, you can find the CollSubfolderPaths function, which will return the paths of all folders and their subfolders in a collection.
In your code, you could utilize it in the following way.
Sub process_folder()
' Preceding code...
Application.ScreenUpdating = False
' Return the paths of all folders and subfolders in a collection.
Dim MyFolders As Collection: Set MyFolders = CollSubfolderPaths(myfolder)
Dim Item As Variant
' Loop through the items in the collection.
For Each Item In MyFolders
' Get the first file.
myfile = Dir(Item & "\" & "*.xml")
'Loop through all files in a folder until DIR cannot find anymore
Do While myfile <> ""
' The same code...
Loop
Next Item
' results
ws.UsedRange.Columns.AutoFit
Application.ScreenUpdating = True
End Sub
I am trying to Loop through all files and sub folders but my code is just works for single folder.
I want to apply this code on all Folders and subfolder which have workbooks.
Any help will be appreciated.
Sub KeepColor()
Dim strFolder As String
Dim strFile As String
Dim wbk As Workbook
Dim wsh As Worksheet
Dim I As Long
Dim xRg As Range
With Application.FileDialog(4)
If .Show Then
strFolder = .SelectedItems(1)
Else
MsgBox "You 't selected a folder!", vbExclamation
Exit Sub
End If
End With
If Right(strFolder, 1) <> "\" Then
strFolder = strFolder & "\"
End If
Application.ScreenUpdating = FALSE
strFile = Dir(strFolder & "*.xls*")
Do While strFile <> ""
Set wbk = Workbooks.Open(strFolder & strFile)
For Each wsh In wbk.Worksheets
For Each xRg In wsh.UsedRange
If xRg.DisplayFormat.Interior.ColorIndex = xlColorIndexNone Then
xRg.Interior.ColorIndex = xlColorIndexNone
Else
xRg.Interior.Color = xRg.DisplayFormat.Interior.Color
End If
Next xRg
wsh.UsedRange.FormatConditions.Delete
Next wsh
wbk.Close SaveChanges:=True
strFile = Dir
Loop
Application.ScreenUpdating = TRUE
End Sub
Dir is much faster than FileSystemObject if you have a filename pattern, so here's a function which mixes both:
Sub Tester()
Dim col As Collection, t
t = Timer
Set col = GetMatches("C:\Tester", "*.xls*")
Debug.Print Timer - t, col.Count
End Sub
'Return a collection of file objects given a starting folder and a file pattern
' e.g. "*.txt"
'Pass False for last parameter if don't want to check subfolders
Function GetMatches(startFolder As String, filePattern As String, _
Optional subFolders As Boolean = True) As Collection
Dim fso, fldr, f, subFldr, fpath
Dim colFiles As New Collection
Dim colSub As New Collection
Set fso = CreateObject("scripting.filesystemobject")
colSub.Add startFolder
Do While colSub.Count > 0
Set fldr = fso.getfolder(colSub(1))
colSub.Remove 1
If subFolders Then
For Each subFldr In fldr.subFolders
colSub.Add subFldr.Path
Next subFldr
End If
'this is faster...
fpath = fldr.Path
If Right(fpath, 1) <> "\" Then fpath = fpath & "\"
f = Dir(fpath & filePattern)
Do While Len(f) > 0
colFiles.Add fso.getfile(fpath & f)
f = Dir()
Loop
'this is slower...
'For Each f In fldr.Files
' If UCase(f.Name) Like filePattern Then colFiles.Add f
'Next f
Loop
Set GetMatches = colFiles
End Function
Please, try the next code:
Sub KeepColor()
Dim strFolder As String, fso As Object, parentFolder As Object, folder As Object
With Application.FileDialog(4)
If .Show Then
strFolder = .SelectedItems(1)
Else
MsgBox "You didn't select a folder!", vbExclamation
Exit Sub
End If
End With
Set fso = CreateObject("scripting.filesystemobject")
Set parentFolder = fso.GetFolder(strFolder)
Application.ScreenUpdating = False
ProcessAllFiles parentFolder, "xls*"
For Each folder In parentFolder.SubFolders
ProcessAllFiles folder, "xls*"
Next
Application.ScreenUpdating = True
End Sub
Sub ProcessAllFiles(strFold As Object, fileExt As String)
Dim fso As Object, objFile As Object, xRg As Range, wbk As Workbook, wsh As Worksheet
Set fso = CreateObject("scripting.filesystemobject")
For Each objFile In strFold.files
If fso.GetExtensionName(objFile.Name) Like fileExt Then
Set wbk = Workbooks.Open(objFile.path)
For Each wsh In wbk.Worksheets
For Each xRg In wsh.UsedRange
If xRg.DisplayFormat.Interior.ColorIndex = xlColorIndexNone Then
xRg.Interior.ColorIndex = xlColorIndexNone
Else
xRg.Interior.color = xRg.DisplayFormat.Interior.color
End If
Next xRg
wsh.UsedRange.FormatConditions.Delete
Next wsh
wbk.Close SaveChanges:=True
End If
Next
End Sub
This is a recursion job.
I am using a generic function, that returns a collection of all files (could be changed to array as well) - either for the folder or for all subfolders.
You need to add a reference to "Microsoft Scripting runtime"
Option Explicit
Sub testFindAllFiles()
Dim strFolder As String: strFolder = "XXXX" 'adjust to your needs
Dim colFiles As Collection
Set colFiles = findAllFilesByExtension(strFolder, "xls*", True)
Dim strFile As Variant
For Each strFile In colFiles
Debug.Print strFile
'do what you need with the file
Next
End Sub
Public Function findAllFilesByExtension(ByVal targetFolder As String, ByVal extension As String, _
Optional fWithSubfolders As Boolean = True) As Collection
Dim fso As FileSystemObject: Set fso = New FileSystemObject
Dim colFiles As Collection: Set colFiles = New Collection
findFilesByExtension targetFolder, colFiles, extension, fso, fWithSubfolders
Set findAllFilesByExtension = colFiles
End Function
Private Sub findFilesByExtension(ByVal targetFolder As String, ByRef colFiles As Collection, _
extension As String, fso As FileSystemObject, fWithSubfolders As Boolean)
Dim objFolder As Folder, objFile As File
Dim subFolders As Folders
Set objFolder = fso.GetFolder(targetFolder)
For Each objFile In objFolder.Files
If Not objFile.Name Like "~*" Then
If objFile.Name Like "*." & extension Then
colFiles.Add objFile.Path
End If
End If
Next
If fWithSubfolders = True Then
Set subFolders = objFolder.subFolders
For Each objFolder In subFolders
findFilesByExtension objFolder.Path, colFiles, extension, fso, fWithSubfolders
Next
End If
End Sub
This question already has answers here:
Get list of sub-directories in VBA
(5 answers)
Closed 2 years ago.
I have a folder where I have many sub-folders and inside of them more than 1000 Excel files.
I want to run a specific macro (that changes a workbook) on all these files.
Already saw the following answer.
Sub ProcessFiles()
Dim Filename, Pathname As String
Dim wb As Workbook
Pathname = ActiveWorkbook.Path & "\C:\...\EXCL\"
Filename = Dir(Pathname & "*.xlsx")
Do While Filename <> ""
Set wb = Workbooks.Open(Pathname & Filename)
DoWork wb
wb.Close SaveChanges:=True
Filename = Dir()
Loop
End Sub
Sub DoWork(wb As Workbook)
With wb
'Do your work here
......
End With
End Sub
There are two problems:
1. this will be extremely slow. Is there a faster way?
2. this will only run on the files in the matching folder and not the files in all sub-folders. Is there way to do that for files in sub-folders as well?
As far as I know, VBA can't edit closet workbook. If you want to do work for every workbook in every subfolder, subfolder of subfolder etc. you can use the following code. I added condition, that it have to be .xlsx file, you can change it on .xls, .xlsb or whatever you want.
Sub ProcessFiles()
Dim objFolder As Object
Dim objFile As Object
Dim objFSO As Object
Dim MyPath As String
Dim myExtension As String
Dim FldrPicker As FileDialog
Set FldrPicker = Application.FileDialog(msoFileDialogFolderPicker)
With FldrPicker
.Title = "Select A Target Folder"
.AllowMultiSelect = False
If .Show <> -1 Then GoTo EmptyEnd
MyPath = .SelectedItems(1)
End With
Application.ScreenUpdating = False
Set objFSO = CreateObject("Scripting.FileSystemObject")
Call GetAllFiles(MyPath, objFSO)
Call GetAllFolders(MyPath, objFSO)
Application.ScreenUpdating = True
MsgBox "Complete."
EmptyEnd:
End Sub
Sub GetAllFiles(ByVal strPath As String, ByRef objFSO As Object)
Dim objFolder As Object
Dim objFile As Object
Set objFolder = objFSO.GetFolder(strPath)
For Each objFile In objFolder.Files
DoWork objFile.Path
Next objFile
End Sub
Sub GetAllFolders(ByVal strFolder As String, ByRef objFSO As Object)
Dim objFolder As Object
Dim objSubFolder As Object
Set objFolder = objFSO.GetFolder(strFolder)
For Each objSubFolder In objFolder.subfolders
Call GetAllFiles(objSubFolder.Path, objFSO)
Call GetAllFolders(objSubFolder.Path, objFSO)
Next objSubFolder
End Sub
Sub DoWork(strFile As String)
Dim wb As Workbook
If Right(strFile, 4) = "xlsx" Then
Set wb = Workbooks.Open(Filename:=strFile)
With wb
'Do your work here
......
.Close True
End With
End If
End Sub
If I get this right you need a function which collects all xl files in a directory and subdirs. This function will do that:
Public Function RecursiveDir(colFiles As Collection, _
strFolder As String, _
strFileSpec As String, _
bIncludeSubfolders As Boolean)
Dim strTemp As String
Dim colFolders As New Collection
Dim vFolderName As Variant
'Add files in strFolder matching strFileSpec to colFiles
strFolder = TrailingSlash(strFolder)
strTemp = Dir(strFolder & strFileSpec)
Do While strTemp <> vbNullString
colFiles.Add strFolder & strTemp
strTemp = Dir
Loop
If bIncludeSubfolders Then
'Fill colFolders with list of subdirectories of strFolder
strTemp = Dir(strFolder, vbDirectory)
Do While strTemp <> vbNullString
If (strTemp <> ".") And (strTemp <> "..") Then
If (GetAttr(strFolder & strTemp) And vbDirectory) <> 0 Then
colFolders.Add strTemp
End If
End If
strTemp = Dir
Loop
'Call RecursiveDir for each subfolder in colFolders
For Each vFolderName In colFolders
Call RecursiveDir(colFiles, strFolder & vFolderName, strFileSpec, True)
Next vFolderName
End If
End Function
Public Function TrailingSlash(strFolder As String) As String
If Len(strFolder) > 0 Then
If Right(strFolder, 1) = "\" Then
TrailingSlash = strFolder
Else
TrailingSlash = strFolder & "\"
End If
End If
End Function
And this shows how to use it
Sub TesterFiles()
Dim colFiles As New Collection
RecursiveDir colFiles, "Your Dir goes here...", "*.XLS*", True
Dim vFile As Variant
For Each vFile In colFiles
' Do sth with the file
Debug.Print vFile
Next vFile
End Sub
Nice one Storax! I would use the script that Storax posted, and modify it just a tad.
i = 1
Dim vFile As Variant
For Each vFile In colFiles
' Do sth with the file
Range("A" & i).Value = vFile
i = i + 1
Next vFile
I think it's just easier to work with a list. Anyway, once you have the file structure, you can run through those elements in the array you just created. Use the script below to do that.
Sub LoopThroughRange()
Dim rng As Range, cell As Range
Set rng = Range("A1:A13")
For Each cell In rng
'For Fnum = LBound(MyFiles) To UBound(MyFiles)
Set mybook = Nothing
On Error Resume Next
Set mybook = Workbooks.Open(cell)
On Error GoTo 0
If Not mybook Is Nothing Then
'Change cell value(s) in one worksheet in mybook
On Error Resume Next
With mybook.Worksheets(1)
If .ProtectContents = False Then
.Range("A1").Value = "My New Header"
Else
ErrorYes = True
End If
End With
If Err.Number > 0 Then
ErrorYes = True
Err.Clear
'Close mybook without saving
mybook.Close savechanges:=False
Else
'Save and close mybook
mybook.Close savechanges:=True
End If
On Error GoTo 0
Else
'Not possible to open the workbook
ErrorYes = True
End If
'Next Fnum
Next cell
End Sub
The idea comes straight from here.
http://www.rondebruin.nl/win/s3/win010.htm
Pay attention to this part:
'Change cell value(s) in one worksheet in mybook
That's where you want to put specific your code to do exactly what you want to do.
I just modified my OP. It's a lot easier, and a little different, than I initially made it out to be. I've adjusted the script accordingly.
I am trying to list all my folders from a drive in a directory onto an excel spreadsheet with a touch of a button. I made the button and assigned this macro... why won't it compile? The *** **** shows what they debugged. Said object folder was not an object. Please help!
Sub ListAllFile()
Dim objFSO As Object
Dim objFolder As Object
Dim objFile As Object
Dim ws As Worksheet
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set ws = Worksheets.Add
'Get the folder object associated with the directory
***Set objFolder = fso.GetFolder("C:hello\EMILY")***
ws.Cells(1, 1).Value = objFolder.Name
'Loop through the Files collection
For Each objFile In objFolder.Files
ws.Cells(ws.UsedRange.Rows.Count + 1, 1).Value = objFile.Name
Next
End Sub
This will allow you to get the folder names, unless you actually want files. It was modified from your original code. I commented out the excel/worksheet logic.
Part of the problem was fso.GetFolder was not an object which was declared and set. If you want still want files, you can change objFolder.Subfolders to .Files
Sub ListAllFile()
Dim objFSO As Object
Dim objFolder As Object
Dim objFile As Object
Dim ws As Worksheet
Set objFSO = CreateObject("Scripting.FileSystemObject")
'Set ws = Worksheets.Add
'Get the folder object associated with the directory
Set objFolder = objFSO.GetFolder("C:\users")
'ws.Cells(1, 1).Value = objFolder.Name
'Loop through the Files collection
For Each objFile In objFolder.subfolders
MsgBox objFile.Name ' to test output
'ws.Cells(ws.UsedRange.Rows.Count + 1, 1).Value = objFile.Name
Next
End Sub
There are many ways to do this. Here is one way.
Option Explicit
Sub FileListingAllFolder()
Dim pPath As String
Dim FlNm As Variant
Dim ListFNm As New Collection ' create a collection of filenames
Dim OWb As Workbook
Dim ShtCnt As Integer
Dim Sht As Integer
Dim MWb As Workbook
Dim MWs As Worksheet
Dim i As Integer
' Open folder selection
With Application.FileDialog(msoFileDialogFolderPicker)
.Title = "Select a Folder"
.AllowMultiSelect = False
If .Show <> -1 Then GoTo NextCode
pPath = .SelectedItems(1)
End With
Application.WindowState = xlMinimized
Application.ScreenUpdating = False
' Create master workbook with single sheets
Set MWb = Workbooks.Add(1)
MWb.Sheets(1).Name = "Result"
Set MWs = MWb.Sheets("Result")
Cells(1, 1) = "No."
Cells(1, 2) = "Sheet Name"
Cells(1, 3) = "File Name"
Cells(1, 4) = "Link"
i = 2
' Filling a collection of filenames (search Excel files including subdirectories)
Call FlSrch(ListFNm, pPath, "*.xls", True)
' Print list to immediate debug window and as a message window
For Each FlNm In ListFNm ' cycle for list(collection) processing
'Start Processing here
Set OWb = Workbooks.Open(FlNm)
ShtCnt = ActiveWorkbook.Sheets.Count
For Sht = 1 To ShtCnt
MWs.Cells(i, 1) = i - 1
MWs.Cells(i, 2) = Sheets(Sht).Name
MWs.Cells(i, 3) = OWb.Name
MWs.Cells(i, 4).Formula = "=HYPERLINK(""" & FlNm & """,""Click Here"")"
i = i + 1
Next Sht
'End file processing file
OWb.Close False
Next FlNm
' Print to immediate debug window and message if no file was found
If ListFNm.Count = 0 Then
Debug.Print "No file was found !"
MsgBox "No file was found !"
MWb.Close False
End
End If
MWb.Activate
MWs.Activate
Cells.Select
Selection.EntireColumn.AutoFit
Range("A1").Select
Application.ScreenUpdating = True
Application.WindowState = xlMaximized
End
NextCode:
MsgBox "You Click Cancel, and no folder selected!"
End Sub
Private Sub FlSrch(pFnd As Collection, pPath As String, pMask As String, pSbDir As Boolean)
Dim flDir As String
Dim CldItm As Variant
Dim sCldItm As New Collection
' Add backslash at the end of path if not present
pPath = Trim(pPath)
If Right(pPath, 1) <> "\" Then pPath = pPath & "\"
' Searching files accordant with mask
flDir = Dir(pPath & pMask)
Do While flDir <> ""
pFnd.Add pPath & flDir 'add file name to list(collection)
flDir = Dir ' next file
Loop
' Procedure exiting if searching in subdirectories isn't enabled
If Not pSbDir Then Exit Sub
' Searching for subdirectories in path
flDir = Dir(pPath & "*", vbDirectory)
Do While flDir <> ""
' Add subdirectory to local list(collection) of subdirectories in path
If flDir <> "." And flDir <> ".." Then If ((GetAttr(pPath & flDir) And _
vbDirectory) = 16) Then sCldItm.Add pPath & flDir
flDir = Dir 'next file
Loop
' Subdirectories list(collection) processing
For Each CldItm In sCldItm
Call FlSrch(pFnd, CStr(CldItm), pMask, pSbDir) ' Recursive procedure call
Next
End Sub
Also, check out the link below.
http://www.learnexcelmacro.com/wp/download/
Save the file from the link named 'File Manager (Excel Workbook)'. That is a very cool app!!
This question already has answers here:
Get list of sub-directories in VBA
(5 answers)
Closed 6 years ago.
My cycling script through individual files works fine, but I now need it to also look through/for multiple directories. I am stuck....
The order things need to happen:
User is prompted to choose root directory of what they need
I need the script to look for any folders in that root directory
If the script finds one, it opens the first one (all folders, so no specific search filter for the folders)
Once open, my script will loop through all files in the folders and do what it needs to do
after it's finished it closes the file, closes the directory and moves to the next one, etc..
Loops until all folders have been opened/scanned
This is what I have, which doesn't work and I know is wrong:
MsgBox "Please choose the folder."
Application.DisplayAlerts = False
With Application.FileDialog(msoFileDialogFolderPicker)
.InitialFileName = "\\blah\test\"
.AllowMultiSelect = False
If .Show <> -1 Then MsgBox "No folder selected! Exiting script.": Exit Sub
CSRootDir = .SelectedItems(1)
End With
folderPath = Dir(CSRootDir, "\*")
Do While Len(folderPath) > 0
Debug.Print folderPath
fileName = Dir(folderPath & "*.xls")
If folderPath <> "False" Then
Do While fileName <> ""
Application.ScreenUpdating = False
Set wbkCS = Workbooks.Open(folderPath & fileName)
--file loop scripts here
Loop 'back to the Do
Loop 'back to the Do
Final Code. It cycles through all sub-directories and files in each sub-directory.
Dim FSO As Object, fld As Object, Fil As Object
Dim fsoFile As Object
Dim fsoFol As Object
Dim fileName As String
MsgBox "Please choose the folder."
Application.DisplayAlerts = False
With Application.FileDialog(msoFileDialogFolderPicker)
.InitialFileName = "\\blah\test\"
.AllowMultiSelect = False
If .Show <> -1 Then MsgBox "No folder selected! Exiting script.": Exit Sub
folderPath = .SelectedItems(1)
End With
If Right(folderPath, 1) <> "\" Then folderPath = folderPath + "\"
Set FSO = CreateObject("Scripting.FileSystemObject")
Set fld = FSO.getfolder(folderPath)
If FSO.folderExists(fld) Then
For Each fsoFol In FSO.getfolder(folderPath).subfolders
For Each fsoFile In fsoFol.Files
If Mid(fsoFile.Name, InStrRev(fsoFile.Name, ".") + 1) = "xls" Then
fileName = fsoFile.Name
Application.ScreenUpdating = False
Set wbkCS = Workbooks.Open(fsoFile.Path)
'My file handling code
End If
Next
Next
End If
You might find it easier to use the FileSystemObject, somthing like this
This dumps a folder/file list to the Immediate window
Option Explicit
Sub Demo()
Dim fso As Object 'FileSystemObject
Dim fldStart As Object 'Folder
Dim fld As Object 'Folder
Dim fl As Object 'File
Dim Mask As String
Set fso = CreateObject("scripting.FileSystemObject") ' late binding
'Set fso = New FileSystemObject 'or use early binding (also replace Object types)
Set fldStart = fso.GetFolder("C:\Your\Start\Folder") '-- use your FileDialog code here
Mask = "*.xls"
Debug.Print fldStart.Path & "\"
ListFiles fldStart, Mask
For Each fld In fldStart.SubFolders
ListFiles fld, Mask
ListFolders fld, Mask
Next
End Sub
Sub ListFolders(fldStart As Object, Mask As String)
Dim fld As Object 'Folder
For Each fld In fldStart.SubFolders
Debug.Print fld.Path & "\"
ListFiles fld, Mask
ListFolders fld, Mask
Next
End Sub
Sub ListFiles(fld As Object, Mask As String)
Dim fl As Object 'File
For Each fl In fld.Files
If fl.Name Like Mask Then
Debug.Print fld.Path & "\" & fl.Name
End If
Next
End Sub
Here is a VBA solution, without using external objects.
Because of the limitations of the Dir() function you need to get the whole content of each folder at once, not while crawling with a recursive algorithm.
Function GetFilesIn(Folder As String) As Collection
Dim F As String
Set GetFilesIn = New Collection
F = Dir(Folder & "\*")
Do While F <> ""
GetFilesIn.Add F
F = Dir
Loop
End Function
Function GetFoldersIn(Folder As String) As Collection
Dim F As String
Set GetFoldersIn = New Collection
F = Dir(Folder & "\*", vbDirectory)
Do While F <> ""
If GetAttr(Folder & "\" & F) And vbDirectory Then GetFoldersIn.Add F
F = Dir
Loop
End Function
Sub Test()
Dim C As Collection, F
Debug.Print
Debug.Print "Files in C:\"
Set C = GetFilesIn("C:\")
For Each F In C
Debug.Print F
Next F
Debug.Print
Debug.Print "Folders in C:\"
Set C = GetFoldersIn("C:\")
For Each F In C
Debug.Print F
Next F
End Sub
Sub MoFileTrongCacFolder()
Dim FSO As Object, fld As Object, Fil As Object
Dim fsoFile As Object
Dim fsoFol As Object
Dim fileName As String
Dim folderPath As String
Dim wbkCS As Object
MsgBox "Please choose the folder."
Application.DisplayAlerts = False
With Application.FileDialog(msoFileDialogFolderPicker)
.InitialFileName = "\\blah\test\"
.AllowMultiSelect = False
If .Show <> -1 Then MsgBox "No folder selected! Exiting script.": Exit Sub
folderPath = .SelectedItems(1)
End With
If Right(folderPath, 1) <> "\" Then folderPath = folderPath + "\"
Set FSO = CreateObject("Scripting.FileSystemObject")
Set fld = FSO.getfolder(folderPath)
If FSO.folderExists(fld) Then
For Each fsoFol In FSO.getfolder(folderPath).subfolders
For Each fsoFile In fsoFol.Files
If Mid(fsoFile.Name, InStrRev(fsoFile.Name, ".") + 1) = "xls" Then
fileName = fsoFile.Name
Application.ScreenUpdating = False
Set wbkCS = Workbooks.Open(fsoFile.Path)
'My file handling code
End If
Next
Next
End If
End Sub