Zachwycony językiem skryptowym Windows PowerShell. Swoją wiedzę, doświadczenia i spostrzeżenia opisuję na blogu.

3 zalety stosowania PowerShell Splatting + skrypt który Ci w tym pomoże

Oczywistym celem każdego skryptu jest działanie zgodnie z założeniami. Często chęć skończenia skryptu jest tak duża, że zapominam o czytelności kodu lub komentarzach. Mówię wtedy, „dodam później” lub „napiszę to lepiej potem”. Potraficie sobie wyobrazić jak te „później” wygląda?

Continue Reading

By

Read More

Formatowanie zapytań SQL w PowerShell – skrypt dla Ciebie

Jeśli nie słyszałeś o sp_WhoIsActive, skryptach Ola Hallengren lub Glenn Berry, aplikacji SQLQueryStress bądź SentryOne Plan Explorer to najwyższy czas to zmienić. Wszystko to znajdziesz tutaj. Brant Ozar przygotował świetne podsumowanie z użytecznymi narzędziami, skryptami, odnośnikami do książek, strony www i blogów.

Continue Reading

By

Read More

7 powodów wykorzystywania PowerShell do złośliwych ataków

Kilka miesięcy temu rosyjski PIR Bank stracił okrągły milion dolarów. Swój udział miały w tym skrypty napisane w PowerShell. Co prawda najsłabszym punktem okazał się router z nieaktualnym oprogramowanie, który posłużył do zainfekowania sieci banku. To jednak dalsze działania post-exploitowe hakerzy wykonali za pomocą skryptów PowerShell unikając szybkiego wykrycia.

Kilka miesięcy temu rosyjski PIR Bank stracił okrągły milion dolarów. Swój udział miały w tym skrypty napisane w PowerShell. Co prawda najsłabszym punktem okazał się router z nieaktualnym oprogramowanie, który posłużył do zainfekowania sieci banku. To jednak dalsze działania post-exploitowe hakerzy wykonali za pomocą skryptów PowerShell unikając szybkiego wykrycia.

Continue Reading

By

Read More

5 sposobów na obejście zasady ograniczonego wykonywania skryptów w PowerShell

Zasady ograniczonego wykonywania skryptów jest jedną z pierwszych zmienianych ustawień związanych z PowerShell. Nie ma, co się dziwić. Bardzo szybko pojedyncze polecenia pisane w konsoli zaczynamy zamykać w skrypty w celu ich wielokrotnego wykonywania. Na stacjach klienckich domyślnie jest to zabronione, nie wykonamy plików ps1 czy psm1.

Continue Reading

By

Read More

Wszystko, co powinieneś wiedzieć o ciągach znaków w Powershell

Jeśli miałbym wytypować, co najczęściej używam w swoich skryptach to postawiłbym na ciągi znaków, stąd przegląd niuansów klasy System.String dostępnej w PowerShell.

Zaczynamy.

Deklarowanie typu zmiennej

PowerShell nie wymaga rzutowania typu zmiennej, pierwsze zastosowanie zmiennej określa jego typ. Tak, więc $messege będzie typu String, tak samo jakbyśmy jawnie rzutowali typ zmiennej w sposób [string]$message

Inaczej wygląda sytuacja dla tablic znakowych. W jednym i drugim przypadku zmienne dziedziczą z System.Array jednak dla jednej właściwość Name wskazuje Object[] a dla drugiej String[].

Niesie to za sobą konsekwencje przy późniejszym dodawaniu kolejnych elementów do tabilicy. String[] dokonuję niejawnej konwersji do typu System.String, gwarantując, że wszystkie elementy tej tablicy są dokładnie tego typu.

Pojedynczy cudzysłów

Zawarcie ciągu znaków w pojedynczych cudzysłowach powoduję przekazanie do zmiennej dokładnie tak, jak zostało to wpisane.

Nie nastąpi podmiana wartości zmiennych, należy wtedy stosować podwójny cudzysłów albo łączenie ciągów tzw. konkatenacja.

Podwójny cudzysłów

Po zawarciu łańcucha w podwójny cudzysłów (ciąg znaków podwójnie cytowany), nazwy zmiennych poprzedzone znakiem dolara $ są zastępowane wartością zmiennej przed przekazaniem łańcucha do polecenia przetwarzania.

Łączenie ciągów (konkatenacja)

Znakiem łączącym ciągi jest + zarówno dla pojedynczego i podwójnego cudzysłowu.

Mając jednak na uwadze właściwość podmieniania zmiennych dla podwójnego cudzysłowu prościej i szybciej napisać.

Mała trudność o której musisz pamiętać występuje, gdy chcesz wyświetlić w ciągu znaków wartość właściwości.

Przy takim zapisie nazwa właściwości Version jest traktowana tylko jako ciąg znaków. Natomiast przy podmianie zmiennej następuję niejawna konwersja do ciągu znaków, w rezultacie zwracany jest typ obiektu (choć nie zawsze). To tak jakby zostało wywołane [string]$host.

Do poprawnego wyświetlenia wartości w ciągu znakowym należy zastosować $().

Taki zapis umożliwia również wykonywanie operacji zawartych w ciągu.

Metody złożonego formatowania

Metodę tą również możesz wykorzystać jako sposób na łączenie ciągów. Polega ona na umieszczaniu, indeksowych znacznikami takich jak {0}, {1}, {n+1} które następnie zastępowane są odpowiednimi obiektami z listy.

Istnieją dwa sposoby obsługi takiego formatowania. Użyciu statycznej metody Format() klasy System.String lub skorzystanie z operatora formatu -f.

Jako listę obiektów możemy również wykorzystywać zdefiniowaną wcześniej tablicę.

Cudzysłów, jako znak ciągu

Niekiedy trzeba umieścić znaki cudzysłowu w ciągu, aby sobie z tym poradzić mamy kilka możliwości. Pierwszy sposób to zdublowanie cudzysłowu w wymaganym miejscu.

Bez problemów można zawrzeć pojedynczy cudzysłów w ciągu określonym podwójnymi cudzysłowami i odwrotnie.

Ostatnim sposobem jest umieszczenie ciągu w @""@, co daje sporą swobodę w stosowaniu cudzysłowów, podmieniania zmiennych oraz pisaniu ciągów wielowierszowych.

Ciąg wielowierszowy (nowa linia)

Istnieje kilka sposobów, jednym z nich jest wykorzystanie @" "@ o który wykorzystałem wyżej. Daje najwięcej elastyczności i jest najczęściej wykorzystywany.

Możesz użyć znaków specjalnych np. nowej linii aby „złamać” swoją treść.

Lista wszystkich znaków specjalnych:

CharacterDescription
`0Null
`aAlert (Beep)
`bBackspace
`eEscape
`fForm feed
`nNew line
`rCarriage return
`tHorizontal tab
`u{x}Unicode escape sequence
`vVertical tab
--%Stop parsing

Działania na ciągach

Obiekty klasy System.String dostarczają nam pewny zbiór metod które umożliwiają nam typowe przekszałcenia  i działania na ciągach.

Wielkość znaków

Metoda ToUpper() umożliwia przekonwertowanie wszystkich znaków w ciągu na ich wielkie odpowiedniki, natomiast ToLower() odwrotnie, zamienia wszystkie znaki na małe.

Usuwanie

Remove() zwraca nowy ciąg znaków, w który zostaje usunięta określona liczba znaków, rozpoczynając od określonej pozycji. Brak podania liczby znaków powoduję usunięcie wszystkiech znaków od określonej pozycji.

Trim(), TrimEnd(), TrimStart()użyteczne przy okazji usuwania białych znaków lub pojedyńczych niechcianych znaków.

Metoda Substring() co, prawda nie służy tyle do usuwania co wyodrębniania konkretnego elementu ciągu, jednak w ten sposób pozbywamy się zbędnego łańcucha znaków.

Jako parametry musimy określić pozycje, opcjonalnie również długość ciągu, którą ma zostać zwrócona.

Dzielenie ciągu

Metoda Split() klasy System.String służy do dzielenia łańcucha znaków na tablicę. Najprostsze zastosowanie tej metody to podanie separatora dzielenia.

Oprócz metody do dzielenia można posłużyć się dostępnym w PowerShell operatorem -split.

Z znacznych różnic pomiędzy metodą a operatorem jest taka, że dla metody możliwe jest określenie tablicy separatorów.

Natomiast dla operatora -split w składni możliwe jest podanie bloku skryptu {<ScriptBlock>}czego nie zrobimy stosując metodę.

Metoda Split() oprócz parametru separatora przyjmuję inne parametry, dokładniejsze omówienie wraz z przykładamy pod tym linkiem. Również operator -split posiada swoje niuanse, które zostały dobrze opisane w dokumentacji.

Zastępowanie znaków (Replace() vs -replace)

Do zastępowania znaków możesz wykorzystać metodę Replace() klasy System.String. Metoda ta przyjmuje dwa parametry, starą wartość (znak lub ciąg) oraz nową wartość

Istnieje również operator -replace, który może wydawać się tylko innym sposobem wywołania ww. metody Replace().

Jednak operator umożliwia korzystanie z wyrażeń regularnych czego nie oferuję metoda Replace()

Alternatywą dla operatora -replace jest skorzystanie z statycznej metody Replace() klasy Regex. Pod tym względem dostarczają identyczną funkcjonalność.

Podsumowanie

Wpis zawiera całą moją wiedze (na dany moment) w tym konkretnym temacie. Jestem pewien, że jeszcze sporo tu brakuję, dlatego do wpisu będę starał się dodawać nowe rzeczy i liczę, że mi w tym pomożesz.

Jeśli jest coś co warto tu dodać, to daj znać w komentarzu lub pisz na maila.

By

Read More

💣Obsługa błędów w PowerShell – Try-Catch-Finally i jego niuanse

Każdy chcę aby jego kod wykonywał się zawszę poprawnie, zawszę bezbłędnie. Niestety prawdopodobieństwo, że tak nie będzie rośnie wraz ze złożonością skryptu. Dlatego przewidujemy miejsc narażone na wystąpienie błędów i myślimy od razu o ich obsłudze. A jeśli obsługa błędów to najczęściej blok Try-Catch-Finally.

Teraz pytanie do Ciebie. Zdarzyło Ci się kiedyś napisać kod, który mimo błędów nie został przechwycony przez Catch? Jeśli tak to, postaram Ci się wyjaśnić, dlaczego tak się stało, jak to naprawić i jaka jest alternatywa dla Try-Catch-Finally.

Zacznijmy jednak od zaszłości.

Trap

Windows PowerShell 1.0 umożliwiał obsługę błędów (i nadal umożliwia) za pomocą instrukcji trap. Słowo kluczowe Trap określa listę instrukcji do uruchomienia w przypadku wystąpienia błędu zakończenia. Polecenie mało znane i rzadko spotykane.

Try-Catch-Finally

Od Microsoft PowerShell 2.0 wprowadzono znaną już chociażby z języka C# konstrukcję Try-Catch-Finally.

Blok Try to sekcja kodu, która monitoruje błędy. To tutaj wykonywany jest kod i jeśli w tym czasie wystąpi błąd powodujący zamknięcie programu, następuję jego przechwycenie przez najbliższy blok Catch. Te dwa bloki są ze sobą powiązane i muszą występować razem.

Ostatni blok (nieobowiązkowy) to Finally, w którym instrukcje zostaną wykonane niezależnie od rezultatów poprzednich bloków.

W opisie konstrukcji Try-Catch-Finally padły kluczowe słowa, powodujące zamknięcie programu i to warto zapamiętać.

Warto też, wiedzieć, że Microsoft PowerShell rozróżnia dwa typy błędów, powodujące niepowodujące zamknięcie programu. Z angielskiego terminating i non-terminating errors.

Terminating i non-terminating errors

Czym się różnią? Już odpowiadam.

Błędy powodujące (terminating errors) zakończenie skryptu to takie, które nie są w żaden sposób przechwycone i nie ma możliwości obsługi wyjątku np. wewnątrz funkcji. Nie ma innej możliwości, tylko całkowite zatrzymanie wykonywania skryptu i zwrócenie czerwonego błędu.

Chyba że użyjemy Try-Catch-Finally i obsłużymy błąd.

Natomiast błędy niepowodujące (non-terminating errors) zakończenia pracy PowerShell to takie, które potrafią być obsłużone bezpośrednio przez funkcję. Zostają wyświetlone  na ekran oraz przekazane do specjalnej zmiennej tablicowej $Error

Jednak co najważniejsze, błędy tego typu nie zostaną obsłużone przez instrukcje Try-Catch-Finally. Blok Catch jest po prostu pomijany.

Jak widać, polecenie Get-Item spowodowało błąd, mimo to kontynuowało działanie, pomijając przy tym blok Catch.

Jak obługiwać błędy niepowodujące zakończenia?

Można to robić na kilka sposobów:

  • Try-Catch-Finally oraz $ErrorActionPreference ustawione na Stop (dla całego skrypty)
  • Try-Catch-Finally oraz -ErrorAction ustawione na Stop na poziomie cmdletu, funkcji – rozwiązanie zalecane

W jednym i drugim przypadku program został całkowicie przerwany, mimo że ścieżka do drugiego folderu była poprawna i wykonywanie teoretycznie mogło być kontynuowane.

  • bez Try-Catch-Finally z zmienną $ErrorActionPreference ustawioną na SilentlyContinue
  • bez Try-Catch-Finally za to z -ErrorAction ustawionym na SilentlyContinue

W tych przypadkach kod kontynuuje wykonywanie, ale również zwraca w przyjazny sposób komunikat o błędzie.

Podsumowanie

Po pierwsze zrozumienie i pamiętanie o różnicach pomiędzy terminating i non-terminating errors bardzo pomaga w późniejszym radzeniu sobie z nimi.

Po drugie, podejście do obsługi błędów w PowerShell z pozoru jest łatwe, gdy pomyślimy o instrukcji Try-Catch-Finally. Jednak ze względów na swoje niuanse nieraz dobrze rozważyć alternatywy. Dużo mniej problematyczne może okazać się wyciszenia błędów, kontynuowania i sprawdzania zmiennej $?.

Jednak, gdy skrypt musimy przerwać przy najmniejszym błędzie, dobrą alternatywą będzie skorzystanie z konstrukcji Try-Catch-Finally razem z zmienną globalną $ErrorActionPreference ustawioną na Stop.

Reasumując, nie ma jednego właściwego podejścia. Wszystko zależy potrzeby konkretnego skryptu.


Na końcu kilka ważnych zagadnień, których nie wyjaśniłem wcześniej, a są ważne w kontekście obsługi błędów.

  • $_ – zmienna nie do końca związana z obsługą błędów. Jednak przy wykorzystaniu Try-Catch-Finally, błędów można szukać właśnie tutaj
  • $? – zawiera stan wykonania ostatniej operacji. $true oznacza, że operacja zakończyła się pomyślnie bez żadnych błędów. $false wskazuje całkowitą niepowodzenie lub częściowy sukces. Uwaga: dla pliku wykonywalnego systemu Windows sprawdzany jest kod wyjścia. Kod wyjścia 0 będzie interpretowany jako sukces i niezerowy jako błąd. Niektóre aplikacje konsoli Windows nie respektują tej konwencji, więc zwykle lepiej jest skontrolować $LASTEXITCODE, aby określić wynik działania.
  • $LASTEXITCODE – kod wyjścia ostatniego pliku wykonywalnego systemu Windows, wywołanego w sesji.
  • $Error – tablica błędów, które wystąpiły w bieżącej sesji. Błędy są zawsze wstawiane na początku tablicy. W rezultacie ostatni błąd zawsze znajduje się w indeksie 0 – $Error[0].
  • $MaximumErrorCount – określa rozmiar tablicy $Error. Domyślnie to 256, wartość maks.
  • $ErrorActionPreference – zmienna globalna, wpływa na zachowanie błędów niepowodujących przerwanie programu. Domyślnym ustawieniem jest Continue, który dodaje wpis do kolekcji $Error i wyświetla błąd na konsoli hosta.
  • -ErrorAction – wspólny parametr dostępny dla większości funkcji (Common Parameters) które określa jak polecenie reaguję na błąd niepowodujący zakończenie programu. Parametr ten zastępuję wartość zmiennej $ErrorActionPreference dla bieżącego polecenia.

Tabela z wartościami jakie przyjmuję parametr -ErrorAction oraz $ErrorActionPreference i ich wypływ na działanie programu:

  Message Error Stop ErrorAction ErrorActionPreference  
SilentlyContinue No Yes No * *  
Stop Yes Yes Yes * *  
Continue Yes Yes No *Default *Default Pyta, czy chcesz kontynuować.
Inquire Yes Yes No * *  
Ignore No No No *    
Suspend       * * Używane przy Workflow

By

Read More

Sposób na usuwanie polskich znaków – PowerShell

Potrzebowałem skryptu który będzie usuwać wszystkie polskie znaki z danego ciągu, po dłuższej chwili… stworzyłem coś takiego:

$Polish = ą, ć, ę, ł, ń, ó, ś, ż, ź, Ą, Ć, Ę, Ł, Ń, Ó, Ś, Ż, Ź
$English = a, c, e, l, n, o, s, z, z, A, C, E, L, N, O, S, Z, Z

$ciag = (Chrząszcz brzmi w trzcinie w Szczebrzeszynie, W szczękach chrząszcza trzeszczy miąższ, 
Czcza szczypawka czka w Szczecinie, Chrząszcza szczudłem przechrzcił wąż, 
Strząsa skrzydła z dżdżu, A trzmiel w puszczy, tuż przy Pszczynie, Straszny wszczyna szum...)
foreach($litera in $ciag.toCharArray())
{
    for($i=0; $i -lt 19; $i++)
    {
        if($litera.ToString().Equals($Polish[$i]))
        {
         $ciag=$ciag.Replace($Polish[$i],$English[$i])
        }
    }
} $ciag

Zastanawiam się czy idzie to napisać prościej, lepiej? Jakieś pomysły, propozycje?

Na pozbycie się polskich znaków jest prostszy w zapisie  i przede wszystkich szybszy sposób. Liczby mówią wszystko, tak na szybko porównanie mojego skryptu z skryptem napisanym w komentarzu przez Kacpra za co wielkie dzięki 🙂

function Remove-DiactricChars1{
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline=$true)]
[string]$ciag
)
begin
    {
    $Polish = ą, ć, ę, ł, ń, ó, ś, ż, ź, Ą, Ć, Ę, Ł, Ń, Ó, Ś, Ż, Ź
    $English = a, c, e, l, n, o, s, z, z, A, C, E, L, N, O, S, Z, Z
    $Elapsed = [System.Diagnostics.Stopwatch]::StartNew()
    }
process
{
    foreach($litera in $ciag.toCharArray())
    {
        for($i=0; $i -lt 19; $i++)
        {
            if($litera.ToString().Equals($Polish[$i]))
            {
             $ciag=$ciag.Replace($Polish[$i],$English[$i])
            }
        }
    }
}
end
    {
    $ciag
    Write-Host ?Calkowity czas: $($Elapsed.Elapsed.ToString())?
    }
}
Trzy próby dały wynik:
  • Całkowity czas: 00:00:00.2308620
  • Całkowity czas: 00:00:00.2129062
  • Całkowity czas: 00:00:00.2629116
function Remove-DiactricChars{
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline=$true)]
[string]$text
)
begin
    {
    $Elapsed = [System.Diagnostics.Stopwatch]::StartNew()
    $chars = @{?ą?=?a?;?ć?=?c?;?ę?=?e?;?ł?=?l?;?ń?=?n?;?ó?=?o?;?ś?=?s?;?ż?=?z?;?ź?=?z?}
    }
process
    {
    $chars.GetEnumerator() | ForEach-Object{
    $text = $text -replace $_.Key, $_.Value}
    }
end
    {
    $text
    Write-Host ?Calkowity czas: $($Elapsed.Elapsed.ToString())?
    }
}

Również trzy próby:

  • Całkowity czas: 00:00:00.0312814
  • Całkowity czas: 00:00:00.0367986
  • Całkowity czas: 00:00:00.0326167

By

Read More

× Close