Não usar variáveis globais a menos que não haja literalmente nenhuma outra opção, ponto final. É bom para pequenas coisas como esta, mas eu acho importante descobrir como fazer as coisas bem cedo.
Então, os globais são bastante terríveis em geral… a menos que você esteja usando-os apenas como referência e você nunca os mude de dentro de uma função. Porque é que isso importa? Porque se você tem dez funções todas usando a mesma variável que dizem, mude o que elas estão fazendo com base nessa variável, e qualquer uma delas poderia possivelmente mudá-la, de repente o comportamento de todas elas torna-se extramamente imprevisível.
O que você precisa se familiarizar em vez disso é capturar a saída de uma função. Você produz dados, objetos, texto, o que você quiser, e então você armazena essa saída em algo ao chamar a função. Uma vez que a função esteja completa, aquele container contém o valor que você precisa.
Isto é muito mais fácil de seguir, porque você acaba com linhas como esta:
$variable = Function-Name -Parameter1 'ParameterValue'
Você sabe num relance que esta variável contém a saída daquela função, e você pode apenas olhar para a função para ver o que você está obtendo. Compare com variáveis globais:
$global:Var = 'initial value'Function-Name -Parameter1 'ParameterValue'# check the value of $global:Var$global:Var
E você não tem idéia se essa variável ainda detém o valor original. Este efeito é agravado quando você começa a usar fortemente variáveis globais, e você terá muita dificuldade em seguir o que seu próprio código está fazendo.
Então, o que você faz para manter as coisas sensatas?
No exemplo de sua função:
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
Deixe-me decompor:
- Funções primeiro. Defina as suas funções antes de fazer qualquer outra coisa. As funções podem ser usadas em qualquer parte do código, por isso é muito mais útil se você tiver um lugar consistente para verificar as suas funções. É mais simples mantê-las no início, então você não está procurando no script inteiro por onde colocar a função que você precisa alterar.
- Definir parâmetros. Quaisquer dados que sua função precise usar a partir do script mais amplo devem ser definidos em seus parâmetros. Isso permite que você veja rapidamente quais dados uma função precisa, e também permite que você esteja certo de que você estará dando os dados certos, e que você não os tenha inserido em outro lugar no seu script acidentalmente.
- Output!
Write-Output
é uma forma muito clara de fazer isso, mas por padrão simplesmente deixar os dados ‘caírem’ para o fluxo de saída ao não capturá-los (por exemplo, deixá-los em uma linha por si só) fará com que eles sejam emitidos. Eu prefiro a sintaxe mais clara de especificar que “sim, eu estou a sair estes dados”. - Capturar! Fazer
$var = function-name
basicamente significa “chamar esta função e capturar qualquer saída para a variável$var
“. Isto permite que você use estes dados mais tarde, e porque você está definindo no mesmo lugar que você está usando, é muito mais fácil seguir o que você está fazendo com seu código. - Tipos de objeto. Você está usando o tipo PSObject, e construindo-o usando uma hashtable. O que estou fazendo é muito parecido, mas você geralmente vai achar o PsCustomObject um tipo mais flexível para qualquer coisa que você precise fazer. Ele formata mais facilmente e funciona mais perfeitamente com todos os cmdlets.
-
Adicional tidbits: esses parênteses rectos são os chamados “atributos”. Eles são bits extras que você pode aplicar às suas funções com vários usos.
declara que esta é uma ‘função avançada’ (nome tolo para mim, mas é o que eles escolheram chamar-lhe). Basicamente isto permite que você faça uso dos parâmetros padrão que o PS permite que as funções tenham. Você pode fazer coisas como chamar
Write-Verbose
para deixar informações sobre o que você está fazendo que normalmente estão escondidas (porque normalmente não é necessário), mas depois chamar a função com o parâmetro-Verbose
para ver as mensagens (se algo não está indo como você espera, então você pode ver onde está mexendo).atributos podem ser definidos para cada parâmetro como esse. Você pode definir posições específicas, se elas são obrigatórias, e muitas outras coisas, como por exemplo, se elas aceitam entrada de pipeline.
Então, com isso em mente, aqui está como eu mesmo lidaria com esta função:
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
Eu mais ou menos apenas a encurtei, fiz algumas coisas mais extravagantes, e a fiz funcionar com o pipeline para que você possa fazer a mesma coisa em menos passos. Reconheço que estou provavelmente a sobrecarregá-lo completamente aqui, por isso não tenha medo de o cutucar pedaço a pedaço e fazer perguntas sobre qualquer coisa que não tenha a certeza. Há coisas aqui que eu não mencionei acima, mas ei! Você pode fazer um uso copioso de Get-Help
ou do Google, e eu estou aqui para qualquer coisa que você precise de uma explicação ‘in-a-nutshell’, ou qualquer coisa que você esteja tendo dificuldade em entender.
Bom sorte lá fora, pode ficar um pouco selvagem. 😀