Skok do liniiKiedy kompilator wyrzuca nam jakiś błąd, mamy o tyle wygodnie, że wystarczy w niego dwukrotnie kliknąć i już w swoim środowisku programistycznym przenosimy się do miejsca, w którym wystąpił problem. To w sumie dość oczywiste i zwykle w ogóle się nad tym nie zastanawiamy.
Zdarza się jednak, że kłopotliwy fragment musimy odnaleźć samodzielnie, dysponując tylko numerem wiersza, w którym się znajduje. Tak jest chociażby wtedy, gdy sygnalizujemy błąd czasu wykonania, używając przy okazji C++’owych makr typu __FILE__ i __LINE__. Co wtedy – mamy szukać feralnego miejsca ręcznie?…
Na szczęście w każdym porządnym IDE istnieje opcja Go To Line, pozwalająca na szybki skok do wiersza kodu o danym numerze. W Visual Studio (i nie tylko) dostępna jest pod skrótem klawiszowym Ctrl+G. Niby nic nadzwyczajnego, ale rzecz okazuje się częstokroć niezmiernie przydatna… na przykład wtedy, gdy z różnych powodów kompilujemy projekt z wiersza poleceń :)
Chromowane gogle
Od wczoraj trudno jest zrobić cokolwiek, aby przy okazji nie usłyszeć o nowym produkcie Google’a, czyli przeglądarce internetowej Chrome. Zdaje sobie niestety sprawę, że pisząc o tym mimowolnie wpisuję się w trend, który – jak mniemam – wkrótce sprawi, że będziemy z lekkim niepokojem otwierać lodówki ;P Jak dotąd na szczęście Google tam nie wtargnął, ale i tak wygląda na to, że narobił sporo zamieszania. Głównie medialnego, rzecz jasna: jego szczytem jest stwierdzenie, że „Chrome być może stanie się wkrótce synonimem przeglądarki internetowej”, które można znaleźć w gazecie.pl. No cóż, nie od dziś mainstreamowe popisują się totalną ignorancją, gdy chodzi o IT.
Żarty żartami, ale w rzeczywistości pojawienie się Chrome’a przede wszystkim stwarza kolejny problem dla webmasterów i twórców aplikacji internetowych. Jest całkiem prawdopodobne, że na samej tylko marce Google’a (niezależnie od obiektywnej jakości) Chrome zdobędzie przynajmniej kilka procent udziału w rynku, być może zrównując się z Operą. A to oznacza ni mniej, ni więcej tylko to, że do trzech liczących się silników przeglądarek WWW, z którymi teraz muszą liczyć się twórcy stron, wkrótce dojdzie czwarty. Gdyby tak jeszcze wszystkie przynajmniej próbowały być nieco bardziej zgodne z sieciowymi standardami… ale cóż, takie życie :P
W każdym razie nowa i potencjalnie popularna przeglądarka to nowy i potencjalnie dotkliwy ból głowy dla developerów. Może więc chociaż użytkownicy dostają w zamian coś nowatorskiego? Oprócz zwiększającego stabilność otwierania zakładek w osobnych procesach – prawdopodobnie nie. Zresztą, czy czegoś podobnego nie miał przypadkiem już Eksplorator Windows i to zaledwie 10 lat temu? :)
Triki z PowerShellem #10 – XMLObecnie 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:
# Ś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.
To, czego oczekuje kompilatorJęzyk C++ jest tworem skomplikowanym i jego złożoność daje się we znaki nie tylko programistom. Dość często mają z nią problemy także kompilatory. Obecnie zdecydowana większość narzędzi tego typu spełnia prawie całkowicie wymagania aktualnego standardu. Istnieje jednak sporo miejsca na usprawnienia, chociażby w bodaj najbardziej niedopracowanej dziedzinie: jakości komunikatów o błędach generowanych przez kompilatory.
Jeśli na przykład trafi nam się taka deklaracja zmiennej:
a typ SomeType z jakiegoś powodu (niedołączony nagłówek, kwestia zasięgu, itd.) będzie w jej miejscu nieznany, to treść wyprodukowanego przy okazji komunikatu o błędzie może nas trochę zaskoczyć. Zarówno CL (kompilator Microsoftu używany w Visual C++), jak i GCC wyplują bowiem coś w stylu:
error: expected ';' before 'variable'
To znaczy ni mniej, ni więcej, tylko to, że według tych kompilatorów właściwa jest "deklaracja":
Dość zaskakujące, nieprawdaż?
Takie zachowanie można częściowo wyjaśnić tym, że kompilator "nie patrzy" na kod tak, jak programista. My widzimy tutaj deklarację zmiennej, natomiast dla kompilatora jest to tylko sekwencja: 'nieznany identyfikator', po którym następuje kolejny 'nieznany identyfikator'. Ponieważ mówi się, że C++ jest językiem kontekstowym (co swoją drogą nie jest do końca ścisłe), taki fragment może sugerować wiele różnych rodzajów pomyłek. To, co sugeruje kompilator, wbrew pozorom też pewnie ma sens, chociaż dość specyficzny. Postawienie średnika w "spodziewanym" miejscu sprawi bowiem, że zamiast dwóch nieznanych nazw w jednej instrukcji będziemy mieli tylko jedną. Cóż za postęp! :)
Na tym prostym przykładzie widać, że interpretacja wyników kompilacji zakończonej niepowodzeniem wymaga niestety pewnego doświadczenia. Dopiero po jakimś czasie nauczymy się trafnie(j) zgadywać, co tak naprawdę oznacza ten czy inny błąd. Najwyraźniej taki już urok C++...
Triki z PowerShellem #9 – PotokowanieJedną 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:
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:
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:
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.
Triki z PowerShellem #8 – Profil i promptJeśli posługujemy się PowerShellem, to pewnie po jakimś czasie zechcemy dodać do niego jakiś nowy alias lub zupełnie nową komendę ogólnego przeznaczenia. W takim przypadku warto wiedzieć o istnieniu tak zwanego profilu, czyli skryptu ładowanego automatycznie przy starcie każdej sesji PS. Ścieżka do niego jest zawarta w zmiennej $profile i domyślnie ma formę:
Początkowo plik ten (ani nawet katalog, w którym się znajduje) nie istnieje, więc musimy go utworzyć. Gdy to zrobimy, będzie on pełnił funkcję powershellowego Autostartu.
Następnie możemy zechcieć coś w nim umieścić. Na początek można na przykład zmienić domyślny prompt (zwany czasem po "polskiemu" znakiem zachęty) powłoki na coś bardziej gustownego i przydatnego. W tym celu należy napisać funkcję prompt, która jako rezultat zwracać będzie odpowiedni ciąg.
Fani linuksowych shelli mogą na przykład sprawić, aby PS wyglądał trochę jak bash przy pomocy następującej funkcji:
Wynik prezentuje się mniej więcej tak:

Można naturalnie poeksperymentować z dodaniem innych informacji niż nazwa komputera i na nim zalogowanego użytkownika. Możliwości są bowiem nieporównywalnie większe niż to, co oferuje chociażby stary cmd.exe.
Odblokowywanie plikówCzasami zdarza się, że chcemy usunąć plik, który okazuje się być używany przez inny proces lub system operacyjny. Często nie mamy pojęcia, dlaczego tak jest i kto ma jakiś interes w trzymaniu blokady na tym właśnie pliku. Zwłaszcza Vista, będąca paranoicznie przewrażliwiona na punkcie zachowania integralności systemu plików, jest bardzo restrykcyjna pod względem swobody kasowania zbiorów będących w użyciu.
Oczywiście w takiej sytuacji można zawsze zrestartować system, ale trudno uznać to za wygodne rozwiązanie :) Dobrze byłoby więc mieć sposób na dowiedzenie się, jaki proces używa danego pliku, aby ewentualnie go zakończyć lub w skrajnym przypadku po prostu ubić.
I tu jest problem, bowiem w Windows nie ma na to prostego sposobu. Dotyczy to między innymi istnienia jakiegoś API, które by na to pozwalało; w najlepszym przypadku należy posłużyć się narzędziami z DDK, czyli pakietu służącego do... pisania sterowników działających w trybie jądra (kernel mode). Podobnie rzecz ma się z istnieniem wbudowanych w system programów, służących do tego celu (a więc czegoś podobnego do uniksowej komendy lsof). Odpowiednie narzędzia są jedynie częścią pakietów dodatkowych, jak Windows Server 2003 Resource Kit (niekompatybilny z Vistą) czy Windows Sysinternals. Mogą być one jednak dość kłopotliwe w obsłudze.

Dlatego podzielę się moim niedawnym odkryciem, którym jest mały i niezwykle przydatny programik o nazwie Unlocker. Potrafi on dokładnie to, o czym jest tu mowa: wylistować procesy, które używają danego pliku. Działa on przy tym na każdej sensownej wersji Windows, integruje się z menu kontekstowym plików dla większej wygody, no i jest do tego aplikacją freeware.
Rzadko robię czemuś aż taką "reklamę", ale w tym przypadku uznałem, że tak dobrym i użytecznym narzędziem należy się niezwłocznie podzielić ze światem :)