Brug ikke globale variabler, medmindre der bogstaveligt talt ikke er nogen anden mulighed, punktum. Det er fint til små ting som dette, men jeg tror, det er vigtigt at finde ud af, hvordan man gør tingene rigtigt tidligt.
Så, globals er ret forfærdelige generelt… medmindre du kun bruger dem som en reference, og du aldrig ændrer dem inde fra en funktion. Hvorfor er det vigtigt? Fordi hvis du har ti funktioner, der alle bruger den samme variabel, som f.eks. ændrer det, de gør, baseret på denne variabel, og enhver af dem kan muligvis ændre den, bliver deres adfærd pludselig ekstremt uforudsigelig.
Det, du i stedet skal gøre dig bekendt med, er at fange output fra en funktion. Du output data, objekter, tekst, hvad du vil, og så gemmer du dette output i noget, når du kalder funktionen. Når funktionen er færdig, indeholder denne beholder den værdi, du har brug for.
Det er meget nemmere at følge med i, fordi du ender med linjer som denne:
$variable = Function-Name -Parameter1 'ParameterValue'
Du ved med et blik, at denne variabel indeholder output fra den pågældende funktion, og du kan bare se på funktionen for at se, hvad du får. Sammenlign med globale variabler:
$global:Var = 'initial value'Function-Name -Parameter1 'ParameterValue'# check the value of $global:Var$global:Var
Og du har ingen anelse om, hvorvidt denne variabel stadig indeholder den oprindelige værdi. Denne effekt forstærkes, når du begynder at bruge globale variabler i stor stil, og du vil få meget svært ved at følge med i, hvad din egen kode gør.
Så, hvad gør du for at holde tingene fornuftige?
I eksemplet 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
Lad mig bryde det ned:
- Funktioner først. Definer dine funktioner, før du gør noget andet. Funktioner kan bruges overalt i koden, så det er meget mere nyttigt, hvis du har et konsekvent sted at kontrollere dine funktioner. Det er bare enklere at have dem i begyndelsen, så du ikke skal søge i hele scriptet efter, hvor du har lagt den funktion, som du skal ændre.
- Det er nemmere at definere parametre. Alle data, som din funktion skal bruge fra det bredere script, skal defineres i dens parametre. Dette giver dig mulighed for at se med ét blik, hvilke data en funktion har brug for, og det giver dig også mulighed for at være sikker på, at du vil give den de rigtige data, og at du ikke ved et uheld har blandet dem et andet sted i dit script.
- Output!
Write-Output
er en meget klar måde at gøre det på, men som standard vil det blot at lade data “falde” til outputstrømmen ved ikke at fange dem (f.eks. ved at lade dem stå på en linje for sig selv) medføre, at de bliver output. Jeg foretrækker den klarere syntaks ved at angive, at “ja, jeg udsender disse data”. - Fangst! At gøre
$var = function-name
betyder i bund og grund “kald denne funktion og opfang det, der outputes, i variablen$var
“. Dette giver dig mulighed for at bruge disse data senere, og fordi du definerer den samme sted, som du bruger den, er det meget nemmere at følge med i, hvad du laver med din kode. - Objekttyper. Du bruger PSObject-typen og konstruerer den ved hjælp af en hashtabel. Det, jeg gør, ligner meget, men du vil generelt finde PsCustomObject som en mere fleksibel type til alt, hvad du har brug for at få gjort. Den formaterer nemmere og fungerer mere problemfrit med alle cmdlets.
-
Ekstra godbidder: De firkantede parenteser er det, der kaldes “attributter”. Det er ekstra bits, som du kan anvende på dine funktioner med forskellige anvendelsesmuligheder.
erklærer, at dette er en “avanceret funktion” (dumt navn i mine øjne, men det er det, de har valgt at kalde den). Dybest set giver den dig mulighed for at gøre brug af de standardparametre, som PS tillader funktioner at have. Du kan gøre ting som at kalde
Write-Verbose
for at efterlade oplysninger om det, du laver, som normalt er skjult (fordi det normalt ikke er nødvendigt), men derefter kalde funktionen med-Verbose
-parameteren for at se beskederne (hvis noget ikke går, som du forventer, så du kan se, hvor det går galt).-attributter kan defineres for hver parameter på den måde. Du kan definere specifikke positioner, om de er obligatoriske, og mange andre ting, f.eks. om de accepterer pipeline-input.
Så, med det i tankerne, er her hvordan jeg selv ville håndtere denne funktion:
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
Jeg har mere eller mindre bare forkortet den, gjort nogle finere ting og fået den til at fungere med pipelinen, så du kan gøre det samme i færre trin. Jeg erkender, at jeg nok overvælder dig fuldstændigt her, så vær ikke bange for at pille i det stykke for stykke og stille spørgsmål om alt det, du er i tvivl om. Der er ting her, som jeg ikke har nævnt ovenfor, men hey! Du kan enten gøre rigelig brug af Get-Help
eller Google, og jeg er lige her for alt, hvad du har brug for en “in-a-nutshell” forklaring på, eller noget du har svært ved at forstå.
Godt held og lykke derude, det kan blive lidt vildt. 😀