Nie używaj zmiennych globalnych, chyba że nie ma dosłownie żadnej innej opcji, kropka. To jest w porządku dla małych rzeczy, takich jak ta, ale myślę, że ważne jest, aby dowiedzieć się, jak robić rzeczy dobrze na początku.
Więc, globale są dość straszne w ogóle… chyba że używasz ich tylko jako odniesienia i nigdy nie zmieniasz ich z poziomu funkcji. Dlaczego to ma znaczenie? Ponieważ jeśli masz dziesięć funkcji używających tej samej zmiennej, które zmieniają to, co robią w oparciu o tę zmienną, a każda z nich mogłaby ją zmienić, nagle zachowanie ich wszystkich staje się skrajnie nieprzewidywalne.
To, z czym musisz się zapoznać zamiast tego, to przechwytywanie danych wyjściowych z funkcji. Wyprowadzasz dane, obiekty, tekst, cokolwiek chcesz, a następnie przechowujesz to wyjście w czymś podczas wywoływania funkcji. Po zakończeniu funkcji, ten pojemnik przechowuje wartość, której potrzebujesz.
To jest znacznie łatwiejsze do naśladowania, ponieważ kończysz z liniami takimi jak ta:
$variable = Function-Name -Parameter1 'ParameterValue'
Wiesz na pierwszy rzut oka, że ta zmienna zawiera dane wyjściowe z tej funkcji i możesz po prostu spojrzeć na funkcję, aby zobaczyć, co dostajesz. Porównaj ze zmiennymi globalnymi:
$global:Var = 'initial value'Function-Name -Parameter1 'ParameterValue'# check the value of $global:Var$global:Var
I nie masz pojęcia, czy ta zmienna nadal przechowuje oryginalną wartość. Efekt ten potęguje się, gdy zaczynasz intensywnie używać zmiennych globalnych i będziesz miał wiele trudności z podążaniem za tym, co robi twój własny kod.
Co więc zrobić, aby zachować rozsądek?
W przykładzie twojej funkcji:
function Get-DiskSize { param( $Drives ) $Drives | ForEach-Object { $Vol = ("$_" + ":") $WMIPriorDisk = Get-WmiObject Win32_LogicalDisk -Filter "DeviceID='$Vol'" | Select-Object Size, FreeSpace $PriorDiskObject = @{ DriveLetter = $Vol DiskSizePrior = "{0:N2}" -f ($WMIPriorDisk.Size / 1GB) DiskFreePrior = "{0:N2}" -f ($WMIPriorDisk.FreeSpace / 1GB) } Write-Output $PriorDiskObject } }$Drives = (::GetDrives() | Where-Object {$_.DriveType -eq "Fixed"} | Select-Object -ExpandProperty 'Name').Replace(":\","")$DisksPrior = Get-DiskSize -Drives $Drives
Pozwól mi to rozbić:
- Najpierw funkcje. Zdefiniuj swoje funkcje zanim zrobisz cokolwiek innego. Funkcje mogą być używane w dowolnym miejscu w kodzie, więc jest to o wiele bardziej użyteczne, jeśli masz spójne miejsce do sprawdzania swoich funkcji. Po prostu prościej jest trzymać je na początku, więc nie przeszukujesz całego skryptu w poszukiwaniu miejsca, w którym umieściłeś funkcję, którą musisz zmienić.
- Zdefiniuj parametry. Wszelkie dane, które twoja funkcja musi wykorzystać z szerszego skryptu, powinny być zdefiniowane w jej parametrach. To pozwala ci zobaczyć na pierwszy rzut oka, jakich danych potrzebuje funkcja, a także pozwala ci się upewnić, że podajesz jej właściwe dane i nie pomyliłeś ich przypadkowo w innym miejscu skryptu.
- Output!
Write-Output
jest bardzo jasnym sposobem na zrobienie tego, ale domyślnie po prostu pozwolenie, by dane 'spadły’ do strumienia wyjściowego przez nie przechwycenie ich (np. pozwolenie, by stały w wierszu same z siebie) spowoduje, że zostaną one wypisane. Wolę bardziej przejrzystą składnię określającą, że „tak, wyprowadzam te dane”. - Capturing! Wykonywanie
$var = function-name
w zasadzie oznacza „wywołaj tę funkcję i przechwyć cokolwiek na wyjściu do zmiennej$var
„. Pozwala to na późniejsze wykorzystanie tych danych, a ponieważ definiujesz je w tym samym miejscu, w którym ich używasz, znacznie łatwiej jest śledzić, co robisz ze swoim kodem. - Typy obiektów. Używasz typu PSObject i konstruujesz go za pomocą hashtable. To, co robię, jest bardzo podobne, ale ogólnie rzecz biorąc, znajdziesz PsCustomObject, aby być bardziej elastycznym typem dla wszystkiego, co musisz zrobić. Formatuje się łatwiej i działa płynniej ze wszystkimi cmdletami.
-
Dodatkowe ciekawostki: te nawiasy kwadratowe są znane jako „atrybuty”. Są to dodatkowe bity, które możesz zastosować do swoich funkcji z różnymi zastosowaniami.
deklaruje, że jest to „funkcja zaawansowana” (głupia nazwa dla mnie, ale tak właśnie postanowili ją nazwać). Zasadniczo pozwala ci to na wykorzystanie domyślnych parametrów, które PS pozwala mieć funkcjom. Możesz robić takie rzeczy jak wywołanie
Write-Verbose
aby zostawić informacje o tym co robisz, które normalnie są ukryte (ponieważ zazwyczaj nie są potrzebne), ale następnie wywołaj funkcję z parametrem-Verbose
aby zobaczyć komunikaty (jeśli coś nie idzie tak jak oczekujesz, więc możesz zobaczyć gdzie jest bałagan).atrybuty mogą być zdefiniowane dla każdego parametru w ten sposób. Możesz zdefiniować konkretne pozycje, czy są obowiązkowe, i wiele innych rzeczy, takich jak to, czy akceptują dane wejściowe rurociągu.
Tak więc, mając to na uwadze, oto jak sam poradziłbym sobie z tą funkcją:
function Get-DiskSize { param( ] $Drives ) process { $Drives | ForEach-Object { $Vol = ("$_" + ":") Get-WmiObject Win32_LogicalDisk -Filter "DeviceID='$Vol'" | Select-Object -Property @{ Name = 'TotalSpace' Expression = { "{0:N2}" -f ($_.Size / 1GB) } }, @{ Name = 'FreeSpace' Expression = { "{0:N2}" -f ($_.FreeSpace / 1GB) } } } } }$DisksPrior = (::GetDrives() | Where-Object {$_.DriveType -eq "Fixed"} | Select-Object -ExpandProperty 'Name').Replace(":\", "") | Get-DiskSize
Mniej więcej po prostu skróciłem ją, zrobiłem kilka bardziej wymyślnych rzeczy i sprawiłem, że działa z potokiem, dzięki czemu możesz zrobić to samo w mniejszej liczbie kroków. Zdaję sobie sprawę, że prawdopodobnie całkowicie cię tu przytłaczam, więc proszę nie bój się szturchać tego kawałek po kawałku i zadawać pytania o wszystko, czego nie jesteś pewien. Są tu rzeczy, o których nie wspomniałam powyżej, ale hej! Możesz albo obficie korzystać z Get-Help
lub Google, a ja jestem tutaj dla wszystkiego, co potrzebujesz 'w pigułce’ wyjaśnienia, lub czego nie rozumiesz.
Powodzenia, to może być trochę dzikie 😀
.