Hey, Scripting Guy! How can I list all of the files in a folder, as well as all the files in any subfolders of that folder?
— MA
Hey, MA. This is a question we get asked quite a bit, and one which have avoided answering up till now. That’s because there is no nice, simple answer to this one: a script that can carry out this task is bound to be a little bit confusing, and isn’t going to lend itself to the easy-to-explain approach we like to take with this column. On the other hand, the customer is always right: if you guys want a script that can list all the files in a folder, as well as all the files in any subfolders of that folder, well, who are we to argue?
Before we begin there are two issues we have to grapple with here. First, we need to pick a scripting technology. WMI, the FileSystemObject, and the Shell object are all capable of listing the files in a folder, as well as listing the subfolders in a folder. However, none of these technologies have a mechanism to automatically list the files in those subfolders (not to mention any sub-subfolders that might be found there). Any of these technologies will do the trick, but none will do it very easily.
We opted to go with WMI. The resulting script is perhaps a little more complicated than a similar script using the FileSystemObject or the Shell object, but a WMI script that can retrieve this kind of information on the local computer can just as easily retrieve this kind of information from a remote computer. That’s not the case with either the FileSystemObject or the Shell object. We voted for the flexibility of WMI.
Second, we noted that none of the scripting technologies has a built-in way to iterate through a folder, list the file names, and then automatically iterate through any and all subfolders and list the files found there. Because of that, we need to use a recursive function to perform this task. Explaining recursion is a bit beyond what we can do in this column; for a brief explanation, check out the Microsoft Windows 2000 Scripting Guide. Suffice to say that we’re creating a function that can call itself as many times as needed. In other words, if we have a function that accesses a folder and lists all its files, that function can call itself to access a subfolder and list all the files found there. And then call itself again to access a sub-subfolder. It’s hard to visualize, but it works.
There’s another complication, too, but we’ll deal with that in a minute. For now, let’s take a look at a script that lists a folder and any and all of its subfolders (although this first sample doesn’t list any files found in those folders):
strComputer = “.” Set objWMIService = GetObject(“winmgmts:\\” & strComputer & “\root\cimv2”)strFolderName = “c:\scripts”
Set colSubfolders = objWMIService.ExecQuery _ (“Associators of {Win32_Directory.Name='” & strFolderName & “‘} ” _ & “Where AssocClass = Win32_Subdirectory ” _ & “ResultRole = PartComponent”)
For Each objFolder in colSubfolders GetSubFolders strFolderName Next
Sub GetSubFolders(strFolderName) Set colSubfolders2 = objWMIService.ExecQuery _ (“Associators of {Win32_Directory.Name='” & strFolderName & “‘} ” _ & “Where AssocClass = Win32_Subdirectory ” _ & “ResultRole = PartComponent”)
For Each objFolder2 in colSubfolders2 strFolderName = objFolder2.Name Wscript.Echo objFolder2.Name GetSubFolders strFolderName Next End Sub
What we’re doing here is using an AssociatorsOf query to get a list of all the subfolders of the folder C:\Scripts. Basically our query says this: Get me a list of all the items associated with the directory C:\Scripts, provided those items are subdirectories (Where AssocClass = Win32_Subdirectory).
This gets us a list of all the top-level subfolders: for example, C:\Scripts\Folder1 and C:\Scripts\Folder2. What it doesn’t get us are any second-level folders; this query does not return a folder like C:\Scripts\Folder1\SubfolderA. To get at these sub-subfolders (subfolders of a subfolder) we need to use a recursive query. That’s what the subroutine GetSubFolders does. We pass it – one-by-one – the name of each subfolder we find (like C:\Scripts\Folder1 and C:\Scripts\Folder2) and let it query each of those subfolders for any sub-subfolders. If there are any sub-subfolders, the function will automatically call itself and look for any sub-sub-subfolders.
Confused? Don’t feel bad; you aren’t the only one. But don’t worry about it; just leave the code as-is and give it a try. To search a different folder (that is, one other than C:\Scripts) simply change the value of the variable containing the folder you want to search. For example, if you want to search C:\Windows, then use this line of code:
strFolderName = “c:\windows”
Now, what about the files found in all these folders? Well, this is where things really get complicated. That’s because we need to make an additional query: we use one query to get the names of all the subfolders, and then a second query to get the collection of files found in each folder. That second query happens to look a lot like this:
Set colFiles = objWMIService.ExecQuery _ (“Select * from CIM_DataFile where Path = ‘” & strPath & “‘”)
That’s not too bad, except for this: in a query such as this, we must “escape” (double-up) any \’s found in the file paths. We can’t use C:\Scripts\Folder1\ in our query; we have to use C:\\Scripts\\Folder1\\ instead. You’ll see code in the script that replaces each \ with \\; that’s just something we need to do when referencing a file path in a query like this. A good chunk of the script is doing nothing more than converting the folder path names so they can be used in the query.
So much for all the caveats. Here’s the script everyone has been dying to get their hands on:
strComputer = “.” Set objWMIService = GetObject(“winmgmts:\\” & strComputer & “\root\cimv2”)strFolderName = “c:\scripts”
Set colSubfolders = objWMIService.ExecQuery _ (“Associators of {Win32_Directory.Name='” & strFolderName & “‘} ” _ & “Where AssocClass = Win32_Subdirectory ” _ & “ResultRole = PartComponent”)
Wscript.Echo strFolderName
arrFolderPath = Split(strFolderName, “\”) strNewPath = “” For i = 1 to Ubound(arrFolderPath) strNewPath = strNewPath & “\\” & arrFolderPath(i) Next strPath = strNewPath & “\\”
Set colFiles = objWMIService.ExecQuery _ (“Select * from CIM_DataFile where Path = ‘” & strPath & “‘”)
For Each objFile in colFiles Wscript.Echo objFile.Name Next
For Each objFolder in colSubfolders GetSubFolders strFolderName Next
Sub GetSubFolders(strFolderName) Set colSubfolders2 = objWMIService.ExecQuery _ (“Associators of {Win32_Directory.Name='” & strFolderName & “‘} ” _ & “Where AssocClass = Win32_Subdirectory ” _ & “ResultRole = PartComponent”)
For Each objFolder2 in colSubfolders2 strFolderName = objFolder2.Name Wscript.Echo Wscript.Echo objFolder2.Name arrFolderPath = Split(strFolderName, “\”) strNewPath = “” For i = 1 to Ubound(arrFolderPath) strNewPath = strNewPath & “\\” & arrFolderPath(i) Next strPath = strNewPath & “\\”
Set colFiles = objWMIService.ExecQuery _ (“Select * from CIM_DataFile where Path = ‘” & strPath & “‘”)
For Each objFile in colFiles Wscript.Echo objFile.Name Next
GetSubFolders strFolderName Next End Sub
We told you it was complicated. But it works. This script will bind to the C:\Scripts folder and echo the names of all the files found there; the script will then get a list of all the subfolders found in C:\Scripts. From there we loop through the collection of subfolders, calling the recursive function GetSubFolders for each one. That function will list all the files found in the subfolder, then check to see if the subfolder has any sub-subfolders. If it does, the recursive function will be called again, and the process will continue to repeat itself until we reach the end of the line: until we’ve listed every file found in C:\Scripts and in all its subfolders.
Tomorrow we should go back to an easy one. Anybody out there want to know how to get the name of the local computer using a script?
Just an FYI – the above scripts suffer from apostrophe and quote modification. If you copy and paste this code into an editor your quotes and apostrophes may be “curly”. Replace any curly apostrophes and quotes in the script with straight apostrophes and quotes.