Centrum skryptów - Systemy operacyjne

Jak uzyskać listę wszystkich plików PDF znajdujących się w folderze i jego podfolderach?

Skrypciarze odpowiadają na Wasze pytania

Cześć Skrypciarze!

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 uzyskać listę wszystkich plików PDF znajdujących się w folderze i jego podfolderach?

Cześć Skrypciarze! Pytanie

Cześć, Skrypciarze! Jak uzyskać listę wszystkich plików PDF (tylko nazwy plików, nie potrzebuję ścieżki dostępu ani rozszerzenia pliku), znajdujących się w folderze oraz jego podfolderach, a następnie umieścić tę listę w pliku tekstowym?

-- PS

Cześć Skrypciarze! Odpowiedź

Cześć, PS. To naprawdę jest bardzo trudne pytanie, o wiele trudniejsze, niż mogłoby się wydawać. A na dodatek Skrypciarz piszący te słowa jest na wakacjach, więc nie ma zbyt wielkiej ochoty na zajmowanie się jakimś tam skryptem.

No dobra, tak naprawdę to nie jest on na wakacjach, tylko na konferencji TechEd 2007 (j.ang.). Ale to przecież tak, jakby był na wakacjach. Jest przecież w Orlando i ma pod nosem Disney World, wytwórnię filmowa Universal Studios oraz miasteczko wodne SeaWorld…

Sami rozumiecie, jak trudno przy takich możliwościach przyjemnego spędzania czasu zabrać się za pisanie skryptu. I to na dodatek takiego, który pozwala uzyskać listę wszystkich plików PDF znajdujących się w folderze i jego podfolderach. O nie, nie ma mowy.

No dobra, macie szczęście, że pada i z zabawy w Disney World i tak nic nie będzie. Tylko dlatego piszący te słowa Skrypciarz zdecydował się odpowiedzieć na pytanie PS. Bo przecież gdyby nie padało, wiecie co by teraz robił…

Nie przedłużajmy jednak, może zaraz przestanie padać, oto skrypt:

strComputer = "."



Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")



strFolderName = "C:\Test"



Set objFSO = CreateObject("Scripting.FileSystemObject")

Set objTextFile = objFSO.CreateTextFile("C:\Scripts\Test.txt")



Set colSubfolders = objWMIService.ExecQuery _

    ("Associators of {Win32_Directory.Name='" & strFolderName & "'} " _

        & "Where AssocClass = Win32_Subdirectory " _

            & "ResultRole = PartComponent")



Set colFiles = objWMIService.ExecQuery _

    ("ASSOCIATORS OF {Win32_Directory.Name='" & strFolderName & "'} Where " _

        & "ResultClass = CIM_DataFile")



For Each objFile in colFiles

    If objFile.Extension = "pdf" Then

        objTextFile.WriteLine objFile.FileName 

    End If

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



    Set colFiles = objWMIService.ExecQuery _

        ("ASSOCIATORS OF {Win32_Directory.Name='" & strFolderName & "'} Where " _

            & "ResultClass = CIM_DataFile")



    For Each objFile in colFiles

        If objFile.Extension = "pdf" Then

            objTextFile.WriteLine objFile.FileName 

        End If

    Next



        GetSubFolders strFolderName

    Next

End Sub

Ostrzegaliśmy: to skomplikowany skrypt, o wiele bardziej skomplikowany, niż powinien być. Wynika to z faktu, że ani usługa WMI, ani obiekt FileSystemObject mają prostego i bezpośredniego sposobu na pracę z folderami i podfolderami. (A tak przy okazji, jest to obszar, w którym powłoka Windows PowerShell wykazuje sporą poprawę w stosunku do poprzednich technologii.) Z tego powodu musimy zastosować funkcję rekurencyjną w celu dobrania się do plików znajdujących się w folderze, plików znajdujących się w podfolderach tego folderu oraz plików znajdujących się w podfolderach tych podfolderów. Teraz nie mamy czasu na szczegółowe wyjaśnienia dotyczące zjawiska rekursji. (Czy wspominaliśmy już, że jesteśmy na wakacjach?) Jednak w tym artykule Microsoft Windows 2000 Scripting Guide (j.ang.) znajdują się informacje na temat rekursji i funkcji rekurencyjych, więc zachęcamy zainteresowanych do odwiedzenia tej witryny.

A teraz przejdźmy do rzeczy i skończmy to , co zaczęliśmy, zanim przestanie padać i pozamykają nam parki. Na początek łączymy się z usługą WMI na lokalnym komputerze. (Chociaż, przypisując nazwę komputera do zmiennej strComputer, możemy także uruchomić ten skrypt dla zdalnego komputera.) Następnie przypisujemy nazwę folderu macierzystego do zmiennej strFolderName. A skoro interesuje nas folder C:\Test (oraz jego podfoldery) przypisujemy zmiennej strFolderName wartość C:\Test:

strFolderName = "C:\Test"

To ma sens, nieprawdaż? A ponieważ czas ku temu jest odpowiedni, tworzymy teraz wystąpienie obiektu Scripting.FileSystemObject i używamy metody CreateTextFile w celu utworzenia pliku tekstowego o nazwie C:\Scripts\Test.txt:

Set objFSO = CreateObject("Scripting.FileSystemObject")

Set objTextFile = objFSO.CreateTextFile("C:\Scripts\Test.txt")

To nas sprowadza do poniższego wiersza kodu:

Set colSubfolders = objWMIService.ExecQuery _

    ("Associators of {Win32_Directory.Name='" & strFolderName & "'} " _

        & "Where AssocClass = Win32_Subdirectory " _

            & "ResultRole = PartComponent")

Stosujemy teraz kwerendę Associators Of w celu uzyskania listy podfolderów znajdujących się w folderze C:\Test. Może na to nie wygląda, ale tak naprawdę nasza kwerenda mówi coś takiego: daj mi listę wszystkich elementów powiązanych z katalogiem C:\Test z zastrzeżeniem, że te elementy są podkatalogami (Przy czym AssocClass = Win32_Subdirectory).

W ostatecznym rozrachunku ta kwerenda przekazuje nam listę wszystkich podfolderów najwyższego szczebla w C:\Test; na przykład, C:\Test\Folder1 oraz C:\Test\Folder2. Za to nie przekazuje nam listy podfolderów niższego szczebla; zatem na liście nie będzie na przykład folderu C:\Test\Folder1\SubfolderA. W celu uzyskania tych pod-podfolderów (czyli podfolderów podfolderu) musimy zastosować kwerendę cykliczną. Do tego posłuży nam podprocedura GetSubFolders. Programistycznie rzecz ujmując, przekazujemy do tej podprocedury nazwę każdego znalezionego podfolderu (na przykład C:\Test\Folder1 oraz C:Test\Folder2); z kolei podprocedura wysyła do każdego podfolderu kwerendę o pliki PDF i dalsze podfoldery. Jeżeli znajdują się tak jakieś pod-podfoldery, podprocedura wywoła się automatycznie i zacznie wyszukiwanie ewentualnych pod-pod-podfolderów.

Zdezorientowani? Prawdę mówiąc, my też. Ale nie ma się czym martwić; wystarczy zostawić kod w obecnej postaci i dać mu szansę. W celu przeszukania innego folderu (czyli innego niż C:\Test) wystarczy po prostu zmienić wartość zmiennej strFolderName na nazwę folderu, z którym chcemy pracować. Przykładowo, jeżeli chcemy przeszukać folder C:\Windows, stosujemy poniższy wiersz kodu:

strFolderName = "C:\Windows"

Zaraz zaraz, chwila moment, to jeszcze nie wszystko. Zanim zaczniemy wywoływanie podprocedury, musimy mieć listę wszelkich plików PDF znajdujących się w naszym macierzystym folderze C:\Test. W tym celu, stosujemy poniższy wiersz kodu pozwalający nam uzyskać kolekcję wszystkich plików (wystąpień klasy CIM_DataFile) znajdujących się w C:\Test (co, jak sobie zapewne przypominamy, jest wartością przypisaną do zmiennej strFolderName):

Set colFiles = objWMIService.ExecQuery _

    ("ASSOCIATORS OF {Win32_Directory.Name='" & strFolderName & "'} Where " _

        & "ResultClass = CIM_DataFile")

Teraz używamy następującego fragmentu kodu w celu sprawdzenia każdego pliku pod kątem rozszerzenia pliku PDF (zauważmy, że w WMI kropka nie jest częścią rozszerzenia pliku):

For Each objFile in colFiles

    If objFile.Extension = "pdf" Then

        objTextFile.WriteLine objFile.FileName 

    End If

Next

Jeżeli znajdziemy jakiś plik PDF, używamy metody WriteLine w celu zapisania właściwości FileName w naszym pliku tekstowym:

objTextFile.WriteLine objFile.FileName

Rzecz jasna, nie trzeba dodawać, że właściwość FileName to sama nazwa pliku bez ścieżki dostępu ani rozszerzenia. Przykładowo, jeżeli nasz pierwszy plik to C:\Test\MyFile.pdf, otrzymamy następujący wpis do pliku tekstowego:

MyFile

To załatwia sprawę wszystkich plików znajdujących się w folderze macierzystym C:\Test. A co z plikami znajdującymi się w podfolderach folderu C:\Test? To załatwi następujący fragment kodu:

For Each objFolder in colSubfolders

    GetSubFolders strFolderName

Next

Kręcimy się teraz w kolekcji podfolderów; dla każdego podfolderu znajdującego się w folderze C:\Test wywołujemy funkcję rekurencyjną GetSubFolders, przekazując ścieżkę folderu jako jedyny parametr:

GetSubFolders strFolderName

Trudno to sobie wyobrazić, ale GetSubfolders zrobi dokładnie to samo, co właśnie skończyliśmy. Załóżmy, że nasz podfolder to C:\Test\Folder1. Podprocedura rozpocznie działanie, szukając wszelkich plików PDF w C:\Test\Folder1; jeżeli jakieś znajdzie, zapisze nazwy tych plików w pliku tekstowym.

Potem funkcja GetSubFolders wyśle kwerendę i sprawdzi, czy podfolder C:\Test\Folder1 ma jakieś własne podfoldery. Przypuśćmy, że ma; co wtedy? No cóż, w takim przypadku podprocedura wywoła się sama, tym razem przekazując nazwę każdego podfolderu jako parametru podprocedury. Jak już mówiliśmy, samo myślenie o tym może przyprawić o ból głowy. Ale przypuśćmy, że mamy podfolder o nazwie C:\Test\Folder1\SubfolderA. Podprocedura wywoła się i sprawdzi, czy w tym pod-podfolderze są jakieś pliki PDF; a potem sprawdzi, czy SubfolderA posiada jakieś pod-podfoldery, a potem, jeżeli będzie taka potrzeba, podprocedura wywoła się jeszcze raz. Tak będzie się działo, dopóki nie dotrzemy do końca wiersza i nie skończą nam się foldery.

Zwariować można. Na szczęście skrypt VBScript odstawia za nas całą trudną robotę. Wystarczy teraz wywołać funkcję rekurencyjną (która, jak się chwilę zastanowić, jest tak naprawdę rekurencyjną podprocedurą). Skrypt VBScript zajmie się resztą, śledząc foldery które już przeszukaliśmy i te, do których jeszcze nie zajrzeliśmy. Co ciekawe, zanim skończy, sprawdzi każdy pojedynczy folder oraz podfolder w C:\Test (nawet, jeżeli ta struktura ma kilka poziomów) i wpisze nazwy wszystkich plików PDF do pliku tekstowego C:\Scripts\Test.txt. Jeżeli nam nie wierzycie (a wcale byśmy się nie zdziwili), sami spróbujcie i zobaczcie jak to działa.

 Do początku strony Do początku strony

Centrum skryptów - Systemy operacyjne