Notki oznaczone tagiem ‘PowerShell’

Triki z PowerShellem #15 – Puff, jak gorąco!

2010-07-13 23:03

Obecne temperatury są uciążliwe nie tylko dla nas - organizmów opartych o białka i węgiel, ale też dla naszych półprzewodnikowych, krzemowych gadżetów. Dotyczy to również komputerów oraz ich procesorów. Jak każde układy scalone, mają one tendencję do nagrzewania się - czasem nadmiernego. Dlatego dobrze jest monitorować temperaturę procesora.
Istnieje oczywiście wiele gotowych aplikacji, które na to pozwalają, ale przecież zawsze fajniej jest zakodować coś samemu, nieprawdaż? :) A już zupełnie świetnie jest wtedy, gdy użyjemy do tego naszego ulubionego narzędzia do administrowania systemem, czyli PowerShella.

Przy jego pomocy pobranie aktualnej temperatury procesora nie jest trudne. Posługujemy się do tego WMI - Windows Management Instrumentation - które to jest rozszerzeniem modelu sterowników systemu Windows, pozwalającym m.in. na monitorowanie różnego rodzaju informacji diagnostycznych. PowerShell zawiera wbudowane komendy, pozwalające na tworzenie obiektów WMI - wśród nich również takich, które udostępniają informacje na temat temperatury procesora:

# cpu-temp.ps1
# Pokazywanie temperatury procesora

$tzt = Get-WmiObject -Class "MSAcpi_ThermalZoneTemperature"
    -Namespace "root\WMI" -ErrorAction SilentlyContinue
if ($tzt -ne $null)
{
    $rawTemp = $tzt.CurrentTemperature
    $tempC = ($rawTemp - 2732) / 10
    "Temp. procesora: " + $tempC.ToString() + " st. C" | Out-Host
}
else { "Pomiar temperatury niemożliwy." | Out-Host }

Z bliżej nieokreślonych powodów wartość temperatury podawana jest w dziesiątych częściach kelwinów, więc konieczne jest ich przeliczenie na stopnie Celsjusza. Ponadto istnieje też prawdopodobieństwo, że używana tutaj klasa WMI o nazwie MSAcpi_ThermalZoneTemperature nie jest dostępna na naszym komputerze - zależy to prawdopodobnie od modelu jego płyty głównej. W moim przypadku powyższy kod działał bez problemu na komputerze stacjonarnym, lecz nie na laptopie. Może to i dobrze... Mam bowiem uzasadnione przeczucie, że temperatura tamtejszego procesora wcale by mi się nie spodobała ;)

  • RSS
  • Facebook
  • Twitter
  • Wykop
  • Reddit
  • del.icio.us
  • Google Bookmarks

Triki z PowerShellem #14 – Moja własna mała konsolka

2010-06-16 17:49

Jak zdołałem (mam nadzieję) pokazać w kilkunastu notkach na temat PowerShella, narzędzie to pozwala na (pół)automatyczne wykonywanie wielu niekiedy skomplikowanych operacji, o ile tylko potrafimy je zakodować w postaci odpowiednich skryptów. Możemy więc ułatwić sobie życie szybkim uploadem na serwer FTP, wysyłaniem maili, a nawet (ło kurczę!) uaktualnianiem statusu na Twitterze :) Kiedyś możemy jednak chcieć takie pojedyncze rozwiązania złożyć w jakąś większą całość - na przykład w tytułową "małą własną konsolkę".

O co chodzi? W skrócie: o coś w rodzaju własnego shella, przyjmującego ograniczony i ściśle określony zestaw poleceń, pozwalających na wykonywanie prostych czynności. Różnica względem normalnej powłoki jest taka, iż komendy te mają być w założeniu bardzo wysokopoziomowe - każda z opisywanych dotąd przeze mnie powershellowych "sztuczek" podpadałaby pod li tylko jedno polecenie, jeśli w ogóle. Najbliższym graficznym odpowiednikiem czegoś takiego jest pasek narzędzi lub skrótów do programów.
Aby nasz mały shell stał się faktem, potrzebujemy tylko kilku rzeczy, wśród których są:

  • Pobieranie poleceń od użytkownika. Od tego mamy w PSh instrukcję Read-Host, nad którą nie ma co się rozwodzić w jakiś specjalny sposób. Ot, po prostu daje nam w wyniku linijkę tekstu wpisaną do konsoli. Jeśli użyjemy parametru Prompt, nasz shell może mieć ładny znak zachęty :)
  • Wybór właściwej akcji w zależności od wpisanej komendy. Gdybyśmy pisali "normalny" program w "normalnym" języku programowania, wtedy pewnie dodalibyśmy bazowy interfejs dla komend, potem specyficzne klasy go implementujące, a potem jeszcze fabrykę tychże... No dobra, zdecydowanie przeginam ;-) W rzeczywistości zwykły switch jest tu niemal zbytkiem, zwłaszcza że w PowerShellu posiada on pożyteczny parametr -wildcard, pozwalający na dopasowanie ciągów znaków do wzorców zawierających symbole wieloznaczne (jak np. * lub ?). Dzięki temu można ograniczyć konieczność wpisywania całych nazw poleceń, jeśli jednoznaczna jest już sama pierwsza litera ("t*" zamiast "tweet", itp.).
  • Uruchamianie programów z parametrami. Odpalenie aplikacji w powłoce takiej jak PowerShell jest oczywiście bardzo proste. Nie zapominajmy jednak, ze mamy tutaj dostęp również do API .NET-owego, w tym chociażby do klasy System.Diagonistics.Process i jej metody Start - czegoś w rodzaju arcyprzydatnej kiedyś funkcji Windows API o nazwie ShellExecute. Jeśli zaś chodzi o przekazywanie parametrów, to nie zapominajmy o metodach do obsługi stringów - cała klasa System.String jest przecież dostępna.
  • Kontrola wejścia i wyjścia. Pod Windows nie jest to specjalnie powszechne, ale zdarzają się programy "w stylu uniksowym" - przyjmujące dane ze standardowego wejścia i wypisujące wyniki na standardowe wyjście. Pamiętajmy, ze przy pomocy potokowania (operator pionowej kreski |) możemy sobie z takimi aplikacjami radzić. PowerShell jest zresztą dobrym narzędziem do tworzenia używalnych front-endów do tego rodzaju programów.
  • Pobieranie kodu wyjścia programów. W starych plikach .bat można było (hmm... w sumie to nadal można) korzystać ze zmiennej %ERRORLEVEL%, zawierającej kod wyjścia ostatnio uruchomionego programu. W PSh. jej odpowiednik ma bardziej opisową nazwę: $LASTEXITCODE. Przypomnieć warto, że dla kodów wyjścia wartością oznaczającą poprawne wykonanie programu jest zero.

Składając te elementy w całość, możemy utworzyć shella z wygodnym dla nas zestawem poleceń. Kto wie, może będzie on nam odpowiadał bardziej niż pełne ikonek, kolorowe paski narzędzi :)

  • RSS
  • Facebook
  • Twitter
  • Wykop
  • Reddit
  • del.icio.us
  • Google Bookmarks

Triki z PowerShellem #13 – Prędkość czasu

2009-12-31 13:47

Ostatni dzień roku to dobry moment na różne dziwne przemyślenia na temat czasu. Kiedyś z tej okazji wymyśliłem zupełnie nowy system kalendarza, a dzisiaj przypomniałem sobie o niezupełnie poważnym pomyśle autorstwa Rega, ze zbliżonej dziedziny.
Chodzi o oryginalną wielkość "fizyczną": prędkość czasu. Ma ona ilościowo mierzyć znany wszystkim fakt, że wrażenie upływającego czasu jest różne w różnych sytuacjach. Gdy w rzeczywistości upłynął pewien czas t, a nam wydawało się, że minął raczej czas t', to odczuwana przez nas prędkość czasu \tau wynosi

\displaystyle \tau = \frac{t}{t'}.

Dla przykładu: na nudnym wykładzie mogło minąć ledwie 15 minut, podczas gdy nam wydaje się, że siedzimy na nim już godzinę; wówczas \tau_1 = 0,25 / 1 = 0,25. Z kolei grając przez trzy godziny możemy mieć wrażenie, że upłynęło tylko 30 minut - wtedy \tau_2 = 3 / 0,5 = 6. Jak widać \tau_1 <\tau_2, zatem teoria zgadza się tutaj z intuicją, co pozwala wnioskować, że wielkość jest dobrze zdefiniowana :)

Pozostaje jeszcze kwestia jej pomiaru. W swoim oryginalnym artykule (który w tajemniczy sposób zniknął z sieci) Reg zaproponował, że można by go przeprowadzić za pomocą odpowiedniego programu. Miałby on działać w tle, losować pewien interwał czasu i po jego upływie pytać użytkownika, ile czasu według niego upłynęło od ostatniego pytania. Z dokładnością do małych błędów pomiarowych (np. czasu potrzebnego na wpisanie odpowiedzi) metoda wydaje się dobra - czemu więc by jej nie zaimplementować? :]
To właśnie uczyniłem, posługując się oczywiście swoim ulubionym narzędziem na takie okazje, czyli PowerShellem. Pewnym problemem było to, jak w skrypcie konsolowym postarać się o mierzenie czasu w sposób, który byłby niezauważalny dla użytkownika. Da się to jednak łatwo zrobić, posługując się... wbudowanym w system Harmonogramem zadań (Task Scheduler). Istnieje bowiem do niego dobrze udokumentowane, ładne i przejrzyste COM-owe API (dostępne od Visty wzwyż), którego użycie w PowerShellu wygląda zresztą kilka razy lepiej niż w C++ ;P
Wynikowy skrypt wygląda więc następująco:

# timespeed.ps1
# Skrypt do mierzenia prędkości czasu
param ([int]$minutes = 0)

# Stałe
$TASK_NAME = "TimeSpeed_Task"   # Nazwa zadania w Harmonogramie

# Pobieramy obiekt harmonogramu zadań
$ts = New-Object -ComObject Schedule.Service
$ts.Connect($null, $null, $null, $null)
$tf = $ts.GetFolder($(New-Object String @([char]92, 1))) # "\"

if ($minutes -eq 0)
{
    # Pierwsze uruchomienie
    $task = $ts.NewTask(0)
    $task.Data = ""
    $continue = "T"
}
else
{
    # Kolejne uruchomienia
    $task = $tf.GetTask($TASK_NAME).Definition
   
    # Pytamy użytkownika i liczymy prędkość czasu
    $userMinutes = Read-Host -Prompt "Liczba minut od ostatniego razu"
    $d_tau = $minutes / $userMinutes
    if (-not [String]::IsNullOrEmpty($task.Data))
    {
        $timeData = $task.Data.Split(',')
        $totalTime = [int]$timeData[0]
        $totalUserTime = [int]$timeData[1]
    }
    $totalTime += $minutes
    $totalUserTime += $userMinutes
    $tau = $totalTime / $totalUserTime
    $task.Data = $totalTime.ToString() + "," + $totalUserTime.ToString()
   
    # Wyświetlamy aktualne wyniki
    "Rzeczywista liczba minut: " + $minutes | Out-Host
    "Chwilowa prędkość czasu: " + $d_tau | Out-Host
    "Całkowita prędkość czasu: " + $tau | Out-Host
   
    # Pytamy użytkownika, czy chce kontynuować
    $continue = Read-Host -Prompt "Wykonać następny pomiar (T/n)?"
}

if ($continue -ine "n")
{
    # Losujemy nowy interwał czasowy (od 5 do 60 minut, co 5)
    $rand = New-Object System.Random
    $nextMinutes = ($rand.Next(0, 12) + 1) * 5
   
    # Dodajemy trigger dla zadania (czas: za X minut)
    $task.Triggers.Clear()
    $trigger = $task.Triggers.Create(1) # 1 - trigger na określony czas
    $nextTime = [DateTime]::Now.AddMinutes($nextMinutes)
    $trigger.StartBoundary = $nextTime.ToString("o")
   
    # Dodajemy akcję (uruchomienie skryptu)
    $task.Actions.Clear()
    $action = $task.Actions.Create(0)   # 0 - uruchomienie polecenia
    $action.Path = "powershell"
    $action.Arguments = "-Command . '" + $MyInvocation.MyCommand.Path
    $action.Arguments += "' -minutes " + $nextMinutes.ToString()
   
    # Dodajemy zadanie
    # (6 - stwórz lub uaktualnij)
    # (3 - zadanie uruchamiane tylko przy zalogowanym użytkowniku)
    $tf.RegisterTaskDefinition($TASK_NAME, $task, 6, $null, $null, 3) | Out-Null
}
else
{
    # Koniec - usuwamy zdarzenie
    try { $tf.DeleteTask($TASK_NAME, 0) } catch { }
}

Przy swoim pierwszym uruchomieniu tworzy on nowe zadanie w Harmonogramie, które polega na jego ponownym odpaleniu po upływie losowej liczby minut (5, 10, 15, itd. aż do maks. 60 minut). Wtedy użytkownik jest pytany o to, ile czasu według niego upłynęło, a następnie wyświetlają się częściowe wyniki pomiaru. Wówczas można zdecydować o jego kontynuowaniu lub zakończeniu. (Zawsze można też ręcznie usunąć zadanie z Harmonogramu, rzecz jasna).

Skrypt w PSh do mierzenia prędkości czasu

I to w sumie tyle. Można już przystąpić do badań określających dokładnie, jak bardzo dłuży nam się czas do północy :))

  • RSS
  • Facebook
  • Twitter
  • Wykop
  • Reddit
  • del.icio.us
  • Google Bookmarks

Triki z PowerShellem #12 – Rozpakowywanie

2009-12-07 23:18

Powtarzające się katalogiWiele programów z sieci wciąż jeszcze ściąga się w postaci archiwów do samodzielnego wypakowania, jak choćby w formacie .zip. Ma to swoje zalety i wady - do tych drugich należy fakt, że nie bardzo wiadomo, jak wygląda wewnętrzna struktura katalogów takiej paczki. Używając opcji typu Wypakuj tutaj ryzykujemy zaśmiecenie folderu Downloads plikami programu. Dlatego osobiście zawsze stosuję polecenie Wypakuj do nowego katalogu.
I tu czasem jest mały zonk, gdy twórca archiwum zdecydował się na spakowanie całego folderu, a nie tylko zawartych w nim plików. Powstają wtedy nadmiarowe katalogi, wydłużające ścieżki do plików (co widać obok - w wersji trochę przesadzonej ;)).

Ponieważ podobne sytuacje zdarzają mi się dość często, postanowiłem im zaradzić przy pomocy najlepszego narzędzia na takie okazje, czyli PowerShella rzecz jasna :) Wynikiem jest poniższy skrypt do sprytniejszego rozpakowywania archiwów:

# unpack.ps1
# Sprytne rozpakowywanie archiwów
param ([string]$archive = $(throw "No archive specified"))

# Bierzemy nazwę archiwum i tworzymy odpowiadający mu katalog
$name = [IO.Path]::GetFileNameWithoutExtension($archive)
Set-Location -Path (New-Object IO.FileInfo @($name)).DirectoryName
$dir = [IO.Directory]::CreateDirectory($name)

# Rozpakowujemy archiwum do tego katalogu
$shell = New-Object -ComObject Shell.Application
$src = $shell.Namespace($archive)
$dest = $shell.Namespace($name)
$dest.CopyHere($src.items())

# Rekurencyjnie badamy zawartość rozpakowanego archiwum
while (($items = $dir.GetFileSystemInfos()) -eq 1)
{
    # Sprawdzamy, czy jego pierwszy i jedyny element jest katalogiem
    $fsi = $items[0]
    if (($fsi.Attributes -band [IO.FileAttributes]::Directory) -eq 0)
        { break }
   
    # Jest - dokonujemy skrócenia ścieżki
    $fsi.MoveTo([IO.Path]::GetRandomFileName())
    $dir.Delete()
    $dir = $fsi
    $dir.MoveTo($name)
}

Jego działanie polega wpierw na zwykłej dekompresji archiwum. Jak można zauważyć, używa do tego obiektu COM-owskiego Shell.Application. To sprawia, że skrypt ma pod tym względem te same możliwości co zwykły windowsowy Eksplorator (dla większych plików pokaże nawet pasek postępu ;]).
Później wypakowana zawartość jest poddawana operacji, którą nazywam tutaj 'skróceniem ścieżki'. Polega ona wyrzuceniu jednego poziomu drzewa folderów, o ile tylko pewien katalog jest jedynym elementem swojego katalogu nadrzędnego. Takie właśnie sytuacje powstają przy dekompresji do nowego folderu archiwów źle zapakowanych (przynajmniej z mojego punktu widzenia ;P). Wynikiem działania skryptu będzie więc w sumie jeden nowy podkatalog zawierający bezpośrednio całą interesującą zawartość archiwum.

Oczywiście używanie powyższego skryptu tylko z poziomu linii komend PowerShella nie jest specjalnie wygodne; lepiej jest dodać go do menu kontekstowego archiwów, czyli np. plików .. O tym, jak można tego dokonać, napisałem dość obszernie przy okazji prezentacji skryptu do wysyłania przez FTP.

  • RSS
  • Facebook
  • Twitter
  • Wykop
  • Reddit
  • del.icio.us
  • Google Bookmarks

Triki z PowerShellem #11 – Ćwierkamy

2009-10-30 19:41

W ramach wyposażania nowozainstalowanego systemu w niezbędne programy, przypomniałem sobie o istnieniu PowerShella. Kiedy jednak chciałem go ściągnąć, spotkała mnie przyjemna niespodzianka: PSh w Windows 7 jest już od razu zainstalowany, więc można go od razu zacząć go używać. Jak sądzę, przyczyni do zwiększenia jego popularności, co jest z pewnością dobrą rzeczą.

Obrazek z TwitteraFakt sprawił rzecz jasna, że zaraz zachciało mi się wypróbować go w jakimś nowym zastosowaniu. Padło na wysyłanie update'ów do Twittera, w którym to zresztą niedawno się zarejestrowałem (i wciąż nie wiem, dlaczego ;)). Sprawa na oko nie jest trudna, bo sprowadza się do wykonania jednego żądania HTTP POST. Ale jak wiadomo, diabeł zwykle tkwi w szczegółach. Oto skrypt:

# tweet.ps1
# Wysyłanie nowego statusu do Twittera

[Reflection.Assembly]::LoadWithPartialName("System.Web") | Out-Null

# Stałe
$LOGIN = "login" # lub e-mail
$PASS = "hasło"

# Pobranie statusu od użytkownika
$tweet = Read-Host -Prompt "Status"

# Złożenie żądania HTTP POST
$uri = [Uri]"http://twitter.com/statuses/update.xml"
$http = [Net.HttpWebRequest]::Create($uri)
$http.Credentials = New-Object Net.NetworkCredential @($LOGIN, $PASS)
$http.Method = [Net.WebRequestMethods+Http]::Post
$http.ServicePoint.Expect100Continue = $false # (*)

# Wysyłanie danych
$data = "status=" + [Web.HttpUtility]::UrlEncode($tweet)
$http.ContentLength = $data.Length
$sw = New-Object IO.StreamWriter @($http.GetRequestStream())
    $sw.Write($data)
$sw.Close()

# Wyświetlamy ID nowego statusu
$resp = $http.GetResponse().GetResponseStream()
$sr = New-Object IO.StreamReader @($resp)
$xml = [xml]$sr.ReadToEnd()
"Status updated (ID: " + $xml.status.id + ")" | Out-Host
$sr.Close()

# Obsługa błędów
trap    { "Error: " + $_.Exception.Message; return }

Jednym z owych detali było kodowanie statusu algorytmem dla URL-i (zamieniającym spacje na %20 itd.), wykonywane poprzez System.Web.HttpUtility.UrlEncode - stąd konieczność importowania assembly System.Web. Ale to jest w sumie pikuś.
Znacznie większym "trikiem" jest linijka oznaczona gwiazdką (*). Powoduje ona obejście domyślnego zachowania .NET, który do każdego żądania HTTP typu POST dodaje nagłówek:

Expect: 100-continue

Powoduje on wysłanie tak naprawdę dwóch requestów: w pierwszym serwer ma tylko sprawdzić poprawność nagłówków (logowania, na przykład) i zwrócić status 100 (Continue). Dopiero w drugim klient wysyła właściwe dane. Mechanizm ten jest w .NET opakowany przezroczyście i ma zapobiegać niepotrzebnemu przesyłaniu dużych ilości danych w żądaniu, które i tak byłoby odrzucone.
API Twittera jednak tego nie obsługuje i jest to właściwe. Trudno przecież nazwać status, mający maks. 160 znaków, "dużą ilością danych". Lepiej więc przesyłać go od razu, a domyślne zachowanie .NET-a obejść. To właśnie robi zaznaczony wiersz.

Przypomnę jeszcze tylko - gdy ktoś zechciał powyższego skryptu używać do przesyłania tweetów - że uruchomienie skryptu PSh z poziomu zwykłej linii poleceń wymaga parametru -Command i kropki:

powershell -Command . 'ścieżka\tweet.ps1'

Do takiej komendy można np. utworzyć skrót i przypisać mu kombinację klawiszy w celu szybkiego uruchamiania.

  • RSS
  • Facebook
  • Twitter
  • Wykop
  • Reddit
  • del.icio.us
  • Google Bookmarks

Triki z PowerShellem #10 – XML

2008-08-25 0:12

Obecnie magiczne trzy literki XML pojawiają się przy okazji niemal każdego aspektu programowania i informatyki w ogóle. Do przetwarzania danych zapisanych w tej postaci istnieje niezmierzona wręcz ilość różnych narzędzi. Nie jest więc niespodzianką, że nasz ulubiony PowerShell w tej dziedzinie również daleko nie odstaje :)
Jako parsera używa on oczywiście narzędzi zawartych w .NET, a dokładniej w przestrzeni nazw System.Xml. Są one jednak o wiele wygodniejsze w stosowaniu w skryptach PSh niż w zwykłych aplikacjach .NET. Spójrzmy chociażby na poniższy przykład:

# rss.ps1
# Czytnik kanałów RSS
param ([string]$url = $(throw "No RSS specified"))

# Ściągnij zawartość kanału RSS jako XML
$webClient = New-Object Net.WebClient
$xml = [xml]$webClient.DownloadString($url)

# Wyświetl pozycje z wszystkich kanałów
foreach ($chan in $xml.rss.channel)
{
    foreach ($item in $chan.item)
    {
        "[" + $item.pubDate + "] " + $item.title | Out-Host
    }
}

To nic innego, jak najprostszy czytnik kanałów RSS. Pobiera on i wyświetla nagłówki wraz z datą ich publikacji z podanego URL-a, co wygląda choćby tak:

[Fri, 22 Aug 2008 11:21:52 +0000] Używanie nagłówków Windows
[Tue, 19 Aug 2008 10:53:24 +0000] To, czego oczekuje kompilator
[Sat, 16 Aug 2008 19:28:58 +0000] Statystyczny warsztatowicz
[Thu, 14 Aug 2008 06:24:54 +0000] Makra z wielokropkiem
[Mon, 11 Aug 2008 20:12:17 +0000] Długie ciągi odwołań
[Fri, 08 Aug 2008 20:00:57 +0000] Obiektowy szowinizm
[Tue, 05 Aug 2008 15:21:31 +0000] Czy void jest typem
[Mon, 04 Aug 2008 15:12:30 +0000] Prawie jak mapa
[Sun, 03 Aug 2008 15:33:28 +0000] Triki z PowerShellem #9 - Potokowanie
[Thu, 31 Jul 2008 14:34:14 +0000] Przeciążanie przecinka

Taka jest operacja jest całkiem łatwa do przeprowadzenia. Sparsowanie łańcucha znaków do postaci XML-owego drzewka DOM, reprezentującego jego zawartość polega na przykład na "rzutowaniu" tekstu na typ xml (będący aliasem na System.Xml.XmlDocument) - i już. Następnie możemy dostać się do poszczególnych tagów używając po prostu kropki. Zatem np. $xml.rss pozwala odwołać się do głównego elementu (<rss>) w dokumencie RSS. Jeśli zaś pod daną nazwą kryje się więcej niż jeden element, możemy je iterować tak samo, jak każdą inną kolekcję (a więc np. przez foreach).

Do bardziej zaawansowanych operacji należy zapewne wykorzystać właściwości i metody klasy XmlDocument. Ale do prostych zastosowań związanych z XML-em, jakie mniej lub bardziej systematycznie przytrafiają się każdemu, powyższy sposób postępowania może być całkowicie wystarczający.

  • RSS
  • Facebook
  • Twitter
  • Wykop
  • Reddit
  • del.icio.us
  • Google Bookmarks
Tagi: ,
Autor: Xion w Aplikacje, Internet » Komentarze (3)

Triki z PowerShellem #9 – Potokowanie

2008-08-03 17:33

Jedną z głównych zalet powłok tekstowych w rodzaju PowerShella jest to, że pozwalają one na łączenie ze sobą małych, elementarnych poleceń w jedno duże zadanie. Gdy odbywa się to na zasadzie przekazywania wyników jednego programu na wejście następnego, mówimy o potokowaniu (pipelining). Znawcy basha mogliby rzucić np. takim przykładem:

cat <program.cpp | grep int | wc --lines

w którym to najpierw odczytywana jest zawartość pliku (cat), wyszukiwane są wszystkie linijki zawierające ciąg "int" (grep) i liczona jest ich ilość (wc). Całkiem oczywiste, prawda? ;-)

W PowerShellu przetwarzanie potokowe jest o tyle lepsze niż w innych powłokach, że między poleceniami wymienia się nie tekst, a obiekty .NET-owe. Dobrze byłoby więc wiedzieć, jak należy pisać własne skrypty, aby mogły one działać w potokach. Na szczęście nie jest to specjalnie trudne; prezentuje to ten oto skrypt:

# pokemonize.ps1
# Zmienia litery w tekście na losowo duże lub małe

begin
{
    # Inicjujemy generator liczb losowych
    $rand = New-Object Random
}

process
{
    $str = $_.ToString()
    $result = New-Object Text.StringBuilder
   
    # Zmieniamy znaki
    for ($i = 0; $i -lt $str.Length; ++$i)
    {
        if ($rand.NextDouble() -lt [float]0.5)
            { $newChar = [Char]::ToLower($str[$i]) }
        else
            { $newChar = [Char]::ToUpper($str[$i]) }
           
        $result.Append($newChar) | Out-Null
    }
   
    # Wyświetlamy przetworzony ciąg
    $result.ToString()
}

Tym, co on robi, jest losowa zmiana wielkości liter w ciągu stanowiącym tekstową reprezentację obiektu (lub obiektów), jaki dostaje na wejściu. Jednym słowem, dokonuje jego pokemonizacji... Pewnie dla niektórych byłoby to przydatne, ale powiedzmy, że to tylko taki przykład ;P
Najważniejsze jest to, że podany skrypt działa dobrze na obiektach otrzymanych poprzez potok, czyli np. linijkach tekstu odczytanego z pliku poprzez Get-Content:

Get-Content ./Plik.txt | . ./pokemonize.ps1 | Out-Host

Dla każdego takiego obiektu (tutaj: wiersza z pliku) wykonywany jest blok process, w którym to ów obiekt jest reprezentowany jako zmienna $_. Można to sobie wyobrazić jako wnętrze pętli foreach, przelatującej po wszystkich obiektach z wejścia skrypt. Dodatkowo możemy jeszcze określić bloki: begin (uruchamiany na samym początku) i end (na końcu). W nich powinniśmy umieścić ewentualny kod inicjalizujący i/lub kończący pracę skryptu.

  • RSS
  • Facebook
  • Twitter
  • Wykop
  • Reddit
  • del.icio.us
  • Google Bookmarks
Tagi: ,
Autor: Xion w Aplikacje » Dodaj komentarz
 



© 2010 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.