No uses variables globales a menos que literalmente no haya otra opción, punto. Está bien para pequeñas cosas como esta, pero creo que es importante averiguar cómo hacer las cosas bien desde el principio.
Entonces, las globales son bastante terribles en general.. a menos que sólo las estés usando como referencia y nunca las cambies desde dentro de una función. ¿Por qué es importante? Porque si tienes diez funciones que usan la misma variable y que, digamos, cambian lo que hacen basándose en esa variable, y cualquiera de ellas podría cambiarla, de repente el comportamiento de todas ellas se vuelve extremadamente impredecible.
Lo que necesitas es familiarizarte con la captura de la salida de una función. Usted da salida a datos, objetos, texto, lo que quiera, y luego almacena esa salida en algo cuando llama a la función. Una vez que la función se completa, ese contenedor contiene el valor que necesitas.
Esto es mucho más fácil de seguir, porque terminas con líneas como esta:
$variable = Function-Name -Parameter1 'ParameterValue'
Sabes de un vistazo que esta variable contiene la salida de esa función, y puedes simplemente mirar la función para ver lo que estás obteniendo. Compara con las variables globales:
$global:Var = 'initial value'Function-Name -Parameter1 'ParameterValue'# check the value of $global:Var$global:Var
Y no tienes ni idea de si esa variable sigue manteniendo el valor original. Este efecto se agrava cuando empiezas a usar mucho las variables globales, y tendrás muchas dificultades para seguir lo que está haciendo tu propio código.
Entonces, ¿qué haces para mantener las cosas sensatas?
En el ejemplo de tu función:
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
Déjame desglosarlo:
- Las funciones primero. Defina sus funciones antes de hacer cualquier otra cosa. Las funciones pueden ser usadas en cualquier parte del código, así que es mucho más útil si tienes un lugar consistente para comprobar tus funciones. Es más simple mantenerlas al principio, así no estás buscando en todo el script dónde pusiste esa función que necesitas cambiar.
- Define los parámetros. Cualquier dato que tu función necesite usar del script más amplio debe ser definido en sus parámetros. Esto te permite ver de un vistazo qué datos necesita una función, y también te permite estar seguro de que le vas a dar los datos correctos, y que no los has mezclado en otra parte de tu script accidentalmente.
- ¡Salida!
Write-Output
es una forma muy clara de hacerlo, pero por defecto simplemente dejar que los datos ‘caigan’ al flujo de salida al no capturarlos (por ejemplo, dejarlos parados en una línea por sí mismos) hará que salgan. Prefiero la sintaxis más clara de especificar que «sí, estoy dando salida a estos datos». - ¡Capturar! Hacer
$var = function-name
significa básicamente «llamar a esta función y capturar cualquier salida en la variable$var
«. Esto te permite usar estos datos más tarde, y como lo estás definiendo en el mismo lugar en el que lo estás usando, es mucho más fácil seguir lo que estás haciendo con tu código. - Tipos de objetos. Estás usando el tipo PSObject, y construyéndolo mediante el uso de un hashtable. Lo que estoy haciendo es muy similar, pero generalmente encontrarás que PsCustomObject es un tipo más flexible para cualquier cosa que necesites hacer. Se formatea más fácilmente y funciona sin problemas con todos los cmdlets.
-
Adiciones: esos corchetes son lo que se conoce como «atributos». Son bits extra que puedes aplicar a tus funciones con diversos usos.
Declara que se trata de una «función avanzada» (nombre tonto para mí, pero es lo que han decidido llamar). Básicamente, esto le permite hacer uso de los parámetros por defecto que PS permite que las funciones tengan. Puedes hacer cosas como llamar a
Write-Verbose
para dejar información sobre lo que estás haciendo que normalmente está oculta (porque normalmente no es necesaria), pero luego llamar a la función con el parámetro-Verbose
para ver los mensajes (si algo no va como esperas, para que puedas ver dónde se está estropeando).Se pueden definir atributospara cada parámetro así. Puedes definir posiciones específicas, si son obligatorios, y muchas otras cosas, como si aceptan la entrada de la tubería.
Así que, con eso en mente, aquí está cómo manejaría esta función yo mismo:
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
Más o menos lo acorté, hice algunas cosas más elegantes, y lo hice trabajar con la tubería para que puedas hacer lo mismo en menos pasos. Reconozco que probablemente te estoy abrumando completamente aquí, así que por favor no tengas miedo de hurgar pieza por pieza y hacer preguntas sobre cualquier cosa que no estés seguro. Hay cosas que no he mencionado más arriba, pero ¡eh! Puedes hacer un uso copioso de Get-Help
o de Google, y yo estoy aquí para cualquier cosa que necesites una explicación «en una cáscara de nuez», o cualquier cosa que estés teniendo dificultad para entender.
Buena suerte ahí fuera, puede ser un poco salvaje. 😀