שלטון על טיפול בשגיאות PowerShell: מדריך ללא שטויות

האם נמאס לכם לראות את הודעות השגיאה האדומות המטרידות בסקריפטים של PowerShell שלכם? למרות שהן עשויות להיראות מאיימות, טיפול נכון בשגיאות הוא חיוני לבניית אוטומציה מהימנה ב-PowerShell. במדריך הזה, תלמדו איך ליישם טיפול חזק בשגיאות בסקריפטים שלכם – מהבנת סוגי השגיאות ועד לשליטה בבלוקי try/catch.

דרישות מקדימות

מדריך זה מניח שיש לכם:

  • Windows PowerShell 5.1 או PowerShell 7+ מותקן
  • היכרות בסיסית עם סקריפטינג ב-PowerShell
  • נכונות לקבל את השגיאות כהזדמנויות למידה!

הבנת סוגי שגיאות ב-PowerShell

לפני שקופצים לטיפול בשגיאות, אתם צריכים להבין את שני סוגי השגיאות העיקריות ש-PowerShell יכול להשליך:

שגיאות מחייבות

אלו הן השגיאות הרציניות – שגיאות שעוצרות לחלוטין את הרצת הסקריפט. תיתקלו בשגיאות מחייבות כאשר:

  • הסקריפט שלכם מכיל שגיאות תחביר שמונעות ממנו להתפרש
  • חריגות לא מטופלות מתרחשות בקריאות שיטה של .NET
  • אתם מציינים במפורש ErrorAction Stop
  • שגיאות קריטיות בזמן ריצה מקשות על המשך הפעולה

שגיאות לא מחייבות

אלו הן שגיאות תפעוליות שכיחות יותר שאינן עוצרות את הסקריפט שלכם:

  • שגיאות קובץ לא נמצא
  • מצבים של סירוב הרשאות
  • בעיות חיבור רשת
  • ערכי פרמטרים לא תקינים

פרמטר ErrorAction: קו ההגנה הראשון שלכם

בואו נתחיל עם דוגמה מעשית. הנה סקריפט שמנסה להסיר קבצים שיש להם יותר ימים מסוימים:

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)]."
}

כברירת מחדל, Remove-Item גורם לשגיאות שאינן מסיימות. כדי להפוך אותו לגורמת שגיאות מסיימות שניתן לתפוס, הוסיפו -ErrorAction Stop:

Remove-Item -Path $file.FullName -ErrorAction Stop

בלוקי Try/Catch: הסכין השוויצרית של טיפול בשגיאות

עכשיו בואו נעטוף את ההסרת קבצים שלנו בבלוק של 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)"
    }
}

הבלוק של try מכיל קוד שעשוי לגרום לשגיאה. אם קורה שגיאה, הביצוע קופץ לבלוק הcatch (אם זו שגיאה מסיימת) שבו תוכלו:

  • לרשום את השגיאה
  • לקחת פעולה תיקונית
  • להודיע למנהלים
  • להמשיך בביצוע הסקריפט בצורה חלקה

עבודה עם $Error: כלי חקירת השגיאות שלך

PowerShell שומר ערימת אובייקטי שגיאה במשתנה $Error האוטומטי. תיחשבו על $Error כעל "רקולודר קופץ" של PowerShell – זה מעקב אחר כל שגיאה שקורה במהלך הסשן שלכם ב-PowerShell, וזה מצוין לצורך פתרון בעיות וניפוי שגיאות.

הנה מתי ולמה אתם עשויים לרצות להשתמש ב-$Error:

  1. טיפול בשגיאות עבר: גם אם פספסתם לראות הודעת שגיאה אדומה, $Error שומר תולדות:

    # צפיה בפרטי שגיאה האחרונים
    $Error[0] | Format-List * -Force
    
    # בדיקה של חמישה שגיאות אחרונות
    $Error[0..4] | Select-Object CategoryInfo, Exception
    
    # חיפוש לפי סוגי שגיאות ספציפיים
    $Error | Where-Object { $_.Exception -is [System.UnauthorizedAccessException] }
    
  2. ניפוי בעיות בסקריפטים: השתמש ב $Error כדי להבין מה השתבש ואיפה:

    # קבל את מספר השורה המדויק והסקריפט שבו קרתה השגיאה
    $Error[0].InvocationInfo | Select-Object ScriptName, ScriptLineNumber, Line
    
    # ראה את כל ערימת השגיאות
    $Error[0].Exception.StackTrace
    
  3. שיחזור ודיווח על שגיאות: מושלם ליצירת דוחות שגיאה מפורטים:

    # צור דוח שגיאה
    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
    }
    

    }

  4. ניהול מושבים: נקה שגיאות או בדוק את מצב השגיאות:

    # נקה את היסטוריית השגיאות (שימושי בתחילת הסקריפטים)
    $Error.Clear()
    
    # ספר את הסך הכולל של השגיאות (טוב לבדוק את סף השגיאות)
    if ($Error.Count -gt 10) {
        Write-Warning "נמצא מספר גבוה של שגיאות: $($Error.Count) שגיאות"
    }
    

דוגמה מהחיים האמיתיים המשלבת את המושגים האלה:

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
        }
    }
}

טיפים מקצועיים:

  • $Error נשמר לכל סשן של PowerShell
  • יש נפח ברירת מחדל של 256 שגיאות (נשלט על ידי $MaximumErrorCount)
  • זהו מערך בגודל קבוע – שגיאות חדשות מניחות שגיאות ישנות כאשר המערך מלא
  • תמיד לבדוק תחילה $Error[0] – זו השגיאה האחרונה ביותר
  • שקול לנקות $Error בהתחלת סקריפטים חשובים למעקב שגיאות נקי

בלוקי תפיסה מרובים: טיפול בשגיאות ממוקד

כמו שלא תשתמש באותה כלי לכל עבודה בבית, אין לך לטפל בכל שגיאת PowerShell באותו אופן. בלוקי תפיסה מרובים מאפשרים לך להגיב באופן שונה לסוגי שגיאות שונים.

הנה איך זה עובד:

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 $_
}

סוגי שגיאות נפוצים שתפגש:

  • [System.UnauthorizedAccessException] – הרשאה נדחתה
  • [System.IO.IOException] – קובץ נעול/בשימוש
  • [System.Management.Automation.ItemNotFoundException] – קובץ/נתיב לא נמצא
  • [System.ArgumentException] – ארגומנט לא חוקי
  • [System.Net.WebException] – בעיות רשת/אינטרנט

הנה דוגמה מהחיים האמיתיים שמממשת זאת:

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
    }
}

טיפים מקצועיים לבלוקי תפיסה מרובים:

  1. הסדר חשוב – לשים יוצא מן הכלל יותר ספציפיים תחילה
  2. השתמש בפונקציות מותאמות אישית כדי לטפל בכל סוג של שגיאה באופן עקבי
  3. שקול בלוגיקת ניסיון מחדש עבור שגיאות חולפות
  4. רשום סוגי שגיאות שונים במקומות שונים
  5. השתמש בסוג החריג המדויק ביותר באפשרות
  6. בדוק כל בלוק catch על ידי גרימת כל סוג שגיאה בכוונה

שימוש בבלוקי Finally: ניקוי אחר עצמך

הבלוק finally הוא צוות הניקוי שלך – הוא מבצע תמיד, בין אם יש שגיאה או לא. זה מהווה את השלמות ל:

  • סגירת ידיות קבצים
  • ניתוק מבסיסי נתונים
  • שחרור משאבי מערכת
  • שחזור הגדרות מקוריות

הנה דוגמה מעשית:

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"
    }
}

חשוב לחשוב על finally כמנהיגות של קאמפר אחראי: "תמיד נקה את המחנה שלך לפני שעוזב, לא משנה מה קרה במהלך הטיול."

שיטות מובילות לטיפול בשגיאות

  1. היי בהתאמה עם פעולות שגיאה
    במקום ErrorAction Stop כללי, השתמש בו באופן בררני על פקודות בהן יש צורך לתפוס שגיאות.

  2. השתמש במשתנים שגיאה

    Remove-Item $path -ErrorVariable removeError
    if ($removeError) {
        Write-Warning "Failed to remove item: $($removeError[0].Exception.Message)"
    }
    
  3. רשומה שגיאות באופן הולם

    • השתמש ב-Write-Warning עבור שגיאות שניתן לשחזר
    • השתמש ב-Write-Error עבור בעיות קשות
    • שקול לכתוב ליומן אירועים של Windows עבור כשלים קריטיים
  4. ניקוי משאבים
    תמיד השתמש בבלוקים finally כדי לנקות משאבים כגון ידיות קבצים וחיבורים לרשת.

  5. בדיקת טיפול בשגיאות
    גרום במכוון לשגיאות כדי לוודא שטיפול השגיאות שלך עובד כצפוי.

שלב את הכל יחד

כאן דוגמה מלאה המשלבת את שיטות העבודה הטובות הללו:

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
    }
}

המימוש הזה:

  • מאשר פרמטרי קלט
  • משתמש בבלוקים catch ספציפיים לשגיאות נפוצות
  • רשומה גם הצלחות וכשלים
  • מספק פלט מפורט לתיקון תקלות
  • מזרז כשלים קריטיים לתסריט הקורא

מסכים

ניהול שגיאות נכון הוא קריטי לסקריפטים אמינים של PowerShell. על ידי הבנת סוגי השגיאות ושימוש יעיל בבלוקים של try/catch, תוכלו לבנות סקריפטים שמטפלים בכישלונות בצורה חלקה ומספקים משוב משמעותי. זכרו לבדוק את ניהול השגיאות שלכם ביסודיות – העתיד שלכם יודה לכם כאשר תתמודדו עם בעיות בסביבת הייצור!

עכשיו צאו ותפסו את השגיאות האלה! רק זכרו – השגיאה הרעה היחידה היא שגיאה שאינה מטופלת.

Source:
https://adamtheautomator.com/powershell-error-handling/