Úvod do zpracování chyb v prostředí PowerShell
Mnoho skriptů PowerShellu je navrženo tak, aby se spouštěly bez obsluhy, takže je důležité zajistit, aby mohly hladce zpracovávat chyby. Správné zpracování chyb pomáhá předcházet celé řadě problémů, od nedokončených operací a výpadků systému až po ztrátu dat. Zpracování chyb musí brát v úvahu jak chyby při ukončení, které zastavují provádění skriptu, tak chyby neukončující, které umožňují pokračovat v běhu skriptu, ale mohou ovlivnit výstup.
Jedním z nejužitečnějších mechanismů pro zpracování chyb v PowerShellu je blok Try-Catch-Finally. Blok Try obsahuje kód, který by mohl vyvolat výjimku. Pokud k tomu dojde, řízení se přenese do bloku Catch, který chybu zpracuje provedením akcí, jako je přeskočení problematického souboru nebo neplatného vstupu, a také protokolováním události. Blok Finally se vždy spustí a provede úkoly čištění, jako je zavírání souborů, uvolnění zdrojů nebo protokolování informací. Například vytvoření podrobných chybových zpráv pro Try Catch v PowerShellu vám umožní podniknout příslušné akce k vyřešení problémů.
Tento dokument poskytuje hluboký ponor do bloků Try-Catch-Finally. Během toho se také budeme věnovat některým dalším metodám zpracování chyb v PowerShellu, včetně použití parametru ErrorAction k řízení toho, jak rutiny reagují na chyby, a použití proměnné $Error k ukládání historie chyb.
Jaké jsou chyby v PowerShellu?
Chyby jsou události, které mohou ovlivnit normální průběh provádění skriptu nebo jej mohou úplně zastavit. Mezi běžné příčiny chyb patří nesprávná syntaxe, chybějící soubory, nedostatečná oprávnění nebo nedostupné zdroje. Existují dva primární typy chyb, ukončovací chyby a neukončující chyby.
Ukončovací chyby: Kritické chyby, které zastavují skript
Chyby ukončení okamžitě zastaví provádění skriptu, pokud nejsou správně zpracovány. K těmto chybám dochází, když PowerShell čelí problému, ze kterého se nemůže zotavit, jako je chyba syntaxe, volání proměnné nebo souboru, který neexistuje, nebo nedostatek dostatečných oprávnění k provedení požadované operace.
Skript níže se například pokouší získat soubor users.txt:
Get-Content C:\users.txt | Get-ADUser -Properties name | Select-Object -Property SamAccountName,name
Pokud tento soubor v zadaném umístění neexistuje nebo účet, na kterém je spuštěn skript, nemá oprávnění k přístupu k němu, skript vygeneruje chybu ukončení a zastaví činnost skriptu, jak je znázorněno zde:

Neukončující chyby: Chyby, které umožňují pokračování skriptu
Neukončující chyby v PowerShellu jsou chyby, které nezastaví provádění skriptu. Tyto chyby jsou běžně generovány rutinami, které se pokoušejí zpracovat více objektů, přičemž jedna chyba nemusí nutně zastavit celý skript.
Předpokládejme například, že spustíme skript použitý v předchozím příkladu, ale nyní soubor existuje a máme oprávnění k přístupu k němu. Nemáme však konektivitu k řadiči domény. V tomto případě se spustí první příkaz a získá obsah souboru. Další příkaz však selže a generuje neukončující chyby pro každý ze tří objektů v souboru.

Nyní předpokládejme, že připojení řadiče domény je obnoveno, ale jeden objekt v souboru má neplatné uživatelské jméno. Spuštění skriptu vygeneruje neukončující chybu uvedenou níže pro objekt, ale ostatní objekty budou skriptem zpracovány.

Kde se ukládají chyby: proměnná $Error
$Error je automatické pole, které ukládá nedávné chyby zjištěné během provádění skriptu, díky čemuž je velmi užitečné při ladění a odstraňování problémů se skripty. Nejnovější chyba je uložena v indexu [0].
Když se například následující skript pokusí načíst neexistující soubor, načte výslednou chybu z $Error a zobrazí ji s popisem:
# trying to get a file which doesn't exist, and an error is generated. Get-Item "C:\file44.txt" # Display a message along with the last error. Write-Host "Oops! Something went wrong Here's the error: $($Error[0])"

Pochopení bloků Try, Catch a Finally
Dobrým způsobem, jak hladce zvládnout chyby, je implementovat bloky Try-Catch-Finally.
Zkuste Blokovat: Kde se nachází kód náchylný k chybám
Blok Try obsahuje kód, který může způsobit chybu. Například následující skript je určen k zastavení procesu Poznámkového bloku. Blok Try zastaví tento proces, pokud je aktuálně spuštěn, ale pokud neběží, spuštění přeskočí na blok Catch, čímž zabrání zhroucení skriptu.
try { # Attempt to stop a process that may not be running Stop-Process -Name "Notepad" -ErrorAction Stop # If no error occurs, this line will execute Write-Host "Notepad process stopped successfully." } catch { # Handle the error gracefully Write-Host "Oops! Could not stop the process. Error: $($_.Exception.Message)" }

Catch Block: Zpracování chyb, když nastanou
Blok Catch se používá ke zpracování výjimek, které se vyskytují v bloku Try. V následujícím příkladu PowerShell Try Catch skript vyzve uživatele k zadání čísla, vydělí 10 tímto číslem a vytiskne výsledek. Pokud je však vstupní číslo 0, pokus o dělení bude mít za následek chybu, protože není možné dělit nulou. V takovém případě se blok Catch spustí a místo čísla vytiskne chybovou zprávu.
try { $number = [int](Read-Host "Enter a number") Write-Output "Result: $(10 / $number)" } catch { Write-Output "Error: Invalid input or division by zero!" } finally { Write-Output "Program execution completed." }

Finally blokovat: Kód, který se vždy spustí, ať už dojde k chybě nebo ne
Blok Finally se provede vždy bez ohledu na to, zda byla vyvolána výjimka.
Například následující skript definuje pole pouze se třemi prvky, ale pokusí se vytisknout pátý prvek; tato výjimka je zpracována v bloku Catch, který vypíše chybu. Blok Finally se však také provede, takže se vytiskne další zpráva.
try { # Define an array $numbers = @(1, 2, 3) # Access an invalid index using a .NET method that throws an exception $value = $numbers.GetValue(5) Write-Host "Value: $value" } catch [System.IndexOutOfRangeException] { # Handle the specific exception Write-Host "Error: Index out of range exception caught." } finally { # This block always executes Write-Host "Execution completed." }

Pokročilý příklad: Použití vícenásobných záchytných bloků pro různé typy výjimek
Skript může obsahovat více bloků Catch pro zpracování různých typů výjimek, což umožňuje podrobnější zpracování chyb. Rámec .Net skutečně poskytuje bohatou sadu typů zpráv o výjimkách PowerShell Try Catch , včetně následujících:
- System.DivideByZeroException — Vyskytuje se při pokusu o dělení libovolného čísla nulou.
- System.FormatException — Vzniká při pokusu o převod nečíselného vstupu na číslo.
- System.ArgumentNullException — Vyskytuje se, když jsou předané argumenty null, ale nikdy by neměly být null.
- System.IO.IOException — Vyvolá se, když dojde k chybě I/O.
Například následující skript požádá uživatele o zadání dvou čísel, převede je na celočíselný formát, vydělí první číslo druhým a vytiskne výsledek. První blok Catch řeší možnost, že vstup uživatele nelze převést do celočíselného formátu, a druhý blok Catch řeší možnost pokusu o dělení nulou.
try { # Prompt user for first input $num1 = Read-Host "Enter the first number" $num1 = [int]$num1 # Convert to integer (may cause FormatException) # Prompt user for second input $num2 = Read-Host "Enter the second number" $num2 = [int]$num2 # Convert to integer (may cause FormatException) # Perform division (may cause DivideByZeroException) $result = $num1 / $num2 # Print result Write-Host "Result: $result" } catch [System.FormatException] { # Handling invalid input error Write-Host "Error: Please enter only numeric values!" } catch [System.DivideByZeroException] { # Handling division by zero error Write-Host "Error: Cannot divide by zero!" } catch { # Handling unexpected errors Write-Host "An unexpected error occurred: $_" } finally { # Code that always runs Write-Host "Execution completed." }

Pokročilé techniky zpracování chyb
Chcete-li vytvořit robustnější a udržovatelnější skripty, zvažte použití následujících technik zpracování chyb v kombinaci s bloky PowerShell Try and Catch:
- Použijte $ErrorActionPreference = „Stop“, abyste zajistili, že všechny chyby budou považovány za ukončující, což usnadňuje jejich zachycení.
- Použijte -ErrorAction Stop pro jednotlivé příkazy, abyste umožnili podrobnější ovládání bez ovlivnění celého skriptu.
- Pro přístup k podrobnostem o chybě použijte $_.Exception.Message a $PSItem.
Chytání konkrétních výjimek
Následující skript je určen k převodu vstupu od uživatele na uživatele na celé číslo a poté ke spuštění příkazu. První blok Catch zpracovává možnost neplatného uživatelského vstupu a druhý zpracovává pokusy o provedení neplatného příkazu. Pokud dojde k některé z výjimek, skript vytiskne vlastní zprávu.
try { # Attempt to convert user input to an integer (may throw FormatException) $number = Read-Host "Enter a number" $number = [int]$number # Try running a non-existent command (may throw CommandNotFoundException) NonExistent-Command } catch [System.FormatException] { Write-Host "Error: Invalid input. Please enter a valid number." } catch [System.Management.Automation.CommandNotFoundException] { Write-Host "Error: Command not found. Please check the command name." } catch { Write-Host "An unexpected error occurred: $_" } finally { Write-Host "Execution completed." }

Použití $_.Exception.Message a $PSItem pro přístup k podrobnostem o chybě
Chcete-li získat přístup k podrobnostem o chybě v bloku Catch, můžete použít následující proměnné:
- $_.Exception.Message poskytuje jasnou, uživatelsky přívětivou chybovou zprávu Try Catch PowerShell, která obsahuje pouze relevantní část chyby.
- $PSItem obsahuje úplný chybový objekt, včetně typu chyby, zdroje a trasování zásobníku.
Následující skript ilustruje rozdíl ve výstupu poskytovaném těmito proměnnými:
try { # Attempt to open a non-existent file Get-Content "C:\File1.txt" -ErrorAction Stop } catch { # Using $_.Exception.Message to get detailed error information Write-Host "Error occurred: $($_.Exception.Message)" # Using $PSItem (same as $_) to display full error details Write-Host "Full Error Details: $PSItem" } finally { Write-Host "Execution completed." }

Použití příkazů Trap
Příkaz Trap definuje kód, který se spustí, když dojde ke konkrétní chybě, bez ohledu na to, kde ve skriptu k chybě došlo. V důsledku toho poskytuje mechanismus pro zpracování chyb na vyšší (globální) úrovni než jednotlivé bloky PowerShell Try-Catch.
Následující skript používá Try-Catch v PowerShellu ke zpracování možnosti pokusu o dělení nulou. Příkaz Trap zachytí jakoukoli další chybu, která se může stát, což je v tomto případě použití rutiny Get-Item se souborem, který neexistuje.
# Global error handler using trap trap { Write-Host "Global Error Handler (trap): $_" continue # Allows the script to continue execution } function Test-TryCatch { try { Write-Host "Inside Try Block" 1 / 0 # This will cause a division by zero error Write-Host "This line will not execute due to the error above." } catch { Write-Host "Caught in Try-Catch: $_" } } Write-Host "Before function call" Test-TryCatch Write-Host "After function call" # Triggering another error outside try-catch to see if trap works Write-Host "Triggering an error outside Try-Catch" Get-Item "C:\NonExistentFile.txt" -ErrorAction Stop # Force a terminating error Write-Host "Script completed"

Zpracování neukončujících chyb v PowerShellu
Záchytné bloky jsou navrženy tak, aby zvládly ukončovací chyby; protože neukončující chyby nezastaví provádění skriptu, nespustí blok Catch. Neukončující chyby však můžeme převést na ukončovací chyby pomocí parametru -ErrorAction Stop. Tento parametr nutí skript, aby s neukončující chybou zacházel jako s ukončovacími chybami, což blokům Catch umožní, aby je náležitě zpracovaly.
Předpokládejme, že spustíme následující skript, ale složka, ke které se pokouší přistupovat, neexistuje. Blok Try používá -ErrorAction Stop k převodu této neukončující chyby na ukončující chybu, takže ji bude zpracovávat blok Catch.
$directoryPath = "C:\NonExistentFolder" try { Write-Host "Checking if directory exists..." Get-ChildItem -Path $directoryPath -ErrorAction Stop # Force a terminating error if the directory doesn't exist Write-Host "Directory found." } catch { Write-Host "Error: The directory '$directoryPath' was not found." }

Doporučené postupy pro používání Try-Catch v PowerShellu
Chcete-li maximalizovat srozumitelnost, efektivitu a udržovatelnost kódu, pečlivě vytvořte blok zpracování chyb. Konkrétně PowerShell Try-Catch by se měl používat pouze pro skutečné výjimky. Omezením kódu v bloku Try na pouze ty operace, které by mohly vyvolat výjimku, usnadňuje identifikaci zdroje výjimek a ladění skriptu. V ostatních případech zvažte příkazy if-else nebo switch.
Mezi další klíčové osvědčené postupy patří následující:
- Ujistěte se, že kód uvnitř bloku Try se řídí principem jediné odpovědnosti, což znamená, že každý kus kódu provádí konkrétní úkol.
- Ke zpracování různých typů výjimek použijte samostatné bloky Catch.
- Vždy zahrňte blok Finally, abyste vyčistili kód a uvolnili prostředky.
Následující skript ilustruje některé z těchto osvědčených postupů. Před pokusem o přístup k souboru používá příkaz if s cestou test ke kontrole, zda soubor existuje. Pokud soubor neexistuje, chyba se zobrazí pomocí příkazu else. Blok Try-Catch se používá pouze ke čtení obsahu souboru; pokud skript narazí na chybu, jako je nedostatek oprávnění, blok Catch zpracuje výjimku a zobrazí chybovou zprávu.
# Define the path to the file $filePath = "D:\NonExistentFile.txt" # Check if the file exists if (Test-Path -Path $filePath) { try { # Try to read the file contents $fileContents = Get-Content -Path $filePath -ErrorAction Stop Write-Host "File contents: $fileContents" } catch { # Handle any exceptions that occur while reading the file Write-Host "Error: Unable to read the file. Exception Message: $_" } } else { # If the file does not exist, display an error message Write-Host "Error: The file does not exist." }

PowerShell Try-Catch a pokračování skriptu
Pomocí příkazů Pokračovat
Když skript narazí na chybu v bloku Try-Catch, výchozí chování je, že skript zpracuje výjimku v bloku Catch a zastaví provádění. Existují však scénáře, kdy chceme po zpracování chyby pokračovat v provádění, například při zpracování dávky souborů nebo testování připojení k více serverům.
Chcete-li zajistit, aby jediné selhání nezastavilo celou iteraci, použijte příkaz Continue. Například následující skript testuje připojení více serverů; příkaz Continue zajišťuje, že provádění bude pokračovat i v případě, že na jednom serveru dojde k chybě.
# List of server names (replace with actual server names or IPs) $servers = @("lhehost9", "lhehost11", "lhehost10") foreach ($server in $servers) { try { Write-Host "Attempting to connect to $server..." # Simulating a remote connection (Replace with actual connection command) Test-Connection -ComputerName $server -Count 2 -ErrorAction Stop Write-Host "Successfully connected to $server.`n" } catch { Write-Host "Error: Failed to connect to $server. Skipping to next server...`n" continue # Continue to the next server in the loop } } Write-Host "Script execution completed."

Pomocí proměnné $ErrorActionPreference
Dalším způsobem, jak vynutit pokračování provádění skriptu po chybě, je použití proměnné $ErrorActionPreference . Zatímco bloky Try-Catch umožňují lokalizované zpracování chyb, $ErrorActionPreference je globální nastavení, které řídí reakci PowerShellu na neukončující chyby.
Již jsme viděli, že můžete nastavit $ErrorActionPreference na „Stop“, abyste zajistili, že všechny chyby budou považovány za ukončující, a tím se usnadní jejich zachycení. Tato proměnná má však další nastavení, včetně Continue (výchozí), Silentlycontinue, Inquire a Ignore. Výběrem nastavení pro tuto proměnnou můžete definovat, jak jsou ve skriptu spravovány chyby.
PowerShell Finally blok: Zajištění vyčištění zdrojů
Jak bylo uvedeno dříve, blok Finally se spustí po blocích Try a Catch, bez ohledu na to, zda během provádění skriptu došlo k chybě. Často se používá pro proces čištění, jako je uzavření souborů, ukončení připojení a uvolnění prostředků.
Například následující skript otevře soubor a zapíše do něj „hello world“. Pokud dojde k chybě, blok Catch vytiskne zprávu. V každém případě blok Finally zajistí, aby byl datový proud souborů uzavřen, aby se zabránilo úniku prostředků.
try { $filePath = "D:\Office\Backup\eventviewer_logs.txt" $fileStream = [System.IO.StreamWriter]::new($filePath) $fileStream.WriteLine("Hello, World!") } catch { Write-Host "An error occurred: $_" } finally { if ($fileStream) { $fileStream.Close() Write-Host "File stream closed successfully." } }
Proměnné, které ukládají a počítají chyby
PowerShell poskytuje dvě proměnné, které jsou docela užitečné při auditování a ladění skriptů, které se setkávají s více výjimkami:
- $Error uchovává seznam chyb, ke kterým došlo během relace.
- $Error.count poskytuje celkový počet chyb zaznamenaných v aktuální relaci.
Pro přesné sledování může být nutné čas od času během relace vymazat seznam chyb. Použití $Error.clear() odstraní všechny uložené chyby.
Použití vnořených bloků Try-Catch ke zpracování více chyb
Následující skript obsahuje dva vnořené bloky Try-Catch pro zpracování dvou různých potenciálních chyb, při pokusu o dělení nulou a při pokusu o přístup k neexistujícímu souboru:
try { Write-Host "Starting First try block..." try { Write-Host "Attempting to divide by zero..." $result = 1 / 0 # This will cause a divide-by-zero exception } catch [System.DivideByZeroException] { Write-Host "Inner catch: Division by zero error occurred" throw "Re-throwing error to outer try block" } try { Write-Host "Accessing a file that doesn't exist..." Get-Content -Path "C:\File1.txt" -ErrorAction Stop } catch [System.IO.FileNotFoundException] { Write-Host "Inner catch: File not found error occurred" } } catch { Write-Host "Outer catch: Handling an error from inner blocks - $($_.Exception.Message)" } finally { Write-Host "Executing final block for cleanup..." }

Chybové zprávy a ladění v PowerShellu
Přístup k podrobným informacím o chybě
Když se během provádění skriptu vyskytnou chyby, nestačí vědět, že k chybě došlo; potřebujeme podrobné informace k diagnostice a odstranění problému. Jak již bylo uvedeno dříve, proměnná $Error uchovává pole chybových záznamů s nejnovější chybou $Error[0] . Mezi specifické chybové vlastnosti patří Exception, CategoryInfo, InvocationInfo a ScriptStackTrace.
Vlastnost ScriptStackTrace je neocenitelná, protože poskytuje podrobný zásobník volání, který ukazuje sekvenci volání funkcí a umístění skriptů, které vedly k chybě. Chcete-li získat přístup k těmto podrobným informacím o nejnovější chybě, použijte $Error[0].ScriptStackTrace.
Poskytování vlastních chybových zpráv
Zobrazování smysluplných a uživatelsky přívětivých chybových zpráv zlepšuje použitelnost a ladění skriptů. Vlastní zprávy mohou uživatelům poskytnout přehled o tom, co se pokazilo, a někdy jim pomohou chybu opravit, například tím, že opraví nesprávnou cestu k souboru, nesprávný název procesu nebo nedostatečná oprávnění.
Pro zobrazení vlastních zpráv můžeme použít Write-host nebo write-error v bloku Catch. Chcete-li poskytnout další podrobnosti, zahrňte zprávu $_.Exception.message.
Pokud například dojde k chybě (dělení nulou) během provádění následujícího skriptu, podrobné upozornění se vytiskne na konzoli a odešle se e-mailem správci:
$SMTPServer = "lvkex.ca.lo" $SMTPPort = 587 $From = "aadministrator@ca.lo" $To = "administrator@ca.lo" $Subject = "PowerShell Script Error Alert" try { Write-Host "Executing script..." # Simulate an error (divide by zero) $result = 1 / 0 } catch { # Capture actual error message details $ErrorMessage = @" An error occurred in the PowerShell script. Message: $($_.Exception.Message) Script: $($MyInvocation.ScriptName) Line: $($_.InvocationInfo.ScriptLineNumber) Date: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss") "@ # Log error to console and send email Write-Host "An error occurred. Sending email notification..." Send-MailMessage -SmtpServer $SMTPServer -Port $SMTPPort -From $From -To $To -Subject $Subject -Body $ErrorMessage } }
Běžné případy použití pro PowerShell Try-Catch
Hromadná aktualizace uživatelských profilů při zpracování chyb při zadávání dat
Správci často používají PowerShell k automatické aktualizaci uživatelských informací ve službě Active Directory na základě souboru csv. Protože vstupní soubor může obsahovat neplatná data, je důležité používat Try-Catch ke zpracování a protokolování chyb.
Následující skript importuje soubor csv obsahující seznam uživatelů a iteruje jej, aby aktualizoval jejich popisy v AD. Vytiskne výsledky na konzole a zaznamená všechny chyby do souboru protokolu.
# Define file paths $CsvFile = "C:\Members (7).csv" $LogFile = "C:\logs.txt" # Import CSV $Users = Import-Csv -Path $CsvFile foreach ($User in $Users) { try { # Attempt to find the user in AD using CN or SamAccountName $ADUser = Get-ADUser -LDAPFilter "(cn=$($User.cn))" -Properties SamAccountName, Description -ErrorAction SilentlyContinue if ($ADUser -eq $null) { # Log missing users $ErrorMessage = "$(Get-Date): User '$($User.cn)' not found in AD." Write-Host $ErrorMessage Add-Content -Path $LogFile -Value $ErrorMessage continue # Skip to the next user } # Define new description $NewDescription = "Test update on $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" # Update AD user description Set-ADUser -Identity $ADUser.SamAccountName -Description $NewDescription -ErrorAction Stop # Log success Write-Host "Updated: $($ADUser.SamAccountName) - New Description: $NewDescription" } catch { # Capture and log errors $ErrorMessage = "$(Get-Date): Error updating $($User.cn) ($($ADUser.SamAccountName)) - $($_.Exception.Message)" Write-Host $ErrorMessage Add-Content -Path $LogFile -Value $ErrorMessage } }

Řešení problémů se serverem nebo síťovým připojením
Bloky Try-Catch jsou také užitečné pro řešení problémů se serverem a síťovým připojením. Následující skript například ukazuje, jak otestovat připojení k serveru, jednou se správným názvem a jednou s nesprávným názvem:
# Define server details $Server = "ada1.adatum.local" $LogFile = "C:\log.txt" try { # Check if the server responds to a ping $PingTest = Test-NetConnection -ComputerName $Server if (-not $PingTest.PingSucceeded) { throw "Server $Server is not responding to ping." } # Check RDP connectivity (port 3389) $RDPTest = Test-NetConnection -ComputerName $Server -Port 3389 if (-not $RDPTest.TcpTestSucceeded) { throw "Server $Server is reachable but RDP port 3389 is not open." } # If both checks pass Write-Host "Server $Server is reachable, and RDP is accessible." } catch { # Capture and log the error $ErrorMessage = "$(Get-Date): Error connecting to server $Server - $($_.Exception.Message)" Write-Host $ErrorMessage Add-Content -Path $LogFile -Value $ErrorMessage }

Závěr
Bloky Try-Catch-Finally jsou neocenitelným nástrojem pro řešení chyb ve skriptech PowerShellu. Jejich kombinací s mechanismy, jako je parametr ErrorAction a proměnné $ErrorActionPreference a $Error, můžete vytvářet robustní skripty, které běží hladce, i když nastanou neočekávané problémy. Díky tomu můžete automatizovat úkoly s jistotou, že nebudete trpět problémy, jako je neúplný provoz, výpadky systému nebo ztráta dat.
Pro více in formací nás neváhejte kontaktovat.
Zdroj: Netwrix