Pide tu presupuesto ya!
¿Estás cansado de ver esos molestos mensajes de error rojos en tus scripts de PowerShell? Si bien pueden parecer intimidantes, el manejo adecuado de los errores es esencial para crear una automatización PowerShell confiable. En este tutorial, aprenderá cómo implementar un manejo sólido de errores en sus scripts, desde comprender los tipos de errores hasta dominar los bloques try/catch.
Este tutorial asume que tienes:
Antes de profundizar en el manejo de errores, debe comprender los dos tipos principales de errores que puede generar PowerShell:
Estos son los errores más graves: errores que detienen por completo la ejecución del script. Encontrarás errores de terminación cuando:
ErrorAction StopEstos son errores operativos más comunes que no detendrán su script:
Comencemos con un ejemplo práctico. Aquí hay una secuencia de comandos que intenta eliminar archivos que tengan más de una determinada cantidad de días:
param (
[Parameter(Mandatory)]
[string]$FolderPath,
[Parameter(Mandatory)]
[int]$DaysOld
)
$Now = Get-Date
$LastWrite = $Now.AddDays(-$DaysOld)
$oldFiles = (Get-ChildItem -Path $FolderPath -File -Recurse).Where{$_.LastWriteTime -le $LastWrite}
foreach ($file in $oldFiles) {
Remove-Item -Path $file.FullName
Write-Verbose -Message "Successfully removed [$($file.FullName)]."
}
Por defecto, Remove-Item genera errores no terminantes. Para que genere errores de terminación que podamos detectar, agregue -ErrorAction Stop:
Remove-Item -Path $file.FullName -ErrorAction Stop
Ahora incluyamos la eliminación de nuestro archivo en un bloque try/catch:
foreach ($file in $oldFiles) {
try {
Remove-Item -Path $file.FullName -ErrorAction Stop
Write-Verbose -Message "Successfully removed [$($file.FullName)]."
}
catch {
Write-Warning "Failed to remove file: $($file.FullName)"
Write-Warning "Error: $($_.Exception.Message)"
}
}
El bloque try contiene código que podría generar un error. Si ocurre un error, la ejecución salta al bloque catch (si es un error de terminación) donde puedes:
PowerShell mantiene una serie de objetos de error en el modo automático. $Error variable. pensar en $Error Como “grabador de caja negra” de PowerShell: realiza un seguimiento de cada error que ocurre durante la sesión de PowerShell, lo que lo hace invaluable para la resolución de problemas y la depuración.
A continuación le indicamos cuándo y por qué es posible que desee utilizar $Error:
Solución de problemas de errores pasados: Incluso si no vio un mensaje de error rojo, $Error mantiene una historia:
# View most recent error details
$Error[0] | Format-List * -Force
# Look at the last 5 errors
$Error[0..4] | Select-Object CategoryInfo, Exception
# Search for specific types of errors
$Error | Where-Object { $_.Exception -is [System.UnauthorizedAccessException] }
Guiones de depuración: Usar $Error para entender qué salió mal y dónde:
# Get the exact line number and script where the error occurred $Error[0].InvocationInfo | Select-Object ScriptName, ScriptLineNumber, Line # See the full error call stack $Error[0].Exception.StackTrace
Recuperación e informes de errores: Perfecto para crear informes de errores detallados:
# Create an error report
function Write-ErrorReport {
param($ErrorRecord = $Error[0])[PSCustomObject]@{
TimeStamp = Get-Date
ErrorMessage = $ErrorRecord.Exception.Message
ErrorType = $ErrorRecord.Exception.GetType().Name
Command = $ErrorRecord.InvocationInfo.MyCommand
ScriptLine = $ErrorRecord.InvocationInfo.Line
ErrorLineNumber = $ErrorRecord.InvocationInfo.ScriptLineNumber
StackTrace = $ErrorRecord.ScriptStackTrace
}
}
Gestión de sesiones: Limpiar errores o comprobar el estado del error:
# Clear error history (useful at the start of scripts)
$Error.Clear()
# Count total errors (good for error threshold checks)
if ($Error.Count -gt 10) {
Write-Warning "High error count detected: $($Error.Count) errors"
}
Ejemplo del mundo real que combina estos conceptos:
function Test-DatabaseConnections {
$Error.Clear() # Start fresh
try {
# Attempt database operations...
}
catch {
# If something fails, analyze recent errors
$dbErrors = $Error | Where-Object {
$_.Exception.Message -like "*SQL*" -or
$_.Exception.Message -like "*connection*"
}
if ($dbErrors) {
Write-ErrorReport $dbErrors[0] |
Export-Csv -Path "C:\\Logs\\DatabaseErrors.csv" -Append
}
}
}
Consejos profesionales:
$Error se mantiene por sesión de PowerShell$MaximumErrorCount)$Error[0] primero: es el error más reciente$Error al inicio de scripts importantes para un seguimiento limpio de erroresAsí como no usarías la misma herramienta para cada trabajo de reparación del hogar, no debes manejar todos los errores de PowerShell de la misma manera. Múltiples bloques catch le permiten responder de manera diferente a diferentes tipos de errores.
Así es como funciona:
try {
Remove-Item -Path $file.FullName -ErrorAction Stop
}
catch [System.UnauthorizedAccessException] {
# This catches permission-related errors
Write-Warning "Access denied to file: $($file.FullName)"
Request-ElevatedPermissions -Path $file.FullName # Custom function
}
catch [System.IO.IOException] {
# This catches file-in-use errors
Write-Warning "File in use: $($file.FullName)"
Add-ToRetryQueue -Path $file.FullName # Custom function
}
catch [System.Management.Automation.ItemNotFoundException] {
# This catches file-not-found errors
Write-Warning "File not found: $($file.FullName)"
Update-FileInventory -RemovePath $file.FullName # Custom function
}
catch {
# This catches any other errors
Write-Warning "Unexpected error: $_"
Write-EventLog -LogName Application -Source "MyScript" -EntryType Error -EventId 1001 -Message $_
}
Tipos de errores comunes que encontrará:
[System.UnauthorizedAccessException] – Permiso denegado[System.IO.IOException] – Archivo bloqueado/en uso[System.Management.Automation.ItemNotFoundException] – Archivo/ruta no encontrada[System.ArgumentException] – Argumento no válido[System.Net.WebException] – Problemas de red/webAquí hay un ejemplo del mundo real que pone esto en práctica:
function Remove-StaleFiles {
[CmdletBinding()]
param(
[string]$Path,
[int]$RetryCount = 3,
[int]$RetryDelaySeconds = 30
)
$retryQueue = @()
foreach ($file in (Get-ChildItem -Path $Path -File)) {
$attempt = 0
do {
$attempt++
try {
Remove-Item -Path $file.FullName -ErrorAction Stop
Write-Verbose "Successfully removed $($file.FullName)"
break # Exit the retry loop on success
}
catch [System.UnauthorizedAccessException] {
if ($attempt -eq $RetryCount) {
# Log to event log and notify admin
$message = "Permission denied after $RetryCount attempts: $($file.FullName)"
Write-EventLog -LogName Application -Source "FileCleanup" -EntryType Error -EventId 1001 -Message $message
Send-AdminNotification -Message $message # Custom function
}
else {
# Request elevated permissions and retry
Request-ElevatedAccess -Path $file.FullName # Custom function
Start-Sleep -Seconds $RetryDelaySeconds
}
}
catch [System.IO.IOException] {
if ($attempt -eq $RetryCount) {
# Add to retry queue for later
$retryQueue += $file.FullName
Write-Warning "File locked, added to retry queue: $($file.FullName)"
}
else {
# Wait and retry
Write-Verbose "File in use, attempt $attempt of $RetryCount"
Start-Sleep -Seconds $RetryDelaySeconds
}
}
catch {
# Unexpected error - log and move on
$message = "Unexpected error with $($file.FullName): $_"
Write-EventLog -LogName Application -Source "FileCleanup" -EntryType Error -EventId 1002 -Message $message
break # Exit retry loop for unexpected errors
}
} while ($attempt -lt $RetryCount)
}
# Return retry queue for further processing
if ($retryQueue) {
return $retryQueue
}
}
Consejos profesionales para múltiples bloques de captura:
El bloque final es su equipo de limpieza: siempre se ejecuta, haya un error o no. Esto lo hace perfecto para:
He aquí un ejemplo práctico:
try {
$stream = [System.IO.File]::OpenRead($file.FullName)
# Process file contents here...
}
catch {
Write-Warning "Error processing file: $_"
}
finally {
# This runs even if an error occurred
if ($stream) {
$stream.Dispose()
Write-Verbose "File handle released"
}
}
Finalmente, piense en la regla de un campista responsable: “Siempre limpie su campamento antes de partir, sin importar lo que sucedió durante el viaje”.
Sea específico con las acciones de error
En lugar de manta ErrorAction Stopúselo selectivamente en comandos donde necesite detectar errores.
Usar variables de error
Remove-Item $path -ErrorVariable removeError
if ($removeError) {
Write-Warning "Failed to remove item: $($removeError[0].Exception.Message)"
}
Registrar errores adecuadamente
Limpiar recursos
Utilice siempre bloques finalmente para limpiar recursos como identificadores de archivos y conexiones de red.
Manejo de errores de prueba
Active errores deliberadamente para verificar que el manejo de errores funcione como se esperaba.
A continuación se muestra un ejemplo completo que incorpora estas mejores prácticas:
function Remove-OldFiles {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[string]$FolderPath,
[Parameter(Mandatory)]
[int]$DaysOld,
[string]$LogPath = "C:\\Logs\\file-cleanup.log"
)
try {
# Validate input
if (-not (Test-Path -Path $FolderPath)) {
throw "Folder path '$FolderPath' does not exist"
}
$Now = Get-Date
$LastWrite = $Now.AddDays(-$DaysOld)
# Find old files
$oldFiles = Get-ChildItem -Path $FolderPath -File -Recurse |
Where-Object {$_.LastWriteTime -le $LastWrite}
foreach ($file in $oldFiles) {
try {
Remove-Item -Path $file.FullName -ErrorAction Stop
Write-Verbose -Message "Successfully removed [$($file.FullName)]"
# Log success
"$(Get-Date) - Removed file: $($file.FullName)" |
Add-Content -Path $LogPath
}
catch [System.UnauthorizedAccessException] {
Write-Warning "Access denied to file: $($file.FullName)"
"$ErrorActionPreference - Access denied: $($file.FullName)" |
Add-Content -Path $LogPath
}
catch [System.IO.IOException] {
Write-Warning "File in use: $($file.FullName)"
"$(Get-Date) - File in use: $($file.FullName)" |
Add-Content -Path $LogPath
}
catch {
Write-Warning "Unexpected error removing file: $_"
"$(Get-Date) - Error: $_ - File: $($file.FullName)" |
Add-Content -Path $LogPath
}
}
}
catch {
Write-Error "Critical error in Remove-OldFiles: $_"
"$(Get-Date) - Critical Error: $_" |
Add-Content -Path $LogPath
throw # Re-throw error to calling script
}
}
Esta implementación:
El manejo adecuado de errores es crucial para que los scripts de PowerShell sean confiables. Al comprender los tipos de errores y utilizar los bloques try/catch de forma eficaz, puede crear scripts que manejen los fallos con elegancia y proporcionen comentarios significativos. Recuerde probar minuciosamente su manejo de errores: ¡su yo futuro se lo agradecerá cuando solucione problemas en producción!
¡Ahora adelante y detecta esos errores! Sólo recuerde: el único error grave es un error no controlado.
Leave a comment