Centrum skryptów - Systemy operacyjne

Jak przenieść pliki na podstawie części nazwy pliku?

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 przenieść pliki na podstawie części nazwy pliku?

Cześć Skrypciarze! Pytanie

Cześć Skrypciarze! Mam dziesiątki tysięcy plików, mających nazwę pliku zawierającą liczbę połączoną dwoma znakami podkreślenia; na przykład: P_19_L00.jpg. Muszę się dowiedzieć, jaka liczba jest zawarta w nazwie pliku, a następnie przenieść ten plik do folderu o odpowiadającej jej nazwie. W tym przypadku oznacza to przeniesienie pliku P_19_L00.jpg do folderu D:\19. Jak to zrobić? Od dawna szukam rozwiązania tej zagadki, ale bezskutecznie. Możecie mi pomóc?

-- MGD

Cześć Skrypciarze! Pytanie

Cześć, MGD. Dzisiejszy dzień jest dla mnie dniem nietypowym. Dlaczego? Cóż, zazwyczaj kiedy ktoś coś knoci, to ja. (Czy ktoś pamięta jeszcze konkurencję 8 (j.ang.) z Zimowej Olimpiady Skrypciarskiej 2007?) Jak się okazuje, sknociliśmy coś w konkurencji 5 w części dla początkujących podczas tegorocznej Zimowej Olimpiady Skrypciarskiej (j.ang.). (Która będzie jeszcze trwać do 3 marca. Dużo czasu, żeby sknocić coś jeszcze.) Konkurencja 5 to było naprawdę trudne zadanie, w szczególności dla początkujących, i wiele osób dostało za nie 0 punktów; wiele osób taki wynik bardzo zaskoczył. Ale przejdźmy do tej dziwnej części: ta konkurencja nie podlega mojej jurysdykcji, odpowiedzialna za nią jest Jean Ross. Chociaż raz ktoś inny niż ja spowodował takie zamieszanie.

Uwaga. Może dlatego, że trudno jest spowodować zamieszanie, jeżeli się nic nie robi. Chociaż w ten temat to się może nie będę zagłębiał….

W każdym razie, mając na uwadze wszystkie wyrazy współczucia i wsparcie, jakie otrzymywałem za każdym razem, gdy coś sknociłem, chciałbym coś powiedzieć w związku z konkurencją 5 w części dla początkujących: jeżeli czujecie się w jakiś sposób pokrzywdzeni, natychmiast wyślijcie wiadomość e-mail do Jean Ross. A co tam, wyślijcie jej 100 wiadomości: niech wie, że nawaliła. Cokolwiek zrobicie, nie dopuśćcie do tego, żeby jej się upiekło.

Nie żebym był mściwy czy coś takiego. Po prostu wydaje mi się, że to ważne, żeby Jean dostała to, na co sobie zasłużyła.

Czyli, żebyście Wy także dostali to, na co zasłużyliście. Wasz wynik na Zimowej Olimpiadzie Skrypciarskiej 2008 to w końcu Wasza sprawa. Naprawdę.

Uwaga. Miło jest popatrzeć na skruszoną Jean Ross (która do tej pory była naprawdę we wszystkim doskonała).

Tak czy inaczej, dajcie Jean znać o tym, jak się teraz czujecie. Nie braliście udziału w konkurencji 5? Co z tego? W końcu nie musicie mieć w piwnicy składu odpadów toksycznych, żeby martwić się zanieczyszczeniem środowiska, prawda?

Dzisiejszy dzień jest dla mnie dniem bardzo nietypowym. Z tego względu najlepsze, co mogę teraz zrobić, to postępować jak zwykle, wrócić do normalnych zajęć i udawać, że nic się nie zmieniło. Na czym polega ten powrót do normalnych zajęć? Cóż, na szczęście dla MGD polega on na napisaniu skryptu, który wyodrębni część nazwy pliku i przeniesie ten plik w oparciu o tę część nazwy:

Set objRegEx = CreateObject("VBScript.RegExp")



objRegEx.Global = True   

objRegEx.Pattern = "_\d{1,}_"



Set objFSO = CreateObject("Scripting.FileSystemObject")

Set objFolder = objFSO.GetFolder("C:\Images")



Set colFiles = objFolder.Files



For Each objFile in colFiles

    strSearchString = objFile.Name

    Set colMatches = objRegEx.Execute(strSearchString)  



    For Each strMatch in colMatches

        strFolderName = strMatch.Value

        strFolderName = Replace(strFolderName, "_", "")

        strFolderName = "D:\" & strFolderName & "\"

        If Not objFSO.FolderExists(strFolderName) Then

            Set objNewFolder = objFSO.CreateFolder(strFolderName)

        End If

    Next



    objFSO.MoveFile objFile.Path, strFolderName

Next

Jak widzimy, skrypt rozpoczyna się od utworzenia wystąpienia obiektu VBScript.RegExp, który umożliwia nam wykorzystanie wyrażeń regularnych w skrypcie VBScript. Czy konieczne jest zastosowanie wyrażenia regularnego w tym skrypcie? Może nie jest konieczne, ale z pewnością ułatwi nam życie. Dlaczego? Cóż, MGD ma pliki podobne do poniższych:

C:\Images\P_19_L00.jpg

C:\Images\P_19_A01.jpg

C:\Images\P_7658_T00.jpg

C:\Images\P_7658_W04.jpg

C:\Images\P_8291517_NI4.jpg

Jak widać, liczba cyfr w nazwie pliku może być – i jest – różna. Gdyby liczba cyfr byłaby zawsze taka sama, wyrażenie regularne nie byłoby nam potrzebne; moglibyśmy zastosować funkcję Mid i pobrać 2, 3 lub 4 środkowe znaki. Ale to podejście działa tylko w przypadku, gdy liczba cyfr jest taka sama w każdej nazwie pliku. W przypadku plików MGD liczba cyfr jest inna w każdej nazwie pliku. Funkcja Mid sobie z tym nie poradzi; wyrażenie regularne tak.

Dlatego jest nam potrzebne.

Po utworzeniu wystąpienia obiektu RegExp konfigurujemy dwie właściwości tego obiektu. Na początek ustawiamy właściwość Global na True; dzięki temu skrypt wyszuka wszystkie wystąpienia tekstu docelowego. Szczerze mówiąc nie ma to w tym wypadku dużego znaczenia; w końcu i tak żadna nazwa pliku nie będzie zawierała więcej niż jednego wystąpienia tekstu docelowego. Jednakże w większości przypadków będziemy chcieli wyszukać wszystkie wystąpienia tekstu docelowego. Dlatego też powiem Wam, jak to zrobić.

Oprócz skonfigurowania właściwości Globar przypisujemy także wartość do właściwości Pattern:

objRegEx.Pattern = "_\d{1,}_"

Właściwość Pattern to miejsce, w którym definiujemy tekst docelowy (czyli tekst, którego szukamy). Za pomocą standardowej składni wyrażenia regularnego (j.ang.) ten wiersz informuje skrypt, że należy wyszukać znaku podkreślenia (_) z następującą po nim co najmniej jedną liczbą ( (\d{1,}) z następującym po nich kolejnym znakiem podkreślenia (_). Właściwość Pattern odnajdzie plik _19_ in P_19_L00.jpg. Ale nie odnajdzie wartości 19A w pliku P_19A_L00.jpg. Dlaczego? No właśnie: właściwość Pattern informuje skrypt, że jedynymi znakami, jakie pojawiają się pomiędzy dwoma znakami podkreślenia są cyfry z zakresu od 0 do 9. Plik P_19A_L00.jpg się nie nadaje, ponieważ nazwa zawiera literę A pomiędzy dwoma znakami podkreślenia.

Ma to jakiś sens, nieprawdaż? Rety, to naprawdę jest jakiś dziwny dzień!

Teraz możemy zacząć przenosić pliki. W tym celu najpierw tworzymy wystąpienie obiektu Scripting.FileSystemObject, a następnie używamy poniższego wiersza kodu w celu połączenia się z folderem C:\Images:

Set objFolder = objFSO.GetFolder("C:\Images")

Po utworzeniu połączenia z folderem możemy zastosować poniższy wiersz kodu w celu pobrania kolekcji wszystkich plików znajdujących się w tym folderze:

Set colFiles = objFolder.Files

Słuszna uwaga: ponieważ używamy obiektu FileSystemObject (zamiast usługi WMI) mamy tylko możliwość uruchomienia tego skryptu na komputerze lokalnym. Wybrałem to podejście z dwóch powodów: Po pierwsze, tego właśnie potrzebuje MGD, bez konieczności uruchamiania tego skryptu na komputerze zdalnym. Po drugie, uruchomienie tego skryptu na komputerze zdalnym może spowodować wystąpienia kilku potencjalnych problemów, w tym trudności w tworzeniu folderów na komputerze zdalnym (chociaż mamy rozwiązanie tego problemu). Czy możliwe jest wykonanie tego zadania na komputerze zdalnym? Tak, chociaż istnieją pewne ograniczenia. Jeżeli chcielibyście się dowiedzieć, jak to zrobić, dajcie nam znać; być może zajmiemy się tym w przyszłości.

Tymczasem jednakże uruchamiamy pętlę For Each, która przejdzie przez kolekcję plików znajdujących się w folderze C:\Images. Wewnątrz tej pętli pobieramy wartość właściwości Name pliku i zachowujemy ją w zmiennej o nazwie strSearchString:

strSearchString = objFile.Name

Następnie wywołujemy metodę Execute w celu znalezienia tekstu docelowego w nazwie pliku (liczby otoczone dwoma znakami podkreślenia):

Set colMatches = objRegEx.Execute(strSearchString)

Jeżeli znajdziemy tekst docelowy w nazwie pliku, wszystkie wystąpienia tego tekstu docelowego zostaną zachowane w kolekcji o nazwie colMatches. Aby dostać się do tych informacji wystarczy uruchomić drugą pętlę For Each, która tym razem przejdzie przez wszystkie elementy znajdujące się w kolekcji colMatches:

For Each strMatch in colMatches

Co robimy wewnątrz tej drugiej pętli? Cóż, na początek pobieramy pasujący tekst przypisując właściwość Value dopasowania do zmiennej o nazwie strFolderName:

strFolderName = strMatch.Value

W ten sposób zmienna strFolderName otrzyma wartość równą:

_19_

Słuszne spostrzeżenie: gdyby nie te znaki podkreślenia, byłaby to nazwa pliku, o którą nam chodziło, nieprawdaż? OK., pozbądźmy się więc tych znaków:

strFolderName = Replace(strFolderName, "_", "")

Używamy teraz funkcji Replace w celu zastąpienia czegoś (czyli wszelkich znaków podkreślenia) absolutnie niczym.

Uwaga. Czy firma Microsoft użyła funkcji Replace dodając Redaktorkę do zespołu Skrypciarzy? W zasadzie, cóż – nieważne. W to też się nie będę lepiej zagłębiał.

W wyniku naszych działań otrzymujemy zmienną strFolderName o wartości 19. Już jest lepiej, ale to jeszcze nie jest to, o co nam chodziło, abyśmy mogli przenieść pliki; do tego potrzebujemy kompletnej ścieżki, np. D:\19\. Aby ją uzyskać, posłużymy się poniższym wierszem kodu:

strFolderName = "D:\" & strFolderName & "\"

Po uzyskaniu kompletnej ścieżki pliku możemy zastosować następujący wiersz kodu w celu określenia, czy dany folder już istnieje:

If Not objFSO.FolderExists(strFolderName) Then

Jeżeli folder D:\19 już istnieje, to cudownie; oznacz to, że możemy kontynuować działanie pozostałej części skryptu. Jeżeli folder D:\19 nie istnieje, cóż – używamy poniższego wiersza kodu w celu utworzenia folderu o tej nazwie;

Set objNewFolder = objFSO.CreateFolder(strFolderName)

Po wykonaniu tych czynności możemy wreszcie przenieść plik P_19_L00.jpg z folderu C:\Images do folderu D:\19, a robimy to za pomocą tego oto kodu:

objFSO.MoveFile objFile.Path, strFolderName

Teraz powtarzamy po prostu cały proces dla następnego pliku znajdującego się w kolekcji.

I to naprawdę wszystko. Kiedy skrypt skończy działać, każdy plik w folderze C:\Images (lub przynajmniej każdy plik zawierający tekst docelowy gdzieś w nazwie pliku) zostanie przeniesiony do odpowiedniego folderu na dysku D. Nieźle, nie?

To powinno wystarczyć, MGD. Jeżeli chodzi o Skrypciarzy, tabela wyników przedstawia się następująco:

Poważne błędy Jean Ross

  1. Konkurencja 5 w Zimowej Olimpiadzie Skrypciarskiej 2008

Moje poważne błędy

  1. Konkurencja 8 w Zimowej Olimpiadzie Skrypciarskiej 2007
  2. Sprawa oprogramowania Anti-Spyware
  3. Pozytywne wyrażenie się o przeglądarce, której nazwy nie wolno wymawiać na głos
  4. Żartowanie z Microsoft Boba w artykule Centrum Skryptów

Itd., itp. (Cała lista ma około 20 stron.)

No to co, Jean: wygląda na to, że teraz jesteśmy kwita, nie?

 Do początku strony Do początku strony

Centrum skryptów - Systemy operacyjne