Använd inte globala variabler om det inte finns något annat alternativ. Det går bra för små saker som det här, men jag tror att det är viktigt att tidigt ta reda på hur man gör saker och ting rätt.
Så, globals är ganska fruktansvärda i allmänhet… om du inte bara använder dem som en referens och du aldrig ändrar dem från en funktion. Varför spelar det någon roll? Därför att om du har tio funktioner som alla använder samma variabel som till exempel ändrar vad de gör baserat på den variabeln, och någon av dem kan möjligen ändra den, blir plötsligt beteendet hos dem alla extremt oförutsägbart.
Vad du behöver bekanta dig med i stället är att fånga upp utdata från en funktion. Du matar ut data, objekt, text, vad du vill, och sedan lagrar du utmatningen i något när du anropar funktionen. När funktionen är klar innehåller den behållaren det värde du behöver.
Det här är mycket lättare att följa, eftersom du hamnar på rader som denna:
$variable = Function-Name -Parameter1 'ParameterValue'
Du vet med en gång att den här variabeln innehåller utdata från den funktionen, och du kan bara titta på funktionen för att se vad du får. Jämför med globala variabler:
$global:Var = 'initial value'Function-Name -Parameter1 'ParameterValue'# check the value of $global:Var$global:Var
Och du har ingen aning om den variabeln fortfarande innehåller det ursprungliga värdet. Denna effekt förvärras när du börjar använda globala variabler i stor utsträckning, och du kommer att ha mycket svårt att följa vad din egen kod gör.
Så, vad gör du för att hålla saker och ting förnuftiga?
I exemplet med din funktion:
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
Låt mig dela upp det:
- Funktioner först. Definiera dina funktioner innan du gör något annat. Funktioner kan användas var som helst i koden, så det är mycket mer användbart om du har ett konsekvent ställe att kontrollera dina funktioner på. Det är helt enkelt enklare att ha dem i början, så att du inte behöver leta i hela skriptet efter var du har lagt den funktion som du behöver ändra.
- Definiera parametrar. Alla data som din funktion behöver använda från det bredare skriptet bör definieras i dess parametrar. På så sätt kan du se på ett ögonblick vilka data en funktion behöver, och det gör också att du kan vara säker på att du kommer att ge den rätt data, och att du inte har minglat den någon annanstans i skriptet av misstag.
- Output!
Write-Output
är ett mycket tydligt sätt att göra det på, men som standard kommer det helt enkelt att låta data ”falla” till utdataströmmen genom att inte fånga upp den (t.ex. genom att låta den stå på en rad för sig själv) att leda till att den skrivs ut. Jag föredrar den tydligare syntaxen att specificera att ”ja, jag matar ut dessa data”. - Capturing! Att göra
$var = function-name
innebär i princip att ”anropa den här funktionen och fånga upp det som skrivs ut i variabeln$var
”. På så sätt kan du använda dessa data senare, och eftersom du definierar dem på samma ställe som du använder dem är det mycket lättare att följa vad du gör med din kod. - Objektstyper. Du använder typen PSObject och konstruerar den med hjälp av en hashtable. Det jag gör är mycket likartat, men du kommer i allmänhet att tycka att PsCustomObject är en mer flexibel typ för allt du behöver få gjort. Den formateras lättare och fungerar mer sömlöst med alla cmdlets.
-
Allmänna tips: De fyrkantiga parenteserna är vad som kallas ”attribut”. Det är extra bitar som du kan tillämpa på dina funktioner med olika användningsområden.
förklarar att detta är en ”avancerad funktion” (dumt namn för mig, men det är vad de valde att kalla den). I grund och botten gör detta att du kan använda dig av de standardparametrar som PS tillåter funktioner att ha. Du kan göra saker som att anropa
Write-Verbose
för att lämna information om vad du gör som normalt är dold (eftersom den vanligtvis inte behövs), men sedan anropa funktionen med parametern-Verbose
för att se meddelandena (om något inte går som du förväntar dig, så att du kan se var det ställer till det).attribut kan definieras för varje parameter på detta sätt. Du kan definiera specifika positioner, om de är obligatoriska och många andra saker, t.ex. om de accepterar pipelineinmatning.
Så, med detta i åtanke, här är hur jag själv skulle hantera den här funktionen:
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
Jag har mer eller mindre bara förkortat den, gjort några finare saker och fått den att fungera med pipeline så att du kan göra samma sak i färre steg. Jag inser att jag förmodligen överväldigar dig helt och hållet här, så var inte rädd för att peta i det bit för bit och ställa frågor om allt du är osäker på. Det finns saker här som jag inte nämnde ovan, men hej! Du kan antingen använda Get-Help
eller Google flitigt, och jag finns här för allt som du behöver en förklaring ”i ett nötskal” för, eller något som du har svårt att förstå.
God tur där ute, det kan bli lite vilt 😀
.