Centrum skryptów - Systemy operacyjne

Jak skopiować strukturę folderów określonego folderu?

Udostępnij na: Facebook

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 skopiować strukturę folderów określonego folderu?

Cześć Skrypciarze! Pytanie

Cześć, Skrypciarze! Jak skopiować strukturę folderów określonego folderu (czyli folderu i wszystkich jego podfolderów) bez jednoczesnego kopiowania plików?

-- AS

Cześć Skrypciarze! Odpowiedź

Cześć, AS. Dla czytelników, którzy ze zniecierpliwieniem czekają na wszelkie informacje dotyczące mojego pobytu we Włoszech, mam najnowsze wieści: dzisiaj zamierzam się wybrać na wycieczkę do Wenecji z całą rodziną. Jeżeli należycie do tej grupy naszych czytelników, którzy mogą (a nawet woleliby) obejść się bez informacji na temat mojego urlopu – no cóż, dla was też mam dobrą wiadomość: kończą mi się pieniądze, a to oznacza, że już niedługo przestanę was męczyć szczegółami dotyczącymi wycieczki do Europy. Wtedy zacznę od nowa raczyć was opowieściami na temat meczy rozgrywanych przez mojego syna.

Jeżeli natomiast należycie do tej grupy czytelników, którzy chcieliby wiedzieć, jak skopiować strukturę folderów danego folderu bez kopiowania znajdujących się w nim plików, to i dla was mam dobrą wiadomość: tak się składa, że mam w zanadrzu skrypt, który załatwi całą sprawę.

Cześć Skrypciarze! – rubryka, w której każdy znajdzie coś dla siebie.

Między innymi poniższy skrypt:

On Error Resume Next



Dim arrFolders()

intSize = 0



strComputer = "."



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



strFolderName = "c:\scripts"



GetSubFolders strFolderName



Sub GetSubFolders(strFolderName)

    Set colSubfolders = objWMIService.ExecQuery _

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

            & "Where AssocClass = Win32_Subdirectory " _

                & "ResultRole = PartComponent")



    For Each objFolder in colSubfolders

        strFolderName = objFolder.Name

        ReDim Preserve arrFolders(intSize)

        arrFolders(intSize) = strFolderName

        intSize = intSize + 1

        GetSubFolders strFolderName

    Next

End Sub



Set objFSO = CreateObject("Scripting.FileSystemObject")



For Each strFolder in arrFolders

    strFolderName = strFolder 

    strNewFolder = Replace(strFolderName, "c:\scripts", "c:\test")

    Set objFolder = objFSO.CreateFolder(strNewFolder)

Next

Tak, wiem – ten skrypt wygląda trochę strasznie. Wszystko dlatego, że nie ma prostej i bezpośredniej metody uzyskania zagnieżdżonych podfolderów, czyli folderów znajdujących się wewnątrz folderów, znajdujących się wewnątrz folderów. (Lub przynajmniej nie w skrypcie VBScript; w powłoce Windows PowerShell (j.ang.) mamy metodę umożliwiającą robienie takich rzeczy.) Dlatego tez musimy zastosować podprocedurę rekursywną: taką, która wywoła się tyle razy, ile tylko będzie trzeba. Podprocedury rekursywne wcale nie są intuicyjne i dlatego nasz kod wygląda, jak wygląda.

Nasza rada: Jeżeli chcecie wiedzieć więcej na temat rekursywności, zajrzyjcie do tego artykułu (j.ang.) przewodnika Microsoft Windows 2000 Scripting Guide. Lub też nie przejmujcie się tym za bardzo: po prostu zastosujcie skrypt w niezmienionej postaci, zmieniając tylko nazwy folderów według potrzeb.

Zaczynamy od pobrania nazwy (lub ściślej mówiąc – ścieżki) każdego podfolderu znajdującego się w folderze C:\Scripts i umieszczenia tej informacji w tablicy; następnie uruchamiamy pętlę, która przejdzie przez wartości znajdujące się w tablicy w celu skopiowania struktury folderu do folderu C:\Test. Mając to na uwadze, najpierw inicjujemy dynamiczną tablicę o nazwie arrFolders, a następnie przypisujemy wartość 0 do zmiennej licznika o nazwie intSize. Robimy to w ten oto sposób:

Dim arrFolders()

intSize = 0

Po podłączeniu się do usługi WMI na lokalnym komputerze przypisujemy ścieżkę macierzystego folderu (C:\Scripts) do zmiennej o nazwie strFolderName. Za chwilę zastosujemy fragment kodu, dzięki któremu uzyskamy kolekcję wszystkich najwyższych podfolderów naszego folderu macierzystego. Co mamy na myśli mówiąc „najwyższych” podfolderów? No cóż, załóżmy, że mamy strukturę folderów wyglądającą następująco:

  • C:\Scripts
  • C:\Scripts\Folder 1
  • C:\Scripts\Folder 1\Folder A
  • C:\Scripts\Folder 2
  • C:\Scripts\Folder 2\Folder B

Jeżeli folder C:\Scripts jest naszym folderem macierzystym (a jest), to najwyższymi folderami są Folder 1 oraz Folder 2; są to podfoldery znajdujące się na poziomie o jeden stopień „niższym” od folderu macierzystego. A co z folderami Folder A oraz Folder B, które są zagnieżdżone dwa poziomy niżej od folderu macierzystego? Cóż, z tym jest problem: dostęp do głęboko zagnieżdżonych podfolderów, takich jak te właśnie, jest utrudniony. Z tego powodu jest nam potrzebna podprocedura rekursywna.

Nadążacie? Dobra. Zabawa się dopiero zaczyna:

GetSubFolders strFolderName

Może „zabawa” to nie jest odpowiednie słowo; może lepiej tak: „zabierzmy się teraz za pobieranie wszystkich nazw folderów i podfolderów”. Teraz wywołujemy rekursywną podprocedurę (podprocedurę o nazwie GetSubFolders) i przekazujemy jej ścieżkę do folderu macierzystego jako jedyny parametr. Na wypadek, gdybyście już zapomnieli, podprocedura GetSubFolders wygląda następująco:

Sub GetSubFolders(strFolderName)

    Set colSubfolders = objWMIService.ExecQuery _

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

            & "Where AssocClass = Win32_Subdirectory " _

                & "ResultRole = PartComponent")



    For Each objFolder in colSubfolders

        strFolderName = objFolder.Name

        ReDim Preserve arrFolders(intSize)

        arrFolders(intSize) = strFolderName

        intSize = intSize + 1

        GetSubFolders strFolderName

    Next

End Sub

Tak, właśnie w tym momencie sprawy zaczynają się komplikować. Wewnątrz podprocedury najpierw wykonujemy kwerendę Associators Of, dzięki której otrzymamy kolekcję najwyższych podfolderów folderu C:\Scripts. Skąd wiemy, że ta kwerenda przekazuje nam podfoldery folderu C:\Scripts? Cóż, z jednej strony otrzymujemy wystąpienia klasy Win32_Subdirectory; tak się składa, że wystąpienia tej klasy są podfolderami. Z drugiej strony wiemy, że folder macierzysty to na pewno C:\Scripts, ponieważ jest to wartość zmiennej strFolderName.

Stąd właśnie wiemy, że ta kwerenda przekazuje nam podfoldery folderu C:\Scripts.

Jak już zauważyliśmy, kwerenda Associators Of przekazuje nam kolekcję najwyższych podfolderów znajdujących się w folderze C:\Scripts (na przykład, C:\Scripts\Folder 1 oraz C:\Scripts\Folder 2). Naszym następnym posunięciem jest uruchomienie pętli For Each, która przejdzie przez tę kolekcję podfolderów. Wewnątrz tej pętli stosujemy poniższy wiersz kodu w celu pobrania nazwy Name folderu (czyli ścieżki folderu) i przypisujemy tę wartość do zmiennej strFolderName:

strFolderName = objFolder.Name

Następnie stosujemy poniższy wiersz kodu w celu zmiany rozmiaru tablicy arrFolders, uważając, aby zachować wszystkie dane znajdujące się w tej tabeli. Przypisujemy ścieżkę do pierwszego podfolderu (np. C:\Scripts\Folder 1) do pierwszego elementu (element 0)w tablicy, a potem zwiększamy wartość naszej zmiennej licznika intSize o 1:

ReDim Preserve arrFolders(intSize)

arrFolders(intSize) = strFolderName

intSize = intSize + 1

W jakim celu zwiększamy wartość zmiennej licznika? To proste. Jak dotąd dodaliśmy jeden folder do tablicy sprawiając, że ten pojedynczy element tablicy to element 0. Kiedy przyjdzie czas na dodanie kolejnego folderu do tablicy, ten polder zostanie dodany jako element 1 w tablicy, co będzie oznaczać, że wartość naszej zmiennej licznika intSize także będzie równa 1.

To nas sprowadza do poniższego wiersza kodu:

GetSubFolders strFolderName

Tak, widzieliśmy ten wiersz kodu już wcześniej: jeszcze raz wywołujemy podprocedurę GetSubFolders, tym razem przekazując wartość C:\Scripts\Folder 1 jak parametr podprocedury. Owszem, robimy to nawet będąc już wewnątrz podprocedury; taka jest natura funkcji rekursywnej. W ten sposób podprocedura wydostanie najwyższe podfoldery z folderu C:\Scripts\Folder 1. Następnie podprocedura zajmie się tymi podfolderami, wyszukując kolejnych podfolderów. Ten rekursywny proces będzie trwał, dopóki nie skończą nam się foldery w drzewie folderów. Zanim jednak to nastąpi, ścieżki do wszystkich tych folderów zostaną dodane do tablicy arrFolders.

Tak, mnie też zaczyna bolec głowa od samego myślenia na ten temat. Na szczęście jednak nie musimy o tym myśleć zbyt dużo; całą ciężką robotę załatwi za nas skrypt VBScript (na przykład pilnowanie za nas bieżącej pozycji w strukturze folderów oraz kolejnego kroku).

Co się zatem dzieje, kiedy już na pewno skończymy dodawanie wszystkich ścieżek folderów do tablicy arrFolders? No cóż, na początek tworzymy wystąpienie obiektu Scripting.FileSystemObject, które jest stosowane w celu tworzenia nowych folderów. Następnie uruchamiamy pętlę For Each, która przeprowadzi nas przez wszystkie ścieżki folderów znajdujących się w tablicy arrFolders, podobne do następujących:

  • C:\Scripts\Folder 1
  • C:\Scripts\Folder 1\Folder A
  • C:\Scripts\Folder 2
  • C:\Scripts\Folder 2\Folder B

Nie ma sensu powtarzać, że to są ścieżki do istniejących folderów; teraz musimy skopiować wszystkie te foldery do folderu C:\Test. Owszem, jest kilka innych sposobów wykonania tego zadania; zdecydowaliśmy, że najprostszym sposobem będzie zastosowanie funkcji Replace w celu zastąpienia każdego wystąpienia folderu C:\Scripts przez C:\Test. W przypadku pierwszego elementu w tablicy otrzymamy następującą ścieżkę folderu:

C:\Test\Folder 1

I wiecie co? Tak się składa, że jest to jedna ze ścieżek potrzebnych do skopiowania struktury folderu C:\Scripts do C:\Test. Oznacza to, że wystarczy teraz wywołać metodę CreateFolder (przekazując tę nazwę folderu jako parametr metody). Tak po prostu, skopiowaliśmy część struktury folderu do C:\Test, ale bez kopiowania znajdujących się tam plików. Następnie po prostu kontynuujemy działanie pętli i powtarzamy proces dla następnego folderu w tablicy.

Kiedy już wszystko będzie załatwione, folder C:\Test będzie zawierał następujące podfoldery:

  • C:\Test\Folder 1
  • C:\Test\Folder 1\Folder A
  • C:\Test\Folder 2
  • C:\Test\Folder 2\Folder B

Są to ni mniej, ni więcej, tylko podfoldery, które folder C:\Test zawierać powinien.

Jak już mówiłem, wybieram się dzisiaj z całą rodziną do Wenecji. Przez większość XX wieku Wenecja pomału zapadała się w lagunę, było to spowodowane studniami artezyjskimi tworzonymi na obrzeżach miasta. Zakaz tworzenia studni artezyjskich wprowadzono w latach sześćdziesiątych, co spowodowało spowolnienie procesu. Jednakże eksperci nie są zgodni co do tego, że proces ten został zatrzymany.

To wszystko razem to jeden z powodów, dla których czuję się z tym miastem tak związany. Coś co powoli zapada się w nicość… Czyż to nie piękny opis mojej kariery w firmie Microsoft? No, może jednak nie.

 Do początku strony Do początku strony

Centrum skryptów - Systemy operacyjne