r. Q - strona domowa
Strona Główna Artykuły Produkcje Linki O mnie Kontakt
Strona Główna Artykuły Produkcje Linki O mnie Kontakt
info ARTYKUŁY: "Programowanie w powłoce Windowsa XP"

     Powstała już dość pokaźna liczba kursów dotycząca programowania wsadowego, ale duża część z nich jest bardzo ogólna i należy traktować ją bardziej jako wprowadzenie. Dlatego, aby nie powielać pracy innych ludzi, postanowiłem poświęcić ten artykuł rozszerzonej składni języka skryptowego powłoki Windowsa XP.
     W poprzednich wersjach systemu Windows pliki wsadowe pozwalały zautomatyzować pewne typowe czynności jak np. tworzenie kopii zapasowych zbioru plików, jednak do bardziej zaawansowanych operacji raczej się nie nadawał (np. nie umożliwiał wykonywania operacji artytmetycznych). Rozwój systemu Windows przyniósł także rozwój jego języka skryptowego. Wiele poleceń uległo rozbudowie, stając się bardziej elastycznymi. W swym artykule postaram się pokazać, jak korzystając z rozszerzonych możliwości Batcha przeprowadzać operacje arytmetyczne, pobierać i sortować dane, zasymulować typ tablicowy, wskaźnikowy i wiele innych sztuczek. Jeśli jesteś niecierpliwy to zapraszam do działu produkcje, w którym znajdziesz kilka przydatnych skryptów wykorzystujących to, o czym piszę w artykule. Znajduje się tam również implementacja gry w "Kółko i krzyżyk" wykonana w całości jako skrypt powłoki. Natomiast jeśli możesz poczekać jeszcze chwilkę, to życzę przyjemnej lektury:)
     Zanim zaczniemy musisz wiedzieć, że jeśli nie znasz podstaw programowania wsadowego, to artykuł ten nie jest tym od którego powinieneś zacząć. Do tego celu polecam http://www.pldos.pl/info/plikiwsadowe.htm »
     I ostatnia sprawa. W artykule nie opisuje dokładnie składni poszczególnych poleceń, a skupiam się raczej na ich praktycznym wykorzystaniu. Jeśli chcesz poznać szczegóły danego polecenia to odsyłam do pomocy systemowej (wpisz w linii poleceń "nazwa_polecenia /?") lub do specjalnej ściągi z plików wsadowych przygotowanej specjalnie na potrzeby tego artykułu. "Ściąge z plików wsadowych" możesz pobrać z działu produkcje.
     Dobra, wystarczy tego ględzenia, teraz do roboty;)

CMD

     W Windows XP aby uruchomić konsole, uruchamiamy program cmd.exe. To właśnie od tego programiku zależy interpretacja plików wsadowych, więc poświęcimy mu tutaj troszkę miejsca.
Domyślnie cmd uruchamia się z parametrem /E:ON co oznacza, że jest włączona obsługa rozszerzonych poleceń. Jest to dla nas bardzo ważne ponieważ gdyby nie to, nie byłoby potrzeby pisania tego arta;) Analogicznie wpisując /E:OFF wyłączamy obsługę rozszerzonych poleceń. Kolejną ważną opcją, którą będziemy namiętnie wykorzystywać, jest możliwość włączenia tzw. opóźnionego rozwijania zmiennych środowiskowych. Brzmi to nieco strasznie, ale uwierz, jest to w swej istocie bardzo prosta sprawa. Aby ją zrozumieć, posłużmy się prostym przykładem. Zmienna środowiskowa RANDOM przechowuje liczbę pseudolosową. Jeśli teraz napiszemy prosty skrypcik bez obsługi opóźnionego rozwinięcia np.:

@ECHO OFF
FOR %%i IN (1 2 3 4 5 6 7 8 9 10) DO ECHO %RANDOM%

to stwierdzimy, że zamiast wyświetlenia dziesięciu liczb pseudolosowych otrzymamy dziesięciokrotne wypisanie jakiejś jednej liczby. Nie o to nam chodziło prawda? Okazuje się, że nasz programik przez cały czas swojego działania posługuje się wartością zmiennej RANDOM jaka występowała w trakcie czytania skryptu przez system, a nie w trakcie jego wykonywania. Aby umożliwić naszemu programikowi korzystanie z aktualnej wartości zmiennej środowiskowej RANDOM, użyjemy właśnie opóźnionego rozwinięcia zmiennych środowiskowych. Aby było to możliwe, w konsoli uruchomimy program cmd z parametrem /V:ON

CMD /V:ON

A następnie przepiszemy program:

@ECHO OFF
FOR %%i IN (1 2 3 4 5 6 7 8 9 10) DO ECHO !RANDOM!

Znaki '!' otaczające zmienną mówią interpreterowi, że tą zmienną należy rozwinąć w sposób opóźniony. Dzięki temu zabiegowi otrzymaliśmy zamierzony efekt - dziesięć liczb pseudolosowych. Jak się później okaże, tego typu zabiegi są bardzo przydatne np. do zasymulowania typu wskaźnikowego lub tablicowego, ale o tym później.
     Jak być może zwróciłeś uwagę, po uruchomieniu programu cmd z włączonym opóźnionym rozwijaniem zmiennych środowiskowych, w konsoli znajdują się teraz dwa wywołąnia cmd - pierwsze to sama konsola (cmd uruchomiony w celu otworzenia konsoli), a drugie to cmd z opcją opóźniania. Jeśli teraz wpiszemy polecenie wyjścia (exit) to nasza konsola się nie zamknie - zamknięciu ulegnie najmłodsze wywołanie. Dopiero po drugim wpisaniu słówka exit konsola się zamknie. Jak pewno się domyślasz, nie jest to najlepsze rozwiązanie - każde takie wywołanie zabiera część pamięci, zwalnia naszą maszynę, oraz wprawia nas w kłopot przy wychodzeniu z konsoli.Możemy się uporać z tym problemem, korzystając z opcji /C. Sprawia ona, że wywołanie cmd z jakimiś ustawieniami dotyczy tylko tego co znajduje się po znaczku /C w tej samej linii, bądź co jest połączone separatorem poleceń &&. W takim wypadku nasz przykład będzie wyglądał tak:

@ECHO OFF
CMD /Q /V:ON /C FOR /L %%i IN (1,1,10) DO ECHO !RANDOM!

Opcja /Q oznacza, że nasz interpreter cmd ma automatycznie wyłączone echo (czyli wyrzucanie poleceń na ekran komputera). Dziwny wygląd pętli FOR to nic innego jak jej nowsza, rozszerzona wersja. Będę się nią zajmował nieco później. Teraz wiedz tylko, że ciało pętli wykona się dziesięć razy.
     Jeśli zależy Ci na poznaniu wszystkich opcji komendy CMD to odsyłam Ciebie do dokumentacji (wpisz cmd /? w linii komend konsoli), ponieważ nie jest moim celem jej powielanie;) Znajdziesz tam m.in. informacje jak zautomatyzować niektóre operacje poprzez zmianę odpowiednich kluczy w rejestrze i tak dalej.

SET

     Kolejnym bardzo przydatnym rozszerzeniom uległo polecenie SET. Wcześniej za jego pomocą mogliśmy przypisać konkretną wartość (ciąg znaków) zmiennej środowiskowej, ale nie mogliśmy przypisać wartości zwracanej przez jakieś wyrażenie. Teraz już możemy, i nie tylko to. Ale po kolei.
     Aby przypisać zmiennej środowiskowej wynik jakiegoś działania, to powinniśmy użyć  polecenia SET z parametrem /A, a następnie w cudzysłowie wpisać nasze wyrażenie np:

SET /A liczba="12345+54321"

ustawi naszą zmienną liczba na wartość 66666. Wszystkie operatory umieściłem w ściągawce z plików wsadowych, dostępnej w dziale produkcje. Praktycznym zastosowaniem może być na przykład zliczanie ilości plików z jakimś rozszerzeniem w naszym katalogu. Dla plików *.bat, skrypcik może wyglądać tak:

@ECHO OFF
SETLOCAL
SET ilosc=0
FOR %%i IN (*.bat) DO SET /A ilosc+=1
ECHO Znalazlem %ilosc% plikow *.bat
ENDLOCAL

Proste prawda? Operator += znany z języka C/C++, jest w naszym przykładnie równoważny z zapisem ilosc="!ilosc!+1". Wygodniej jak widać używać +=, ponieważ nie musimy uruchamiać opóźnionego rozwijania zmiennych środowiskowych.
     Polecenie SET zostało dodatkowo wzbogacone o możliwość pobierania ciągu znaków od użytkownika. Możemy to wykorzystać do listowania plików z katalogu, do którego ścieżkę dostępu podaje nam użytkownik:

@ECHO OFF
SETLOCAL
SET /P sciezka="Podaj pelna sciezke dostepu do katalogu "
FOR %%i IN (%sciezka%\*) DO ECHO %%~nxi
ENDLOCAL

Znaczki ~nx przed zmienną sterującą formatują wyjście w taki sposób, że polecenie ECHO wyświetla tylko nazwy i rozszerzenia plików (a nie, jak domyślnie, pełną ścieżkę dostępu). I tutaj poraz kolejny odsyłam do dokumentacji lub wspomnianej wcześniej ściągawki.
Powracając jednak do tematu, możliwości wykorzystania rozszerzonego polecenia SET są ogromne, więc pewno nie będziesz miał kłopotów z ich znalezieniem. W następnym rozdziale opiszę rozszerzoną pętlę FOR, która w połączeniu z ustawianiem zmiennych stanowi już całkiem pokaźny arsenał.

FOR

     Pętla FOR jest jednym z bardziej rozszerzonych elementów "nowego" Batcha. Wyewoluowała ze zwykłej pętli „jeżdżącej” po plikach w podanym katalogu lub po podanym zbiorze wartości (przykłady takiej pętli możesz znaleźć w rozdziale poprzednim) do całkiem sporej machinki, która umożliwia m.in. wykonywanie pętli iteracyjnych, parsowania plików (czyli wykonywaniu pewnych operacji na zawartości pliku) i wiele więcej.
     Zacznijmy może od pętli „jeżdżącej” po katalogach. Pętli takiej używamy z opcją /D wpisaną po słówku FOR. Może to się nam przydać w różnych sytuacjach. Przypuśćmy, że masz płytkę CD całą wypełnioną e-bookami. Każdy ebook ma swój katalog, a w tym katalogu jest wiele plików; każdy odpowiadający jednemu rozdziałowi tego ebooka. I co robisz? Sięgasz oczywiście do tego arta, którego właśnie czytasz;) Stara pętla FOR jest tutaj bezużyteczna, bowiem dotyczy tylko plików, a nas nie interesują pliki, tylko katalogi.
Skrypt wykonujący listingu katalogów może wyglądać tak:

@ECHO OFF
FOR /D %%i IN (*) DO ECHO %%i

Tutaj oczywiście zmienna sterująca zawiera ciąg znaków określający nazwę katalogu.
Możemy nasz program wzbogacić o obsługę danych wprowadzanych przez użytkownika, który mógłby wpisać jakie katalogi chce listować. Jest to kwestia użycia polecenia SET /P o którym pisałem w poprzednim rozdziale więc myślę, że potrafiłbyś to zrobić sam;)
     Kolejną możliwością jest zejście rekurencyjne po drzewie katalogów.Tę możliwość uzyskujemy poprzez dodanie /R przed słówko FOR. Ta pętla „jeździ” po plikach, podobnie jak jej stara wersja, jednak tym razem wchodzi do wszystkich katalogów podrzędnych dla obecnego katalogu, następnie do wszystkich podrzędnych katalogów tych podrzędnych katalogów i tak dalej. Jeśli więc uruchomimy skrypt w katalogu głównym, nasza pętla wykona się dla WSZYSTKICH plików naszego dysku. Ciekawym zastosowaniem takiej pętli, może być skrypt wyszukujący pliki. Może on w naszym przypadku wyglądać tak (UWAGA! nie należy łamać linii w miejscu oznaczonym strzałką "->"!):

@ECHO OFF
SETLOCAL
SET ile=0
SET /P plik="Podaj nazwe i rozszerzenie pliku, ktorego szukasz "
FOR /R %HOMEPATH% %%i IN (*) DO IF /I "%%~nxi" EQU "%plik%" ->
SET /A ile+=1 && ECHO %%i
ECHO ===========
ECHO Znaleziono %ile% pliki(-ow).
ENDLOCAL

Program szuka łańcuchów znaków oznaczających nazwę i rozszerzenie pliku, pasujących (EQU – patrz rozdział o rozszerzonej instrukcji warunkowej IF) do wzorca pobranego od użytkownika (/I - nie zwracając uwagi na wielkość liter). Szuka ich schodząc rekurencyjnie do wszystkich podrzędnych katalogów katalogu użytkownika (HOMEPATH). Gdy znajdzie, zwiększa licznik o jeden i wyświetla pełną ścieżkę dostępu do odnalezionego pliku. Znaczek && to tzw. separator poleceń, który służy do łączenia poleceń w jeden blok. Gdy połączone są dwa polecenia, to pół biedy. Gdy chcemy połączyć większą ilość poleceń to sugeruje tworzenie oddzielnych bloków i odwoływanie się do nich za pomocą polecenia CALL. Polecenie CALL opiszę później.
     Jak być może pamiętasz, aby zmienna sterująca pętlą FOR przyjmowała określone wartości np. od 1 do 10 z krokiem równym 1, należało napisać:

FOR %%i IN (1 2 3 4 5 6 7 8 9 10) DO [polecenie]

Teraz, dysponując składnią rozszerzoną, kiedy potrzebujemy wykonać coś określoną ilość razy wtedy wykorzystamy pętlę iteracyjną. Wykorzystujemy do tego atrybut /L pętli FOR. Zmienna sterująca zostaje przypisana wartość początkowa. W kolejnych krokach, zmienna sterująca jest zwiększana o podaną wielkość kroku. Pętla kończy się, gdy zmienna sterująca uzyska wartość graniczną. Możemy to wykorzystać do stworzenia sobie swoistej tablicy dowolnego typu, z indeksami od 1 do n. Zatem jako przykład proponuje przyjrzeć się, w jaki sposób możemy zainicjować sobie taką tablicę dziesięcioelementową:

FOR \L %%i IN (1,1,10) DO SET tablica[%%i]=%%i

Zmienna sterująca naszej pętli na początku ma wartość 1, a po każdym cyklu pętli jest zwiększana o jeden. Inicjujemy sobie zmienna tablica z indeksem określonym przez zmienną sterującą i przypisujemy jej taką samą wartość. Indeksy nie muszą być wcale w nawiasach kwadratowych. Można napisać np. tablica%%i lub tablica(%%i), ale ja zastosowałi składnię podobną do C/C++ (z czystej wygody). Jeśli wolisz, możesz używać innego zapisu, masz wolny wybór;)
     Teraz aby odczytać sobie zawartość naszej pseudo-tablicy musimy użyć (jak się zapewne domyślasz) opóźnionego rozwijania zmiennych środowiskowych (bo nie znamy zawartości tablicy w trakcie uruchamiania skryptu):

FOR /L %%i IN (1,1,10) DO ECHO !tablica[%%i]!

Ot i cała filozofia:) Dzięki temu zabiegowi mamy w pełni funkcjonalną tablicę. Nie muszę nikogo przekonywać o korzyściach z tego płynących;). Przykłady „poważniejszych” zastosowań tej sztuczki pokażę przy okazji polecenia CALL, oraz IF. Na razie tyle musi nam wystarczyć.
     Ostatnim rozszerzeniem pętli FOR jest dodanie możliwości parsowania plików/ciągów znaków. Uzyskujemy ją poprzez dodania znaczka /F po słówku FOR. Generalnie taka pętla jest bardzo przydatna gdy chcemy np. przeglądać lub analizować logi systemowe. To, jak chcemy analizować podajemy przed inicjalizacją zmiennej sterującej, przy użyciu odpowiednich słów kluczowych. Pierwszym takim ciekawym słowem jest TOKENS. Co to takiego robi? W manualu czytamy: „określa tokeny, które mają być przekazywane  z każdego wiersza do głównego polecenia w każdej iteracji. Spowoduje to przydzielenie dodatkowych nazw zmiennych.”. Teraz to, co to oznacza zobaczmy na przykładzie. Załóżmy że mamy w pliku o nazwie spis.txt to, co wyrzuca na ekran polecenie DIR. Teraz tworząc taką pętlę:

FOR /F „tokens=1” %%i IN (spis.txt) DO ECHO %%i

Otrzymamy spis dat utworzenia naszych plików. Jak zapewne zauważysz , znalazły się tutaj również pierwsze wyrazy nagłówka listy plików, oraz na samym dole liczby określające ilość plików. Tak to właśnie działa. Tokens=1 oznacza pierwszy leksem (cały wyraz oddzielony białą spacją) w danym wierszu. Jeśli chcemy dodać jeszcze np. czas utworzenia pliku, to trzeba nam dodać jeden token, odpowiedzialny za kolejny leksem w wierszu:

FOR /F „tokens=1,2” %%i IN (spis.txt) DO ECHO %%i %%j

Pewnie zastanawia Cię ta dodatkowa zmienna. Nie ma w tym nic dziwnego – zmienna %%i zawiera ciąg znaków dla pierwszego tokena, zaś druga dla drugiego. Jest to jak najbardziej dozwolona konstrukcja. Musimy jednak pamiętać o tym, że dodatkowa zmienna MUSI mieć postać kolejnej litery alfabetu w stosunku do swej poprzedniczki. Czyli jeśli mamy np. trzy tokeny, a pierwszą zmienną jest %%a, to kolejnymi będą %%b i %%c. Lecz powróćmy do naszego przykładu. Możemy w nim zastosować jeszcze jedną „sztuczkę”. Załóżmy, że chcemy wyświetlić tylko daty utworzenia plików i folderów, oraz ich nazwy. Wiemy, że daty mają token=1, zaś nazwy mają token=4. Niestety, gdy użyjemy konstrukcji „tokens=1,4” to zauważymy, że nazwy plików są obcięte. Jest to spowodowane tym, że nazwy plików i katalogów zawierają w sobie znak białej spacji, więc dla naszej pętli FOR kolejne wyrazy nazwy pliku/folderu są oddzielnymi tokenami. W takim wypadku, musimy użyć dodatkowej zmiennej, która będzie przechowywać resztę ciągów znaków w danym wierszu. W takim wypadku, nasza pętla będzie wyglądać tak:

FOR /F „tokens=1,4*” %%i IN (spis.txt) DO ECHO %%i %%j %%k

Gwiazdka przy cyfrze 4 oznacza, że zainicjowana zostaje nowa zmienna (w naszym przypadku %%k), która będzie przechowywać ciąg znaków będący wszystkim tym co jest po tokenie czwartym. W naszym przypadku, ta zmienna zawierać będzie całą nazwę pliku/katalogu wraz z rozszerzeniem. Istnieje wiele opcji formatujących parsowanie. Na przykład opcja „eol=” pozwala ustalić znak, od którego zaczynające się wiersze będą ignorowane np. „eol=:” sprawi, że pętla będzie ignorowała wszystkie wiersze rozpoczynające się od dwukropka.
Kolejną ciekawą opcją jest opcja „skip=” która pozwala określić, od którego wiersza ma nastąpić parsowanie, licząc od początku pliku. Jeśli np. wpiszemy „skip=4” to pętla pominie pierwsze cztery wersy pliku/ciągu znaków.
Opcja „delims=” pozwala określić znaki ograniczników. Jeśli np. nie życzymy sobie, aby jakiś wiersz był parsowany po znaczku „<” to po prostu wpisujemy „delims=<”,co spowoduje, że dany wiersz zakończy się przed pierwszym wystąpieniem tego znaku.
Ostatnią z opcji jest opcja „usebackq”, która nie pobiera żadnych wartości, a jedynie umożliwia nam podawanie nazw plików w cudzysłowie, oraz poleceń w odwrotnych apostrofach. Jest to sprawa wygody. Dla przykładu, jeśli nie chcemy wpierw zapisywać do pliku wyniku działań polecenia FOR aby potem móc te plik parsować, możemy wykonać pewną sztuczkę. Otóż możemy wynik działania polecenia DIR zapisać w pamięci komputera, a następnie wykonywać działania. Stanie się tak, gdy zamiast nazwy pliku do parsowania wpiszemy w apostrofach prostych nazwę polecenia (wraz ze wszystkimi jego opcjami) np.:

FOR /F „tokens=1,4*” %%i IN ('DIR') DO ECHO %%i %%j %%k

Jeśli życzymy sobie, aby polecenia wpisywane były w odwrotnych apostrofach, użyjemy opcji “usebackq”:

FOR /F “tokens=1,4* usebackq” %%i IN (`DIR`) DO ECHO %%i %%j %%k

Ostatnim przykładem będzie program, który pobiera argument w postaci np. /C:jakaś_wartość i formatuje go do ciągu jakaś_wartość. Taki skrypt może wyglądać tak:

@ECHO OFF
REM Zalozmy, ze uzytkownik podal jako argument ciag 
REM /C:jakis_argument
REM Wyluskujemy interesujacy nas jakis_argument
FOR /F „tokens=1* delims=:” %%a IN („%1”) DO ECHO %%b
[dalszy ciag programu...]

Jak widać wyrzucamy na ekran wszystko to, co znajduje się po znaczku dwukropka ‘:’, wykorzystując „sztuczkę z gwiazdką”. Wzięcie zbioru wartości pętli w cudzysłów oznacza, że zbiór traktowany jest jako ciąg znaków. Jak widać pętla FOR równie dobrze funkcjonuje zarówno na plikach, jak i na samych łańcuchach znaków. Tyle, jeśli chodzi o rozszerzoną pętlę FOR. Teraz omówimy sobie instrukcje warunkową IF.

IF

     Instrukcję tę wykorzystywaliśmy już wcześniej przy omawianiu niektórych przykładów. Jak widać, nie dało się napisać czegokolwiek użytecznego bez jej użycia. Jest to bardzo ważna instrukcja, o czym postaram się Ciebie przekonać na poniższym przykładzie. Połączmy naszą możliwość zasymulowania typu tablicowego oraz instrukcję warunkową IF, do zaimplementowania prostego algorytmu wyznaczającego wartości maksymalną i minimalną. Skrypt taki może wyglądać tak:

@ECHO OFF
SETLOCAL
FOR /L %%i IN (1,1,10) DO SET tab[%%i]=!RANDOM!
SET min=%tab[1]%
SET max=%min%
FOR /L %%i IN (1,1,10) DO ECHO !tab[%%i]!
FOR /L %%i IN (2,1,10) DO IF !tab[%%i]! GTR !max! (
				SET max=!tab[%%i]!
			) ELSE (
				IF !tab[%%i]! LSS !min! SET min=!tab[%%i]!
			)
ECHO Max to %max%, a Min to %min%
ENDLOCAL

Tutaj wykorzystujemy instrukcję warunkową IF do porównania kolejnego elementu tablicy z wartością max i min. Wykorzystujemy do tego operatory porównania w wersji słownej. Wybrałem tą wersję, ponieważ wydaje mi się o wiele bardziej wyraźna.
Łamanie linii w tym programie jest jak najbardziej prawidłowe. Jak zapewne zauważyłeś, instrukcja warunkowa IF posiada klauzulę ELSE, co wykorzystaliśmy do stworzenia rozgałęzionej instrukcji warunkowej. Rozszerzona składnia umożliwia nam porównywanie łańcuchów znaków, nie zwracając uwagi na wielkość liter. Uzyskujemy to dzięki opcji /I. Wykorzystaliśmy to w rozdziale poświęconemu pętli FOR, w przykładzie programu poszukującego pliku.
     Często instrukcję warunkową IF wykorzystuje się do sprawdzenia wartości zmiennej środowiskowej. Jedną z takich zmiennych, jest zmienna środowiskowa CMDEXTVERSION. Zmienna ta przechowuje numer wersji interpretera poleceń CMD. Dlatego jeśli chcemy mieć pewność, że skrypt jest uruchomiony na komputerze z włączoną odpowiednią wersją interpretera poleceń, to możemy porównać wartość tej zmiennej środowiskowej z wartością jaką zwraca ona na naszym komputerze.
     Kolejnym, ostatnim udogodnieniem jest możliwość sprawdzenia, czy jakaś zmienna środowiskowa jest zdefiniowana. Służy do tego konstrukcja IF DEFINED. Dalszych zastosowań instrukcji warunkowych przedstawiać nie muszę, ponieważ jestem pewien że sam, drogi Czytelniku, znajdziesz dla niej odpowiednie zastosowania (jeśli nie to znajdziesz je w dalszej części artykułu;)).

CALL

     Kolejnym poleceniem jakim się zajmiemy, będzie polecenie CALL. Jest to polecenie, które w połączeniu z pętlą FOR i instrukcją warunkową IF daje naprawdę spore możliwości. Przy odpowiedniej konstrukcji bowiem możemy używać polecenia CALL jak wywołania procedury z zadanymi argumentami (lub bez nich).
     W poprzednich wersjach interpretera, polecenie CALL służyło do wywoływania innych plików wsadowych w obrębie jednego pliku. Rozszerzenie polecenia CALL polega na tym, że gdy chcemy uruchomić oddzielną procedurę, to nie musimy koniecznie umieszczać jej w oddzielnym pliku. Wystarczy jedynie wydzielić kod odpowiednią etykietą. Nazwa wywoływanej etykiety musi być poprzedzona dwukropkiem, po etykiecie natomiast możemy podać listę argumentów dla procedury. W procedurze odwołujemy się do argumentów tak, jakby była ona oddzielnym plikiem tj. pierwszy argument ma postać %1, drugi %2 itd. Aby lepiej zobrazować Tobie działanie polecenia CALL proponuję przykład. Będzie to skrypt symulujący działanie licznika kuchennego. Program pobiera od użytkownika ilość sekund, po których zostanie wyświetlony komunikat o upłynięciu żądanego czasu. Stosuję w nim poznane wcześniej konstrukcje FOR i IF. Taki skrypt może wyglądać tak (UWAGA! nie należy łamać linii w miejscu oznaczonym strzałką "->"!):

@ECHO OFF
CLS
SETLOCAL
SET /P koniec="Ilosc sekund do odliczenia: "
SET licznik=0
FOR /F "tokens=3 delims=:," %%a IN ("%TIME%") DO SET tm=%%a
:start
FOR /F "tokens=3 delims=:," %%a IN ("%TIME%") DO IF %tm% NEQ %%a CALL ->
:wyswietl %%a
IF %licznik% EQU %koniec% GOTO koniec_czasu
GOTO start

:wyswietl
CLS
SET tm=%1
SET /A licznik+=1
ECHO %licznik%
GOTO :EOF

:koniec_czasu
ENDLOCAL
ECHO Koniec czasu!

     Pojawiło się tutaj nowe polecenie GOTO :EOF. Jak wiemy, polecenie GOTO "skacze" do etykiety podanej jako argument polecenia. Etykieta :EOF to wynik rozszerzenia polecenia GOTO i oznacza po prostu koniec pliku. Dzięki temu nie musimy pisać na końcu pliku etykiety :koniec, bowiem zapis GOTO :EOF jest z nią równoznaczny. A teraz przejdźmy do analizy kodu. Jak widać procedura :wyświetl czyści ekran, ustala zmienną tymczasową tm na nową wartość, inkrementuje licznik i wyświetla jego stan. Po wykonaniu tego wszystkiego przechodzi do etykiety :EOF. Jest to konieczne, bowiem w przeciwnym wypadku procedura wykonywała by wszystko to, co znajduje się po niej. Gdy program wychodzi z procedury powraca do miejsca jej wywołania. Być może zwróciłeś uwagę, że korzystamy ze zmiennych, które wciąż się zmieniają (ulega zmianie ich wartość), a nie wykorzystujemy tutaj w ogóle opóźnionego rozwinięcia zmiennych środowiskowych. Dzieje się tak, ponieważ system dla polecenia CALL tworzy oddzielny wątek za każdym razem, gdy występuje odwołanie do etykiety. Dzięki temu, nasza procedura dysponuje wciąż uaktualnianą wartością zmiennych, bez konieczności używania opóźnionego rozwinięcia zmiennych środowiskowych.
     Generalnie, polecenie CALL w tej formie jest niezwykle przydatne, gdy np. w jednym kroku pętli FOR chcemy zrobić więcej niż jedną rzecz, bądź spełnienie/niespełnienie warunku powoduje wykonanie jakiegoś ciągu instrukcji.

SETLOCAL i ENDLOCAL

     Te polecenia omawiam razem, ponieważ działanie jednego jest przeciwieństwem działania drugiego. Są one bardzo przydatne, gdy w naszym skrypcie używamy zmiennych środowiskowych. Dzieje się tak dlatego, że po wywołaniu polecenia SETLOCAL informujemy system, że wszystkie zmienne które inicjujemy w programie mają zasięg lokalny. Jest to bardzo ważne, ponieważ jeśli zmienne będą inicjowane globalnie (bez użycia SETLOCAL), to po zakończeniu programu te zmienne będą wciąż dostępne. Teraz wyobraź sobie, że uruchamiasz kilka programów, które także pozostawiają po sobie w przestrzeni zmiennych środowiskowych swoje zmienne. Mamy zatem ładny bajzel.
Aby uchronić się przed tego typu sytuacjami, zawsze przed inicjowaniem jakiejkolwiek zmiennej wywołuj polecenie SETLOCAL no chyba, że rzeczywiście zależy Ci na zmianie jakiejś konkretnej zmiennej środowiskowej (np. PATHEXT lub PATH). Jeśli wywołaliśmy polecenie SETLOCAL i chcemy wyłączyć jego funkcje, to wywołujemy polecenie ENDLOCAL.
     Rozszerzona składnia poleceń SETLOCAL i ENDLOCAL umożliwia nam sprawdzenie wersji interpretera poleceń, oraz włączenie/wyłączenie opóźnionego rozwinięcia zmiennych środowiskowych. SETLOCAL, w odróżnieniu od wcześniejszych wersji interpretera, może pobierać argumenty. Wywołanie polecenia SETLOCAL z jakimkolwiek prawidłowym argumentem, ustawia zmienną środowiskową ERRORLEVEL na 0, w przeciwnym wypadku zmienna ERRORLEVEL ma wartość niezerową (1). Przykładowy kod może wyglądać tak:

@ECHO OFF
SETLOCAL ENABLEEXTENSIONS
IF ERRORLEVEL 1 ECHO Rozszerzenie nie dostepne!

Aby wyłączyć rozszerzenie poleceń wystarczy wywołać:

SETLOCAL DISABLEEXTENSIONS

Podobnie sprawa się ma z włączeniem lub wyłączeniem opóźnionego rozwinięcia zmiennych środowiskowych. Jeśli chcemy je włączyć, to wystarczy wywołać polecenie SETLOCAL z argumentem ENABLEDELAYEDEXPANSION. Natomiast jeśli zależy nam na wyłączeniu opóźnionego rozwinięcia, to wywołujemy polecenie SETLOCAL z argumentem DISABLEEXTENSIONS.
     Skoro zacząłem już pisać o sprawdzaniu wersji interpretera poleceń, to napiszę teraz nieco o sytuacji, w której zależy nam na poznaniu wersji systemu operacyjnego. Jest to ważne, gdy chcemy nasz skrypcik gdzieś opublikować. Wykorzystywanie powyższych przykładów sprawdzenia wersji interpretera jest skuteczne tylko w wypadku różnych wersji Windows XP, nie istnieje natomiast mechanizm dostępny np. dla Windows 98 lub 95. Istnieje jednak pewien sposób, który teraz przedstawię. Wykorzystamy w nim mechanizmy dostępne w starszych wersjach Windows tj. przetwarzanie potokowe, oraz polecenia VER i FIND. Dobrym pomysłem jest stworzenie oddzielnego pliku np. start.bat, który przed uruchomieniem właściwego programu będzie weryfikował wersję systemu. Taki plik może wyglądać tak:

@ECHO OFF
CLS
SETLOCAL
VER | FIND „XP” > NUL
IF NOT ERRORLEVEL 0 GOTO zla_wersja
CMD /E:ON /V:ON /Q /C nasz_program.bat [lista argumentow]
GOTO :koniec

:zla_wersja
ECHO Wymagany Windows XP!
	
:koniec
ENDLOCAL

Jak widać, działanie tego skryptu jest bardzo proste. Polecenie FIND szuka w łańcuchu znaków zwróconym przez polecenie VER (polecenie to wyświetla wersję systemu), ciągu „XP”. Jeśli znajdzie, ustala zmienną ERRORLEVEL na wartość 0, w przeciwnym razie zmienna ERRORLEVEL ma wartość różną od zera (w zależności od rodzaju błędu). Osobiście zalecam, aby takim skryptem poprzedzać wszystkie większe programy. Zawsze ładniej wygląda błąd w postaci napisu np. „Zła wersja systemu”, niż zbiór chaotycznie wyrzuconych zmiennych, poleceń i komentarzy.
     Dotychczas omówione polecenia i konstrukcje uznałem za najbardziej przydatne i ciekawe z punktu widzenia możliwości rozszerzonego Batcha. Można było dowiedzieć się, w jaki sposób opóźnione rozwijanie zmiennych środowiskowych wykorzystać do stworzenia sobie typu tablicowego, jak obliczać wyrażenia arytmetyczne, wywoływać procedury i wiele innych. Dzięki tym operacją możemy implementować sobie różne algorytmy, prowadzić interakcje z użytkownikiem, wykonywać operacje na strukturach danych itd.
Z pozostałymi poleceniami rozszerzonymi możesz zapoznać się w manualu systemowym. Nie opisuję ich, ponieważ nie chcę przepisywać dokumentacji, a same ich zastosowania są raczej wąskie więc będziesz je stosował w konkretnych sytuacjach.

     Mam nadzieję, że przedstawione przeze mnie polecenia i przykłady zainspirują Ciebie do zapoznania się z możliwościami jakie daje programowanie wsadowe w systemie Windows XP. Zależało mi na pokazaniu, jak korzystając z małego narzędzia można robić duże rzeczy. Wszystko to co udało nam się zrobić, jest wynikiem odpowiedniego użycia pewnych konstrukcji. Niekiedy metody bywały dość mętne, ale niestety jest to wynik ograniczonych możliwości samego narzędzia, którego braki musieliśmy sobie własnoręcznie uzupełniać. Dlatego nie jest to najprostsza rzecz ale uwierz, daje mnóstwo satysfakcji:) Zachęcam zatem do samodzielnego eksperymentowania.

r.Q

© by r.Q 2007 - 2008

Valid XHTML 1.0 Transitional Poprawny CSS!