Jak utworzyć listę wszystkich plików w folderze i wszystkich podfolderów?
Skrypciarze odpowiadają na Wasze pytania
Witamy w rubryce TechNet, w której Skrypciarze z firmy Microsoft odpowiadają na częste pytania dotyczące używania skryptów w administracji systemu. Jeśli macie jakieś pytania z tej dziedziny, zachęcamy do wysłania e-maila na adres: scripter@microsoft.com. Nie możemy zagwarantować odpowiedzi na każde otrzymane pytanie, ale staramy się jak możemy. |
Jak utworzyć listę wszystkich plików w folderze i wszystkich podfolderów?
Cześć, Skrypciarze! Jak utworzyć listę wszystkich plików w folderze i wszystkich plików znajdujących się w jego podfolderach?
-- MA
Cześć, MA. To pytanie pojawia się dosyć często, ale jak dotąd unikaliśmy odpowiadania na nie. A to dlatego, że nie ma tu prostego i zgrabnego rozwiązania – skrypt mogący wykonać to zadanie będzie na pewno trochę zakręcony, co odbije się też na naszej odpowiedzi. Z drugiej jednak strony, klient ma zawsze rację – skoro zależy Wam na skrypcie mogącym utworzyć listę plików w folderze i jego podfolderach, to proszę bardzo.
Zanim zaczniemy, trzeba jeszcze poruszyć dwie kwestie. Po pierwsze, musimy wybrać technologię skryptów. Listę plików w folderze i listę podfolderów można utworzyć za pomocą WMI, FileSystemObject lub obiektu Shell. Jednak żadna z tych technologii nie pozwala na automatyczne tworzenie listy plików znajdujących się w podfolderach, czy też podfolderach podfolderów. Możemy wybrać dowolną z nich, bo z każdą będzie tak samo trudno.
Zdecydowaliśmy się wybrać WMI. Powstały w ten sposób skrypt jest może trochę bardziej złożony od analogicznego, używającego technologii FileSystemObject lub obiektu Shell, ale może z równą łatwością (trudnością?) pobierać informacje z lokalnego, co ze zdalnego komputera. Inaczej jest w przypadku FileSystemObject i Shell. Zdecydowaliśmy, że elastyczność WMI jest ważniejsza.
Po drugie, zauważyliśmy, że żadna z technologii skryptów nie ma wbudowanej funkcji umożliwiającej przejście przez wszystkie podfoldery i utworzenie listy znajdujących się tam plików. Dlatego musimy użyć tzw. funkcji rekursywnej. Wyjaśnienie rekursji wybiega trochę poza zakres niniejszego artykułu; można je znaleźć w przewodniku Microsoft Windows 2000 Scripting Guide (j.ang.). Na razie wystarczy, jeśli powiemy, że chodzi o funkcję, która może uruchomić się tyle razy, ile będzie wymagane. Innymi słowy, nasza funkcja otworzy folder i utworzy listę znalezionych w nim plików, a następnie uruchomi się ponownie, otworzy podfolder tego folderu i utworzy listę plików, które się w nim znajdują. A następnie uruchomi się ponownie, otwierając pod-podfolder itd. Nie jest to może szczególnie zgrabne, ale działa.
Mamy jeszcze jedno utrudnienie, którym zajmiemy się za chwilę. Na razie rzućmy okiem na skrypt tworzący listę folderów i znajdujących się w nich podfolderów (w pierwszej wersji nie tworzymy jeszcze list plików):
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
Używamy tu kwerendy AssociatorsOf w celu uzyskania listy wszystkich podfolderów folderu C:\Scripts. Znaczenie naszej kwerendy jest właściwie takie: Pobierz listę wszystkich elementów skojarzonych z katalogiem C:\Scripts, będących podkatalogami (Where AssocClass = Win32_Subdirectory).
Uzyskujemy w ten sposób listę wszystkich podfolderów pierwszego rzędu, np. C:\Scripts\Folder1 i C:\Scripts\Folder2. Nie uzyskujemy natomiast podfolderów drugiego rzędu – nie otrzymamy takich folderów, jak C:\Scripts\Folder1\SubfolderA. Do tego potrzebna jest kwerenda rekursywna. Tym właśnie zajmuje się podprogram GetSubFolders. Przekazujemy mu – jedną za drugą – nazwy poszczególnych znalezionych podfolderów (np. C:\Scripts\Folder1 i C:\Scripts\Folder2), w których następnie przeprowadzona zostaje kwerenda wyszukująca pod-podfoldery. W razie ich znalezienia funkcja automatycznie uruchomi się ponownie i wyszuka pod-pod-podfoldery.
Zagubieni? Nie martwcie się, nie Wy jedni. Nie ma się co przejmować, wystarczy pozostawić kod bez zmian i go uruchomić. Aby wyszukać w folderze innym niż C:\Scripts, wystarczy zmienić wartość zmiennej zawierającej jego nazwę. Na przykład w celu przeszukania folderu C:\Windows należy użyć takiego wiersza kodu:
strFolderName = "c:\windows"
Pozostaje jeszcze kwestia plików znajdujących się w tych wszystkich folderach. No cóż, teraz sprawy naprawdę zaczynają się komplikować. Potrzebujemy bowiem dodatkowej kwerendy, która będzie pobierać kolekcje plików wyszukiwanych w poszczególnych folderach. Tak się składa, że wygląda ona tak:
Set colFiles = objWMIService.ExecQuery _
("Select * from CIM_DataFile where Path = '" & strPath & "'")
Nie tak źle, pomijając jedno – w takiej kwerendzie musimy oznaczyć, czyli zdublować, każdy znak \ w ścieżkach plików. Nie możemy użyć w kwerendzie ścieżki C:\Scripts\Folder1\; musimy ją zapisać jako C:\\Scripts\\Folder1\\. W skrypcie można znaleźć kod zastępujący znak \ ciągiem \\. Spora część skryptu służy tylko do takiego przekształcania ścieżek, by nadawały się do zastosowania w kwerendzie.
To tyle jeśli chodzi o kwestie problematyczne. Oto skrypt, na który wszyscy czekali:
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
Mówiliśmy, że będzie trudno. Ale działa. Skrypt połączy się z folderem C:\Scripts i wyświetli listę znajdujących się tam plików, a następnie pobierze listę znajdujących się w nim podfolderów. Następnie skrypt przechodzi przez kolekcję podfolderów, wywołując dla każdego z nich rekursywną funkcję GetSubFolders. Funkcja ta tworzy listę plików znajdujących się w danym podfolderze, a następnie sprawdza, czy są w nim pod-podfoldery. Jeśli tak, ponownie zostanie wywołana funkcja rekursywna, a proces będzie powtarzany, dopóki nie dojdziemy do końca i nie będziemy mieć listy wszystkich plików znajdujących się w folderze C:\Scripts i wszystkich jego podfolderów.
Jutro zajmiemy się czymś prostym. Na przykład: jak uzyskać nazwę komputera lokalnego za pomocą skryptu?
Do początku strony |