Centrum skryptów - Systemy operacyjne

Jak usunąć wszystkie duplikaty danego procesu, ale zachować najstarszy proces?

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 usunąć wszystkie duplikaty danego procesu, ale zachować najstarszy proces?

Cześć Skrypciarze! Pytanie

Cześć, Skrypciarze! Cześć, Skrypciarze! Jeśli mam kilka zduplikowanych procesów, jak usunąć je wszystkie oprócz najstarszego?

-- IR

Cześć Skrypciarze! Odpowiedź

Cześć, IR. Przepraszamy za opóźnienie w odpowiadaniu na to pytanie. Na początku uznaliśmy, że miło będzie, jeśli Peter Costantini (j.ang.), najstarszy żyjący Skrypciarz, odpowie na to pytanie. W końcu uznaliśmy, że Petera ucieszy scenariusz, gdzie wszystko oprócz najstarszego zostanie wyeliminowane. Spytaliśmy nawet Petera, czy zna rozwiązanie tego problemu, ale zamiast odpowiedzieć na pytanie, zaczął opowiadać nam o wojnie hiszpańsko-amerykańskiej i dniu, gdy otrzymał swój pierwszy Model T. W końcu włączył Koło Fortuny i zasnął. Gdy się obudził, znowu zaczął opowiadać o wojnie hiszpańsko-amerykańskiej (Najwyraźniej on i Teddy Roosevelt byli rywalami).

Wtedy właśnie uznaliśmy, że lepiej będzie, jeśli sami odpowiemy na to pytanie:

strComputer = "."



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



Set colItems = objWMIService.ExecQuery _

    ("Select * From Win32_Process Where Name = 'Notepad.exe'")



If colItems.Count < 2 Then

    Wscript.Quit

End If



dtmTarget = Now



For Each objItem in colItems

    dtmDateHolder = objItem.CreationDate

    

    dtmDateHolder = CDate(Mid(dtmDateHolder, 5, 2) & "/" & _

        Mid(dtmDateHolder, 7, 2) & "/" & Left(dtmDateHolder, 4) _

            & " " & Mid (dtmDateHolder, 9, 2) & ":" & _

                Mid(dtmDateHolder, 11, 2) & ":" & Mid(dtmDateHolder, 13, 2))



    If dtmDateHolder < dtmTarget Then

         intProcessID = objItem.ProcessID

         dtmTarget = dtmDateHolder

    End If

Next



Set colItems = objWMIService.ExecQuery _

    ("Select * From Win32_Process Where Name = 'Notepad.exe' " & _

        "AND ProcessID <> " & intProcessID)



For Each objItem in colItems

    objItem.Terminate

Next

Mówicie, że jest to trochę skomplikowane? Może troszeczkę. Ale nie martwcie się; wyjaśnimy jak to działa, krok po kroku.

Uwaga:

Peter chciałby, abyśmy poinformowali wszystkich, że w jego czasach nie potrzebowali wymyślnych rzeczy jak Windows i Notatnik, aby zamknąć parę procesów. Starczyła karta perforowana i stary dobry Univac i w parę dni pozbyłby si tych wrednych starych procesów. Oczywiście po przejściu 10 mil w sięgającym do pasa śniegu, aby dotrzeć do biura.

 

Jak widzicie, nasz skrypt zaczyna się dość prosto, wiążąc się z usługą WMI na lokalnym komputerze. (Chociaż skrypt ten działa równie dobrze na zdalnych maszynach; wystarczy jedynie przydzielić nazwę zdalnego komputera zmiennej strComputer.) W tym przykładzie, postanowiliśmy pozbyć się wszystkich “dodatkowych” działających kopii Notatnika. Pamiętając o tym, wykonujemy tę kwerendę, aby otrzymać kolekcję wszystkich procesów z Name równym Notepad.exe:

Set colItems = objWMIService.ExecQuery _

("Select * From Win32_Process Where Name = 'Notepad.exe'")

Teraz pójdziemy trochę okrężną drogą. Załóżmy, że działa tylko jedno wystąpienie Notatnika; innymi słowy, załóżmy, że nie działają żadne zduplikowane procesy. W takim wypadku zakładamy, że nie chcemy zamknąć naszego jedynego wystąpienia Notatnika. Dlatego dodaliśmy następujący fragment kodu:

If colItems.Count < 2 Then

    Wscript.Quit

End If

Sprawdzamy tutaj wartość właściwości Count aby sprawdzić, ile wystąpień Notatnika Notepad naprawdę działa. Jeśli Count wynosi mniej niż 2, oznacza to, że mamy najwyżej jedno wystąpienie Notatnika. W takim wypadku nie mamy nic do roboty: nie ma żadnych zduplikowanych procesów do zamknięcia. Dlatego wywołujemy metodę Wscript.Quit i zamykamy skrypt.

Załóżmy jednak, że mamy wiele wystąpień Notatnika. W takim wypadku musimy określić, które z nich jest najstarsze (czyli utworzona najwcześniej), a następnie usuwamy wszystkie pozostałe. Jest na to kilka sposobów, ale wybraliśmy następujące podejście.

Po pierwsze przydzielamy aktualną datę i godzine (przy użyciu funkcji Now) zmiennej o nazwie dtmTarget. Po co? Zamierzamy porównać datę i czas utworzenia każdego wystąpienia Notatnika z tą datą i czasem. Załóżmy, że sprawdzamy czas utworzenia wystąpienia 1i okazuje się, że jest starsze niż wartość dtmTarget (a tak będzie). W takim wypadku, wystąpienie Notatnika 1 stanie się naszym najstarszym wystąpieniem Notatnika i przydzielimy jego datę i czas utworzenia zmiennej dtmTarget. Następnie porównamy datę i czas utworzenia wystąpienia 2 z nową wartością dtmTarget. Co jeśli wystąpienie 2 jest “młodsze” niż dtmTarget? Żaden problem; wtedy po prostu powtarzamy proces z wystąpieniem Notatnika 3.

Jeśli zaś wystąpienie Notatnika 2 jest starsze niż dtmTarget, przydzielamy jego czas utworzenia zmiennej dtmTarget. Oznacza to, że przynajmniej na razie, wystąpienie notatnika 2 jest naszym najstarszym wystąpieniem.

Rozumiecie? Jeśli nie, zostańcie z nami; pokażemy wam dokładnie, jak to działa. Po pobraniu wszystkich wystąpień Notatnika tworzymy pętlę For Each, aby zająć się każdym elementem kolekcji. Pierwszą rzeczą, którą robimy wewnątrz pętli jest przydzielenie wartości właściwości CreationDate procesu zmiennej o nazwie:

dtmDateHolder:

dtmDateHolder = objItem.CreationDate

Tutaj sprawy się trochę komplikują. Wartość właściwości CreationDate property – jak większość wartości daty i czasu WMI – przechowywana jest w formacie Universal Time Coordinate (UTC); oznacza to, że dtmDateHolder będzie równe czemuś takiemu:

20070504085221.620161-420

Z wartością UTC niewiele możemy zrobić, przynajmniej nie w tym formacie. Zamiast tego musimy przekonwertować ją do “prawdziwej” wartości daty i czasu. To właśnie robi następujący fragment kodu:

dtmDateHolder = CDate(Mid(dtmDateHolder, 5, 2) & "/" & _

    Mid(dtmDateHolder, 7, 2) & "/" & Left(dtmDateHolder, 4) _

        & " " & Mid (dtmDateHolder, 9, 2) & ":" & _

            Mid(dtmDateHolder, 11, 2) & ":" & Mid(dtmDateHolder, 13, 2))

Nie omówimy szczegółów konwertowania wartości UTC do zwykłej wartości daty i czasu; szczegółowe informacje na ten temat można znaleźć w Microsoft Windows 2000 Scripting Guide (j.ang.). Powiemy tylko, że powyższy fragment kodu zmienia wartość taką jak 20070504085221.620161-420 w taką wartość:

5/4/2007 8:52:21 AM

Z taką wartością o wiele łatwiej nam się pracuje. Co ważniejsze, łatwiej pracuje się z nią VBScriptowi. OK. Przyjmijmy teraz, że wartość dtmTarget jest równa:

5/4/2007 8:58:13 AM

W naszym kolejnym wierszu kodu sprawdzamy, czy nasz pierwszy proces (dtmDateHolder) jest starszy niż następująca docelowa data:

If dtmHolder < dtmTarget Then

W tym wypadku będzie to prawda. Nasz proces jest starszy niż docelowa data. Robimy więc dwie rzecy 1) przydzielamy wartość właściwości ProcessID zmiennej o nazwie intProcessID i 2) przydzielamy wartość dtmDateHolder naszej docelowej zmiennej (dtmTarget). To właśnie robią poniższe wiersze:

intProcessID = objItem.ProcessID

dtmTarget = dtmDateHolder

Po co to wszystko? Cóż, teraz znamy datę i czas utworzenia najstarszego procesu (a przynajmniej najstarszego z na razie sprawdzonych). Oprócz tego znamy również identyfikator najstarszego procesu. To ważne, bo gdy trzeba będzie zacząć usuwanie procesów, potrzebujemy prostego sposobu na upewnienie się, że nie usuniemy najstarszego z nich. Jako że identyfikatory procesów są unikatowe, to wydaje się być prosty sposób na odróżnienie najstarszego procesu od reszty stada.

Następnie skrypt się zapętla i robimy to ponownie z następnym elementem kolekcji. Gdy sprawdzimy już całą kolekcję, zmienna intProcessID zawierać będzie identyfikator procesu najstarszego wystąpienia Notatnika na danym komputerze. Nadal jesteście z nami? Dobrze. Prawie skończyliśmy. Obiecujemy.

Czas teraz pozbyć się całej tej notatnikowej zepsutej młodzieży. Jak to zrobimy? Na początek pobierzemy nową kolekcję procesów, używając następującej kwerendy

Set colItems = objWMIService.ExecQuery _

    ("Select * From Win32_Process Where Name = 'Notepad.exe' " & _

        "AND ProcessID <> " & intProcessID)

W tej kwerendzie nadal pobieramy wszystkie elementy klasy Win32_Process o Name równym Notepad.exe. Dodaliśmy jednak kolejny warunek: interesują nas jedynie te wystąpienia Notatnika, gdzie ProcessID nie jest równe intProcessID. Po to jest właśnie ten fragment:

AND ProcessID <> " & intProcessID

Co się stanie? Kwerenda pobierze wszystkie wystąpienia Notatnika oprócz wystapienia o identyfikatorze procesu równym intProcessID. A jako że intProcessID jest identyfikatorem procesu naszego najstarszego wystąpienia Notatnika, oznacza to, że nasza kolekcja zawierać będzie wszystkie wystąpienia Notatnika oprócz najstarszego.

Oznacza to również, że możemy teraz usunąć wszystkie wystąpienia Notatnika (oczywiście oprócz najstarszego) przy użyciu następującego fragmentu kodu:

For Each objItem in colItems

    objItem.Terminate

Next

I już. Gdyby tylko Peter mógł dożyć tej chwili...

Uwaga:

Peter poinformował nas właśnie, że jednak żyje. Wierzymy mu, ale wciąż wolelibyśmy zasięgnąć drugiej opinii.

 

Tak przy okazji, wielu z was pyta nas, ile właściwie Peter ma lat. Jak wspomnieliśmy w jednym z webcastów), ma jedynie 27 lat. Ale jest stary w porównaniu z resztą Skrypciarzy, którzy dopiero co przestali być nastolatkami. Zaraz... jeśli tak, to jak Syn Skrypciarza może mieć 17 lat? To bardzo dobre pytanie i chętnie kiedyś na nie odpowiemy.

 Do początku strony Do początku strony

Centrum skryptów - Systemy operacyjne