obsługa błędu może przyprawić nieco problemu, dla tego kilka zebranych tips’n’tricks, które mogą się przydać.
try/catch nie działa!
czasem nie działa. a to dla tego, że klauza catch zostanie wykonana dla błędów 'terminujących’. większość błędów natomiast jest 'zwykłymi’ błędami, nieterminującymi. zachowanie można zmienić albo zmieniając standardowe zachowanie poprzez globalnie ustawienie zmiennej
$ErrorActionPreference="stop"
co spowoduje nadpisanie wartości ustawionych dla commandletu.
można też po prostu dodać parametr ’-ea’ dla konkretnego wywołania. przykład. to nie zadziała zgodnie z oczekiwaniami:
try { gwmi -class win32_bios -ComputerName tralla} catch { echo 'TU NIE DZIAŁA'}
napis 'tu nie działa’ nigdy się nie pojawi. poprawiona wersja:
try { gwmi -class win32_bios -ComputerName tralla -ea stop} catch { echo "TU NIE DZIAŁA" }
zapytanie reqrsywne
problem z takim wymuszeniem jest w przypadq zapytań reqrsywnych. scenariusz: chciałbym wyszukać wszystkie katalogi, do których nie mam dostępu. robię reqrsywne przejście po katalogach i ustawiam błąd jako terminujący:
try { ls -Recurse -Directory C:\Windows\ -ea stop|out-null} catch { write-host "TU NIE DZIAŁA!" }
niby działa… ale, błąd jest terminujący. co oznacza, że po pierwszym wystąpieniu zakończy działanie wykonania. zobaczymy tylko pierwszy katalog, do którego nie mamy dostępu.
należy skorzystać z innej metody – przekazania błędów do zmiennej. uzysqje się to poprzez parametr 'ErrorValue’ [alias 'ev’]. wada jest taka, że zmienna zwracana jest po zakończeniu działania. tutaj już nie ma try/catch!
ls -Recurse -Directory C:\Windows\ -ev LISTINGERRORS|out-null foreach($lerr in $LISTINGERRORS) { write-host "TU NIE DZIAŁA!" $lerr }
oczywiście jeśli nie chce się oglądać błędów z przebiegu, to można sobie dodać ’-ea SilentlyContinue’ .
szczegóły błędu
na ekranie nadal będzie śmietnik. fajnie byłoby złapać tylko nazwy – i np. dla nich coś wykonać. dla tego warto zapoznać się z obiektem błędu:
TypeName: System.Management.Automation.ErrorRecord Name MemberType Definition ---- ---------- ---------- Equals Method bool Equals(System.Object obj) GetHashCode Method int GetHashCode() GetObjectData Method void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.... GetType Method type GetType() ToString Method string ToString() CategoryInfo Property System.Management.Automation.ErrorCategoryInfo CategoryInfo {get;} ErrorDetails Property System.Management.Automation.ErrorDetails ErrorDetails {get;set;} Exception Property System.Exception Exception {get;} FullyQualifiedErrorId Property string FullyQualifiedErrorId {get;} InvocationInfo Property System.Management.Automation.InvocationInfo InvocationInfo {get;} PipelineIterationInfo Property System.Collections.ObjectModel.ReadOnlyCollection[int] PipelineIterationInfo {g... ScriptStackTrace Property string ScriptStackTrace {get;} TargetObject Property System.Object TargetObject {get;} PSMessageDetails ScriptProperty System.Object PSMessageDetails {get=& { Set-StrictMode -Version 1; $this.Except...
teraz widać co mamy do dyspozycji. wyjątkowo ciekawe do obsługi będą 'FullyQualifiedErrorId’ pozwalający na zdefiniowanie logiki zależnie od rodzaju błędu, oraz 'TargetObject’ gdzie powinna znaleźć się informacja o obiekcie, który wygenerował błąd. proponuję przetestować, bo tak najlepiej się uczyć:
ls -Recurse -Directory C:\Windows\ -ev LISTINGERRORS -ea SilentlyContinue|out-null foreach($lerr in $LISTINGERRORS) { write-host $lerr.TargetObject }
nie wszystko jest błędem
i na koniec – nie wszystko co my uważamy za błąd, jest błędem. nie mam teraz przygotowanego przykładu, niemniej zdarzało mi się, że commandlet zwracał null lub informował o niepowodzeniu – co było jak najbardziej prawidłowym zachowaniem a nie błędem per se. w takim przypadq trzeba sobie obsłużyć błąd zwykłym if’em bo try/catch tego nie znajdzie.
eN.