1Interpreter poleceń cmd.exe

Czym jest Wiersz Poleceń?

Wiersz poleceń (cmd.exe) to domyślny interpreter poleceń w systemach operacyjnych z rodziny Windows NT (Windows XP, 7, 10, 11). Jest bezpośrednim następcą interpretera COMMAND.COM znanego z MS-DOS.

Jego głównym zadaniem jest interaktywne wykonywanie poleceń wpisywanych przez użytkownika oraz interpretowanie plików wsadowych (skryptów), czyli sekwencji poleceń zapisanych w pliku tekstowym.

  • Kontekst historyczny: Wywodzi się z potrzeby automatyzacji zadań w czasach, gdy interfejsy graficzne nie były standardem w systemach operacyjnych.
  • Ograniczenia: W porównaniu do nowoczesnych powłok jak BASH czy PowerShell, cmd.exe ma uboższą składnię i brak obiektowego podejścia.
  • Zastosowanie: Proste zadania administracyjne, skrypty logowania, automatyzacja uruchamiania aplikacji, procesy wdrożeniowe.
:: Uruchom wiersz poleceń (cmd.exe) i wpisz te komendy

:: Wyświetla wersję systemu Windows
ver

:: Wyświetla listę plików i katalogów
dir

:: Wyświetla konfigurację sieciową
ipconfig

:: Czyści ekran konsoli
cls

Interpreter cmd.exe jest podstawowym narzędziem do automatyzacji zadań w systemach Windows i pomimo ograniczonej składni wciąż znajduje szerokie zastosowanie w środowiskach korporacyjnych. Wielu administratorów rozpoczyna swoją przygodę z programowaniem właśnie od skryptów wsadowych, ponieważ ich składnia jest prosta i łatwa do opanowania. cmd.exe wywodzi się z ery MS-DOS, ale na przestrzeni lat doczekał się wielu udoskonaleń w kolejnych wersjach Windows. W codziennej pracy skrypty wsadowe są używane do wykonywania rutynowych czynności, takich jak kopiowanie plików, uruchamianie programów czy sprawdzanie stanu systemu. Mimo pojawienia się PowerShella, cmd.exe wciąż pozostaje domyślnym interpreterem w wielu organizacjach ze względu na kompatybilność wsteczną. Znajomość tego interpretera jest niezbędna dla każdego specjalisty IT pracującego w środowisku Windows.

Warto pamiętać, że cmd.exe oferuje znacznie więcej możliwości niż jego poprzednik COMMAND.COM, między innymi obsługę dłuższych nazw plików i rozszerzone opcje poleceń. Mimo tych udoskonaleń nowoczesne skrypty coraz częściej pisze się w PowerShellu, który oferuje obiektowe podejście do danych.

2Pliki .bat vs .cmd

Pliki wsadowe (Batch Files)

Skrypty dla interpretera cmd.exe zapisuje się w plikach tekstowych z rozszerzeniem .bat lub .cmd.

  • .bat (batch): Historyczne rozszerzenie z MS-DOS. Zapewnia maksymalną kompatybilność wsteczną.
  • .cmd (command): Wprowadzone w Windows NT. Funkcjonalnie niemal identyczne z .bat, ale posiada drobne ulepszenia w obsłudze błędów.

Różnice w praktyce są minimalne. Najważniejsza dotyczy zmiennej %ERRORLEVEL%. W plikach .cmd zmienna %ERRORLEVEL% jest zerowana (ustawiana na 0) po niektórych udanych poleceniach (takich jak PATH, SET, ASSOC), czego nie robią pliki .bat. W nowoczesnych systemach Windows zaleca się używanie rozszerzenia .cmd.

   +----------------------+
   | Plik tekstowy        |
   | (np. moj_skrypt.cmd) |
   +----------------------+
             |
             | Zawiera listę
             | poleceń
             v
   [ polecenie 1 ]
   [ polecenie 2 ]
   [ polecenie 3 ]
             |
             | Jest czytany linia
             | po linii przez...
             v
   +----------------------+
   | Interpreter cmd.exe   |
   +----------------------+
             |
             | ...który wykonuje
             | polecenia w systemie
             v
   +----------------------+
   |   System Operacyjny   |
   +----------------------+
            

Rozróżnienie między plikami .bat a .cmd ma znaczenie przede wszystkim historyczne i praktyczne dla programistów piszących skrypty wsadowe. Pliki .bat pochodzą jeszcze z czasów MS-DOS i są rozpoznawane przez wszystkie wersje systemu Windows, podczas gdy .cmd zostało wprowadzone wraz z Windows NT. Najważniejsza różnica dotyczy zachowania zmiennej %ERRORLEVEL%, która w plikach .cmd jest automatycznie zerowana po niektórych udanych poleceniach (takich jak PATH, SET, ASSOC), podczas gdy w .bat pozostaje wtedy niezmieniona. W praktyce codziennej pracy administratorzy rzadko przejmują się tą różnicą, ale warto o niej wiedzieć przy debugowaniu złożonych skryptów. Nowoczesne systemy Windows traktują oba rozszerzenia niemal identycznie. Wybierając rozszerzenie .cmd, sygnalizujemy, że skrypt został napisany z myślą o nowszych systemach Windows.

Niezależnie od wybranego rozszerzenia, zarówno pliki .bat, jak i .cmd są zwykłymi plikami tekstowymi, które można edytować dowolnym edytorem tekstu. Ważne jest jednak, aby zapisywać je z kodowaniem ANSI, ponieważ interpreter cmd.exe ma ograniczoną obsługę UTF-8, co może prowadzić do problemów z polskimi znakami diakrytycznymi.

3Nasz pierwszy skrypt: "Witaj, świecie!"

Struktura podstawowego skryptu

Utwórz plik witaj.cmd w Notatniku. Zapisz go z kodowaniem ANSI, aby uniknąć problemów z polskimi literami.

  • @echo off: Wyłącza wyświetlanie poleceń, pokazując tylko wyniki. Znak @ ukrywa samo polecenie echo off.
  • REM lub ::: Służy do komentarzy. REM to oficjalne polecenie, :: to popularny nieudokumentowany trick (interpreter traktuje to jako etykietę i ignoruje).
  • echo.: Wyświetla pustą linię.
  • pause: Zatrzymuje wykonanie i czeka na naciśnięcie klawisza.
@echo off
REM To jest moj pierwszy skrypt wsadowy.
:: Uzywam obu typow komentarzy dla demonstracji.

echo Witaj, swiecie!
echo.
echo To jest prosty skrypt demonstrujacy podstawowe komendy.
echo.

echo Nacisnij dowolny klawisz, aby zakończyć...
pause

Tworzenie pierwszego skryptu wsadowego to doskonały sposób na zrozumienie podstawowych mechanizmów działania interpretera cmd.exe. Polecenie @echo off jest standardowym otwarciem każdego skryptu, ponieważ zapobiega wyświetlaniu samych poleceń na ekranie i czyni wynik czytelniejszym dla użytkownika. Komentarze w skryptach wsadowych można zapisywać na dwa sposoby, przy czym :: jest szybsze od REM, ponieważ interpreter traktuje je jako etykietę, którą po prostu pomija. Polecenie echo z kropką służy do wyświetlania pustej linii, co jest przydatne przy formatowaniu komunikatów na ekranie. Polecenie pause zatrzymuje wykonanie skryptu i wyświetla zachętę do naciśnięcia dowolnego klawisza. Dobrą praktyką jest zawsze umieszczanie pause na końcu skryptów testowych, aby zobaczyć wyniki przed zamknięciem okna konsoli.

W profesjonalnych skryptach wsadowych zamiast pause stosuje się często timeout /t -1, który czeka na naciśnięcie klawisza (podobnie jak pause, również wyświetla komunikat). Aby ukryć komunikat, stosuje się przekierowanie: timeout /t -1 >nul. Pozwala to na zachowanie pełnej kontroli nad interfejsem użytkownika skryptu.

4Uruchamianie skryptu i dobre praktyki

Jak uruchomić skrypt?

  1. Poprzez dwukrotne kliknięcie: W Eksploratorze Windows kliknij dwukrotnie plik .cmd lub .bat.
  2. Z wiersza poleceń: Otwórz cmd.exe, przejdź do katalogu i wpisz nazwę skryptu.
  3. Z uprawnieniami administratora: Kliknij prawym przyciskiem myszy i wybierz "Uruchom jako administrator".
  4. Z planowania zadań (Task Scheduler): Automatyczne uruchamianie o określonych porach.

Dobre praktyki: setlocal / endlocal

Zmienne tworzone poleceniem set są domyślnie widoczne w całej sesji konsoli. Po zakończeniu skryptu pozostają i mogą zaśmiecać środowisko.

  • setlocal: Tworzy lokalny zasięg dla zmiennych.
  • endlocal: Przywraca środowisko do stanu sprzed setlocal.
@echo off
setlocal
:: Wszystkie zmienne beda lokalne

title Moj Pierwszy Skrypt

echo Sprawdzam aktualna zmienna TEST_VAR...
echo Wartosc: %TEST_VAR%
echo.

set TEST_VAR=To jest wartość ze skryptu
echo Ustawilem TEST_VAR na: %TEST_VAR%
echo.

echo Po zakończeniu TEST_VAR powróci do poprzedniego stanu.
pause

endlocal
:: Od tego momentu TEST_VAR wraca do oryginalnej wartości.

Uruchamianie skryptów wsadowych w systemie Windows jest intuicyjne i nie wymaga nadawania specjalnych uprawnień, w przeciwieństwie do systemów Linux. Dwukrotne kliknięcie pliku .cmd w Eksploratorze Windows uruchamia go w domyślnym interpreterze cmd.exe, ale uwaga – skrypt wykona się w katalogu profilu użytkownika, a nie tam, gdzie znajduje się plik. Uruchamianie z uprawnieniami administratora jest kluczowe przy operacjach wymagających modyfikacji systemowych plików lub rejestru. Użycie setlocal na początku skryptu jest jedną z najważniejszych dobrych praktyk, ponieważ zapobiega zaśmiecaniu środowiska zmiennymi po zakończeniu skryptu. Planowanie zadań pozwala na uruchamianie skryptów o określonych porach, co jest przydatne przy automatyzacji zadań konserwacyjnych. Warto pamiętać, że skrypty uruchomione przez Task Schedulera działają w tle, bez widocznego okna konsoli.

Różne metody uruchamiania skryptów mają wpływ na dostępne uprawnienia i środowisko wykonawcze. Skrypty uruchomione z sieciowego udostępnienia mogą wymagać dodatkowej konfiguracji zabezpieczeń, ponieważ nowoczesne wersje Windows domyślnie blokują wykonywanie skryptów z nieznanych źródeł.

5Zmienne: Deklaracja i dostęp

Przechowywanie danych

Zmienne w skryptach wsadowych to zmienne środowiskowe. Przechowują dane w formie tekstowej.

Deklaracja: polecenie set

Do tworzenia i przypisywania wartości służy polecenie set. Wokół znaku = nie może być spacji!

  • set ZMIENNA=jakaś wartość
  • set "ZMIENNA=jakaś wartość" - zalecana forma z cudzysłowami

Dostęp do wartości: %ZMIENNA%

Nazwę zmiennej otacza się znakami procentu. Interpreter zamienia %NAZWA% na wartość. Proces ten to rozszerzanie (expansion).

Wyświetlanie wszystkich zmiennych

Polecenie set bez argumentów wyświetla listę wszystkich zmiennych środowiskowych.

@echo off
setlocal

set USER_NAME=JanKowalski
set LOG_FILE=C:\Logs\%USER_NAME%.log
set APP_VERSION=1.2.3

echo Witaj, %USER_NAME%!
echo.
echo Wersja aplikacji: %APP_VERSION%
echo Sciezka do logu: %LOG_FILE%
echo.

:: Laczenie zmiennych
set GREETING=Czesc, %USER_NAME%!
echo %GREETING%
echo.

:: Sprawdzenie czy zmienna istnieje
if defined USER_NAME (
    echo Zmienna USER_NAME jest zdefiniowana.
)

:: Usuwanie zmiennej
set USER_NAME=
if not defined USER_NAME (
    echo Zmienna USER_NAME została usunięta.
)

pause
endlocal

Zmienne w skryptach wsadowych są zawsze tekstowe, co oznacza, że wartości liczbowe są również przechowywane jako ciągi znaków. Polecenie set jest podstawowym narzędziem do pracy ze zmiennymi, a jego składnia wymaga szczególnej uwagi przy znaku równości. Forma z cudzysłowem wokół całego przypisania jest zalecana, ponieważ zapobiega problemom z ukrytymi spacjami na końcu wartości. Dostęp do wartości zmiennej następuje przez otoczenie jej nazwy znakami procentu, co nazywa się rozszerzaniem zmiennej. Polecenie set bez argumentów wyświetla wszystkie zmienne środowiskowe, co jest przydatne przy debugowaniu. Sprawdzanie, czy zmienna istnieje za pomocą if defined, pozwala uniknąć błędów przy próbie użycia niezdefiniowanej zmiennej. Usunięcie zmiennej następuje przez przypisanie jej pustej wartości za pomocą set ZMIENNA=.

W nowoczesnych skryptach wsadowych warto pamiętać o ograniczeniu długości nazw zmiennych i wartości – maksymalna długość pojedynczej zmiennej środowiskowej wynosi 32767 znaków, choć w praktyce rzadko osiąga się ten limit.

6Zmienne lokalne i globalne - szczegółowa analiza

Zasięg zmiennych (Scope)

W skryptach wsadowych rozróżniamy dwa typy zmiennych ze względu na zasięg:

  • Zmienne globalne:
    • Domyślnie wszystkie zmienne tworzone set bez setlocal są globalne.
    • Widoczne w całej sesji konsoli cmd.exe.
    • Pozostają po zakończeniu skryptu.
    • Mogą kolidować z innymi skryptami.
  • Zmienne lokalne:
    • Tworzone wewnątrz bloku setlocal...endlocal.
    • Istnieją tylko w obrębie tego bloku lub skryptu.
    • Po zakończeniu są automatycznie usuwane.
    • Pozwalają na bezpieczne używanie dowolnych nazw.

UWAGA: Bezpieczna wartość domyślna to zawsze umieszczać setlocal na początku każdego skryptu!

@echo off
setlocal

:: Zmienna globalna - zdefiniowana PRZED setlocal
set GLOBAL_VAR=Treść globalna

:: Zmienna lokalna - zdefiniowana PO setlocal
set LOKALNA_VAR=Treść lokalna

echo === Wewnątrz skryptu ===
echo GLOBAL_VAR = %GLOBAL_VAR%
echo LOKALNA_VAR = %LOKALNA_VAR%
echo.

:: Demonstracja zagnieżdżania setlocal
setlocal
    set LICZNIK=200
    echo === Wewnątrz zagnieżdżonego setlocal ===
    echo LICZNIK = %LICZNIK%
endlocal

echo Po zagnieżdżonym endlocal:
echo LICZNIK = %LICZNIK%

pause
:: Po zakończeniu skryptu zmienne lokalne zostaną usunięte
endlocal

Zrozumienie różnicy między zmiennymi lokalnymi a globalnymi jest kluczowe dla pisania bezpiecznych i przewidywalnych skryptów wsadowych. Zmienne globalne, tworzone bez użycia setlocal, pozostają w pamięci po zakończeniu skryptu i mogą wpływać na działanie innych programów. Jest to szczególnie niebezpieczne w środowiskach serwerowych, gdzie równolegle może działać wiele skryptów. Zmienne lokalne, deklarowane wewnątrz bloku setlocal...endlocal, są automatycznie usuwane, co zapobiega konfliktom nazw. Zagnieżdżanie setlocal pozwala na tworzenie hierarchicznych poziomów widoczności zmiennych. Stosowanie setlocal na początku każdego skryptu powinno być standardem, nawet w najprostszych skryptach. Mechanizm setlocal jest szczególnie ważny przy wywoływaniu jednego skryptu z drugiego, ponieważ zapobiega przypadkowemu nadpisaniu zmiennych.

Warto pamiętać, że setlocal ma również dodatkowe opcje, takie jak EnableDelayedExpansion, które rozszerzają możliwości skryptów. Jednak podstawowe setlocal bez opcji jest wystarczające do zarządzania zasięgiem zmiennych w większości przypadków.

7Przekazywanie zmiennych przez endlocal & set

Problem: Zmienna lokalna nie wraca na zewnątrz

Standardowo zmienne utworzone wewnątrz setlocal...endlocal nie są widoczne po zakończeniu bloku. Czasami potrzebujemy, aby niektóre zmienne "przetrwały".

Rozwiązanie: Sztuczka z ENDLOCAL & SET

Specjalna konstrukcja endlocal & set "ZMIENNA=%ZMIENNA%" pozwala na przekazanie wartości na zewnątrz.

Ważne: Zmienne są pobierane PRZED wykonaniem endlocal, ponieważ interpreter przetwarza całą linię przed jej wykonaniem.

@echo off
setlocal

:: Obliczamy jakas wartosc
set CALC_RESULT=0
set /a CALC_RESULT=15 * 3

echo Wewnątrz setlocal: CALC_RESULT = %CALC_RESULT%
echo.

:: Przekazanie wartosci na zewnatrz
:: Ta linia jest kluczowa!
endlocal & set "CALC_RESULT=%CALC_RESULT%"

:: Po zakończeniu skryptu zmienna nadal istnieje
echo Po endlocal: CALC_RESULT = %CALC_RESULT%
echo.

:: Przekazywanie wielu zmiennych naraz
setlocal
set VAR1=Wartosc 1
set VAR2=Wartosc 2
set VAR3=Wartosc 3
echo Wewnątrz: %VAR1%, %VAR2%, %VAR3%

endlocal & set "VAR1=%VAR1%" & set "VAR2=%VAR2%" & set "VAR3=%VAR3%"
echo Po endlocal: %VAR1%, %VAR2%, %VAR3%

pause

Sztuczka z endlocal & set to jedna z najbardziej zaawansowanych technik w skryptach wsadowych, pozwalająca na przekazanie wartości zmiennej lokalnej poza blok setlocal. Działa ona dzięki temu, że interpreter cmd.exe przetwarza całą linię przed jej wykonaniem, więc wartość zmiennej jest pobierana przed wykonaniem endlocal. Jest to szczególnie przydatne w funkcjach (podprogramach), które muszą zwrócić obliczoną wartość do głównego skryptu. Można w ten sposób przekazać wiele zmiennych jednocześnie, łącząc je operatorem &. Technika ta wymaga jednak ostrożności, ponieważ łatwo o błąd przy nieprawidłowym cytowaniu. W praktyce endlocal & set jest używane w zaawansowanych skryptach do zarządzania konfiguracją i stanem. Zrozumienie tego mechanizmu jest ważnym krokiem w nauce profesjonalnego programowania w CMD.

Alternatywnym rozwiązaniem jest użycie zmiennych globalnych, ale w dłuższych skryptach prowadzi to do bałaganu i trudności w utrzymaniu kodu. Dlatego endlocal & set pozostaje preferowaną metodą w profesjonalnych skryptach wsadowych.

8Zmienne: Wczytywanie danych od użytkownika

Polecenie set /p

Aby skrypty były interaktywne, możemy prosić użytkownika o wprowadzenie danych. Służy do tego polecenie set z przełącznikiem /p (prompt).

Składnia: set /p ZMIENNA=Tekst zachęty:

  • ZMIENNA: Nazwa zmiennej, w której zostaną zapisane dane.
  • set /p ZMIENNA=Tekst zachęty: : Opcjonalny tekst wyświetlany użytkownikowi.

Skrypt zatrzyma się i będzie czekał, aż użytkownik wpisze tekst i naciśnie Enter.

Walidacja danych wejściowych

Zawsze sprawdzaj, czy użytkownik faktycznie coś podał!

@echo off
setlocal

echo === Kreator raportu ===
echo.

:: Prosba o nazwe serwera
set /p SERVER_NAME=Podaj nazwe serwera: 

:: Sprawdz czy uzytkownik cos podal
if not defined SERVER_NAME (
    echo Blad: Nie podano nazwy serwera!
    goto :koniec
)

:: Prosba o adres IP
set /p SERVER_IP=Podaj adres IP serwera %SERVER_NAME%: 

:: Prosta walidacja
echo %SERVER_IP% | findstr /r "^[0-9.]*$" > nul
if errorlevel 1 (
    echo Uwaga: Adres IP moze byc niepoprawny.
)

echo.
echo --- Podsumowanie ---
echo Nazwa serwera: %SERVER_NAME%
echo Adres IP:      %SERVER_IP%
echo --------------------

:koniec
pause
endlocal

Polecenie set /p umożliwia tworzenie interaktywnych skryptów, które mogą dostosowywać swoje działanie do wprowadzonych danych. Jest to podstawowy mechanizm komunikacji z użytkownikiem w skryptach wsadowych, ponieważ cmd.exe nie oferuje bardziej zaawansowanych interfejsów. Ważne jest, aby zawsze walidować dane wprowadzone przez użytkownika, ponieważ mogą one zawierać błędy lub być celowo nieprawidłowe. Sprawdzenie za pomocą if not defined pozwala wykryć, czy użytkownik faktycznie coś wpisał, czy po prostu nacisnął Enter. Walidacja formatu danych za pomocą findstr z wyrażeniem regularnym pozwala na sprawdzenie, czy adres IP lub inna wartość ma oczekiwany format. W skryptach produkcyjnych warto również zabezpieczyć się przed zbyt długimi danymi wejściowymi. Interaktywne skrypty z set /p są często używane w narzędziach wdrożeniowych i instalacyjnych.

Nowoczesne skrypty wsadowe coraz częściej rezygnują z interakcji z użytkownikiem na rzecz przyjmowania parametrów z linii poleceń. Takie podejście ułatwia automatyzację i umożliwia uruchamianie skryptów bez nadzoru człowieka.

9Zmienne wbudowane (dynamiczne)

Przydatne zmienne dostarczane przez system

Interpreter cmd.exe udostępnia "magiczne" zmienne, które są generowane dynamicznie w momencie odwołania.

Najważniejsze zmienne dynamiczne:

  • %CD%: Ścieżka do bieżącego katalogu (Current Directory).
  • %DATE%: Bieżąca data w formacie systemowym.
  • %TIME%: Bieżący czas.
  • %RANDOM%: Losowa liczba od 0 do 32767.
  • %ERRORLEVEL%: Kod wyjścia ostatnio wykonanego programu.
  • %CMDCMDLINE%: Pełna linia poleceń uruchomienia.
  • %CMDEXTVERSION%: Numer wersji rozszerzeń cmd.exe.
@echo off
setlocal

echo Skrypt uruchomiony w katalogu:
echo %CD%
echo.

echo Aktualna data i godzina:
echo Data: %DATE%
echo Czas: %TIME%
echo.

echo Losowa liczba: %RANDOM%
echo Losowa z zakresu 1-100:
set /a LOSOWA = (RANDOM %% 100) + 1
echo %LOSOWA%
echo.

:: Sprawdzenie ERRORLEVEL
dir > nul
echo Errorlevel po 'dir': %ERRORLEVEL%

dir C:\nie\ma\takiego\pliku.txt > nul 2> nul
echo Errorlevel po błędnym 'dir': %ERRORLEVEL%

echo.
echo Linia polecen: %CMDCMDLINE%

pause
endlocal

Zmienne dynamiczne w cmd.exe są automatycznie aktualizowane przez interpreter w momencie odczytu, co odróżnia je od zwykłych zmiennych środowiskowych. Zmienna %RANDOM% generuje liczbę pseudolosową i jest przydatna do tworzenia unikalnych nazw plików tymczasowych lub prostych systemów losowania. %DATE% i %TIME% zwracają aktualny czas systemowy, ale ich format zależy od ustawień regionalnych, co może powodować problemy z przenośnością skryptów. %ERRORLEVEL% przechowuje kod wyjścia ostatnio wykonanego polecenia i jest fundamentem obsługi błędów w skryptach wsadowych. Zmienna %CD% zawsze wskazuje bieżący katalog roboczy i jest przydatna przy operacjach na plikach. %CMDCMDLINE% i %CMDEXTVERSION% dostarczają informacji o samym interpreterze, co bywa pomocne przy debugowaniu. Znajomość tych zmiennych znacząco ułatwia tworzenie zaawansowanych skryptów.

Warto pamiętać, że zmienne dynamiczne nie są widoczne w wywołaniu set bez argumentów, ponieważ są generowane na żądanie. Można je jednak swobodnie używać w skryptach tak jak zwykłe zmienne, otaczając ich nazwy znakami procentu.

10Problem: Rozszerzanie zmiennych w blokach kodu

Pułapka natychmiastowego rozszerzania

Interpreter cmd.exe ma kłopotliwą cechę: rozszerza wszystkie zmienne (%VAR%) w całej linii lub bloku kodu (pętla for, nawiasy if) w momencie, gdy zaczyna go czytać, a nie wykonuje.

Jeśli zmienisz wartość zmiennej wewnątrz pętli i spróbujesz ją odczytać za pomocą %...%, zobaczysz jej wartość sprzed wejścia do pętli.

W przykładzie obok, mimo że licznik jest inkrementowany, echo %LICZNIK% zawsze wyświetli 0.

@echo off
setlocal

set LICZNIK=0
echo Przed petla, LICZNIK = %LICZNIK%
echo.

echo Proba inkrementacji w petli FOR:
for /L %%i in (1,1,5) do (
    set /a LICZNIK+=1
    echo Wewnątrz petli, iteracja %%i. LICZNIK to wciaz: %LICZNIK%
)
echo.

echo Po petli, LICZNIK = %LICZNIK%

pause
endlocal

:: --- Oczekiwany (ale błędny) wynik ---
:: Przed petla, LICZNIK = 0
:: Wewnątrz petli... LICZNIK to wciaz: 0
:: Wewnątrz petli... LICZNIK to wciaz: 0
:: ... (zawsze 0)
:: Po petli, LICZNIK = 0 (lub %LICZNIK%)

Problem natychmiastowego rozszerzania zmiennych jest jednym z największych wyzwań dla programistów piszących skrypty wsadowe. Interpreter cmd.exe czyta cały blok kodu (na przykład pętlę for lub blok if w nawiasach) przed jego wykonaniem, rozwijając wszystkie zmienne %VAR% do ich aktualnych wartości. Oznacza to, że zmiana wartości zmiennej wewnątrz pętli nie jest widoczna, dopóki pętla się nie zakończy. Jest to szczególnie mylące dla programistów przychodzących z innych języków, gdzie zmienne są odczytywane w momencie wykonania. Problem ten dotyczy wszystkich konstrukcji ujętych w nawiasy, w tym pętli for i bloków if-else. W przykładzie z licznikiem widać, że mimo inkrementacji wartość odczytywana w pętli zawsze wynosi 0. Zrozumienie tego mechanizmu jest kluczowe dla poprawnego projektowania skryptów wsadowych.

Rozwiązaniem tego problemu jest opóźnione rozszerzanie zmiennych, które zostanie szczegółowo omówione na następnym slajdzie. Warto zapamiętać zasadę: jeśli zmieniasz wartość zmiennej wewnątrz pętli, użyj wykrzykników zamiast procentów.

11Rozwiązanie: Opóźnione rozszerzanie zmiennych

setlocal EnableDelayedExpansion

Aby rozwiązać problem z poprzedniego slajdu, włączamy opóźnione rozszerzanie zmiennych (delayed expansion). Zmienne otoczone wykrzyknikami (!VAR!) są rozszerzane w momencie wykonania, nie parsowania bloku.

Jak tego używać?

  1. Na początku skryptu dodaj: setlocal EnableDelayedExpansion.
  2. Wewnątrz bloków kodu używaj: !NAZWA_ZMIENNEJ!.

Poza blokami kodu nadal można używać %...%.

@echo off
setlocal EnableDelayedExpansion

set LICZNIK=0
echo Przed petla, LICZNIK = %LICZNIK%
echo.

echo Proba inkrementacji w petli FOR (z opóźnionym rozszerzaniem):
for /L %%i in (1,1,5) do (
    set /a LICZNIK+=1
    echo Wewnątrz petli, iteracja %%i. LICZNIK to teraz: !LICZNIK!
)
echo.

echo Po petli, LICZNIK = !LICZNIK!

pause
endlocal

:: --- Poprawny wynik ---
:: Przed petla, LICZNIK = 0
:: Wewnątrz petli... LICZNIK to teraz: 1
:: Wewnątrz petli... LICZNIK to teraz: 2
:: ...
:: Po petli, LICZNIK = 5

Opóźnione rozszerzanie zmiennych to jedna z najważniejszych funkcji cmd.exe, która znacząco rozszerza możliwości skryptów wsadowych. Włącza się je przez dodanie opcji EnableDelayedExpansion do polecenia setlocal, co aktywuje alternatywny sposób odczytywania zmiennych. Zmienne otoczone wykrzyknikami (!VAR!) są rozwijane w momencie wykonania linii, a nie w momencie parsowania bloku kodu. Dzięki temu można odczytać wartość zmiennej, która została zmieniona wewnątrz pętli lub bloku warunkowego. Poza blokami kodu można nadal używać standardowych procentów. Opóźnione rozszerzanie jest niezbędne przy przetwarzaniu plików w pętlach for, gdzie każda iteracja może zmieniać stan zmiennych. W praktyce zaleca się włączanie opóźnionego rozszerzania w każdym skrypcie, który używa pętli lub modyfikuje zmienne wewnątrz bloków kodu.

Należy jednak pamiętać, że opóźnione rozszerzanie ma pewne skutki uboczne, na przykład zmienia sposób interpretacji wykrzykników w wartościach zmiennych. Dlatego w prostych skryptach, które nie modyfikują zmiennych w blokach, można go nie włączać.

12Wyrażenia arytmetyczne (set /a)

Operacje na liczbach całkowitych

Standardowe polecenie set służy do operacji na tekście. Do obliczeń matematycznych używamy przełącznika /a (arithmetic).

Składnia: set /a ZMIENNA=wyrażenie

  • Wewnątrz wyrażenia nie trzeba używać znaków % do zmiennych.
  • Dostępne operatory: + (dodawanie), - (odejmowanie), * (mnożenie), / (dzielenie całkowite), % (modulo).
  • Można używać nawiasów () do grupowania.

Uwaga: cmd.exe domyślnie operuje na 32-bitowych liczbach całkowitych. Maksimum: 2147483647.

@echo off
setlocal

set A=20
set B=7

set /a SUMA = A + B
set /a ILOCZYN = A * B
set /a ILORAZ = A / B
set /a RESZTA = A %% B

echo %A% + %B% = %SUMA%
echo %A% * %B% = %ILOCZYN%
echo %A% / %B% = %ILORAZ% (dzielenie calkowite)
echo Reszta z dzielenia %A% przez %B% to %RESZTA%
echo.

:: Inkrementacja i dekrementacja
set LICZNIK=10
echo Pierwotna wartość: %LICZNIK%
set /a LICZNIK+=1
echo Po inkrementacji: %LICZNIK%
set /a LICZNIK-=5
echo Po dekrementacji o 5: %LICZNIK%
echo.

:: Operacje zlozone
set X=5
set /a WYNIK = (X + 3) * 2 - 10 / 2
echo (5 + 3) * 2 - 10 / 2 = %WYNIK%

pause
endlocal

Operacje arytmetyczne w cmd.exe są możliwe dzięki przełącznikowi /a polecenia set, który interpretuje przypisaną wartość jako wyrażenie matematyczne. Mimo że zmienne są przechowywane jako tekst, wewnątrz wyrażenia /a są traktowane jako liczby całkowite. Warto pamiętać, że cmd.exe operuje na 32-bitowych liczbach całkowitych, co oznacza maksymalną wartość nieco ponad 2 miliardy. Dzielenie w cmd.exe jest zawsze całkowite, a resztę można uzyskać za pomocą operatora modulo. Nawiasy okrągłe służą do grupowania wyrażeń i zmiany kolejności działań. Operatory += i -= pozwalają na wygodną inkrementację i dekrementację zmiennych. W praktyce wyrażenia arytmetyczne są używane do zliczania iteracji, obliczania rozmiarów plików czy tworzenia prostych kalkulatorów.

W przypadku potrzeby bardziej zaawansowanych obliczeń, na przykład zmiennoprzecinkowych, warto rozważyć użycie PowerShella lub zewnętrznych narzędzi. cmd.exe nie oferuje natywnego wsparcia dla liczb zmiennoprzecinkowych.

13Operacje na ciągach znaków: Podciągi

Wycinanie fragmentów tekstu

Skrypty wsadowe oferują prostą, ale potężną składnię do wycinania fragmentów (podciągów) ze zmiennych tekstowych.

Składnia

  • %ZMIENNA:~start%: Wycina od znaku o indeksie `start` do końca. Numeracja od 0.
  • %ZMIENNA:~start,długość%: Wycina podciąg o `długości`, zaczynając od `start`.
  • Ujemne indeksy: Liczy od końca ciągu. Np. -4 to cztery ostatnie znaki.

Praktyczne zastosowania

  • Wycinanie roku, miesiąca, dnia z daty systemowej
  • Usuwanie rozszerzenia z nazwy pliku
  • Ekstrakcja fragmentów ścieżek
@echo off
setlocal EnableDelayedExpansion

set NAZWA_PLIKU=raport_dzienny_2025-10-09.log

echo Pelna nazwa pliku: %NAZWA_PLIKU%
echo Wartosc zmiennej: !NAZWA_PLIKU!
echo.

:: Pierwsze 6 znakow ("raport")
set TYP=%NAZWA_PLIKU:~0,6%
echo Pierwsze 6 znakow: %TYP%

:: Fragment daty (10 znakow, od 15.)
set DATA=%NAZWA_PLIKU:~15,10%
echo Fragment daty: %DATA%

:: Ostatnie 4 znaki (rozszerzenie ".log")
set ROZSZ=%NAZWA_PLIKU:~-4%
echo Rozszerzenie: %ROZSZ%

:: Wszystko oprocz rozszerzenia
set BEZ_ROZSZ=%NAZWA_PLIKU:~0,-4%
echo Nazwa bez rozszerzenia: %BEZ_ROZSZ%

pause
endlocal

Operacje na podciągach w cmd.exe pozwalają na wycinanie fragmentów tekstu ze zmiennych, co jest niezwykle przydatne przy przetwarzaniu ścieżek plików, dat czy innych sformatowanych danych. Składnia %ZMIENNA:~start,długość% jest elastyczna i pozwala na używanie ujemnych indeksów do liczenia od końca ciągu. Indeksy numerowane są od zera, co jest zgodne z konwencją większości języków programowania. Praktyczne zastosowania obejmują wycinanie daty ze zmiennej %DATE%, usuwanie rozszerzeń plików czy ekstrakcję fragmentów ścieżek. Ujemne wartości start pozwalają na wygodne pobieranie ostatnich znaków ciągu, na przykład rozszerzenia pliku. Operacje na podciągach są często łączone z podstawianiem wyniku do nowej zmiennej. Znajomość tej składni jest niezbędna przy przetwarzaniu danych tekstowych w skryptach wsadowych.

Warto eksperymentować z różnymi kombinacjami indeksów, ponieważ nieprawidłowe wartości (na przykład start poza zakresem) nie powodują błędu, a jedynie zwracają pusty ciąg. Ułatwia to tworzenie bezpiecznych operacji bez konieczności sprawdzania długości ciągu.

14Operacje na ciągach znaków: Zamiana

Wyszukiwanie i zamiana tekstu

Możliwa jest operacja wyszukiwania i zamiany fragmentu tekstu wewnątrz zmiennej.

Składnia

  • %ZMIENNA:szukany=zamiennik%: Zamienia wszystkie wystąpienia.
  • %ZMIENNA:*szukany=zamiennik%: Zamienia wszystko od początku ciągu do pierwszego wystąpienia szukany włącznie.
  • Aby usunąć ciąg, zostaw pusty zamiennik: %ZMIENNA:usun_to=%.
@echo off
setlocal EnableDelayedExpansion

set SCIEZKA=C:\Uzytkownicy\Jan\Dokumenty\Projekt X
echo Oryginalna ścieżka: %SCIEZKA%
echo.

:: Zamien spacje na podkreslenia
set NOWA=!SCIEZKA: =_!
echo Po zamianie spacji: !NOWA!

:: Zamien tylko pierwsze 'U' na 'u'
set ZMIENIONA=!SCIEZKA:U=u!
echo Po zamianie pierwszego U: !ZMIENIONA!
echo.

set ZDANIE=To jest test, testowy test.
echo Oryginalne zdanie: !ZDANIE!

:: Zamien pierwsze wystąpienie 'test' na 'przyklad' (od początku)
set NOWE=!ZDANIE:*test=przyklad!
echo Nowe zdanie (pierwsze od początku): !NOWE!

:: Zamien wszystkie 'test' na 'przyklad'
set WSZYSTKIE=!ZDANIE:test=przyklad!
echo Nowe zdanie (wszystkie): !WSZYSTKIE!

:: Usuwanie tekstu
set TEKST=Prefix_Tresc_Suffix
echo.
echo Oryginalny tekst: !TEKST!
echo Bez Prefix_: !TEKST:Prefix_=!
echo Bez _Suffix: !TEKST:_Suffix=!

pause
endlocal

Operacja zamiany tekstu w cmd.exe to potężne narzędzie do transformacji danych, które działa na poziomie pojedynczej zmiennej. Składnia %ZMIENNA:szukany=zamiennik% zamienia wszystkie wystąpienia podanego wzorca na nowy tekst, co jest przydatne przy czyszczeniu lub formatowaniu danych. Wariant z gwiazdką (*) na początku wzorca zamienia wszystko od początku ciągu do pierwszego wystąpienia wzorca włącznie. Aby usunąć fragment tekstu, wystarczy pozostawić pusty zamiennik po znaku równości. Operacja zamiany jest często używana do zastępowania spacji podkreślnikami w nazwach plików lub do usuwania niepotrzebnych prefiksów i sufiksów. W praktyce zamiana tekstu sprawdza się znakomicie przy przetwarzaniu danych z plików konfiguracyjnych. Mechanizm ten jest jednak ograniczony do prostych wzorców i nie obsługuje wyrażeń regularnych.

W przypadku potrzeby bardziej zaawansowanych transformacji tekstu, takich jak zamiana z użyciem wzorców, warto sięgnąć po narzędzia zewnętrzne, takie jak sed dla Windows lub PowerShell. cmd.exe pozwala tylko na dosłowne dopasowanie wzorca.

15Instrukcja if: Porównania i istnienie plików

Podejmowanie decyzji w skrypcie

Instrukcja if pozwala na wykonanie polecenia (lub bloku) tylko gdy warunek jest spełniony.

Podstawowe zastosowania

  • Porównywanie ciągów:
    • if "%ZMIENNA1%"=="%ZMIENNA2%" (polecenie)
    • /I - ignoruje wielkość liter
    • Zawsze otaczaj zmienne w cudzysłowach!
  • Sprawdzanie istnienia:
    • if exist "C:\plik.txt"
    • if not exist "C:\temp"
  • Sprawdzanie czy zmienna istnieje:
    • if defined ZMIENNA
    • if not defined ZMIENNA
@echo off
setlocal

set /p ODPOWIEDZ=Czy chcesz kontynuowac? (T/N): 

:: Porownanie ciagu (ignoruje wielkosc liter)
if /I "%ODPOWIEDZ%"=="T" (
    echo Wybrano kontynuacje.
) else (
    echo Anulowano operacje.
    goto :koniec
)

:: Sprawdzenie istnienia pliku
set PLIK_KONFIG=C:\Windows\System32\drivers\etc\hosts
echo.
echo Sprawdzam istnienie pliku %PLIK_KONFIG%...

if exist "%PLIK_KONFIG%" (
    echo Plik zostal znaleziony.
) else (
    echo Blad: Nie znaleziono pliku!
)

:: Sprawdzenie zmiennej
set MOJA_ZMIENNA=wartość
if defined MOJA_ZMIENNA (
    echo Zmienna jest zdefiniowana.
)

:koniec
pause
endlocal

Instrukcja if w cmd.exe jest podstawowym mechanizmem podejmowania decyzji i umożliwia wykonywanie różnych działań w zależności od spełnienia warunku. Porównywanie ciągów znaków wymaga szczególnej uwagi – zmienne należy zawsze otaczać cudzysłowami, aby uniknąć błędów przy pustych zmiennych lub wartościach zawierających spacje. Parametr /I powoduje ignorowanie wielkości liter, co jest przydatne przy porównywaniu danych wejściowych od użytkownika. Sprawdzanie istnienia pliku za pomocą if exist jest jedną z najczęściej wykonywanych operacji w skryptach administracyjnych. Operator if defined pozwala sprawdzić, czy zmienna została zdefiniowana, co jest przydatne przy walidacji argumentów. Bloki kodu w if należy umieszczać w nawiasach, aby można było wykonać więcej niż jedno polecenie. else musi znajdować się w tej samej linii co zamykający nawias bloku then.

Prawidłowe stosowanie cudzysłowów i nawiasów w instrukcjach warunkowych jest jedną z tych umiejętności, która odróżnia początkujących od zaawansowanych programistów cmd.exe. Błędy składniowe w if są jednymi z najtrudniejszych do wyśledzenia.

16Instrukcja if: Porównania liczbowe i errorlevel

Porównywanie liczb

Do porównywania wartości liczbowych używa się specjalnych operatorów:

  • EQU - równy (equal)
  • NEQ - nierówny (not equal)
  • LSS - mniejszy (less than)
  • LEQ - mniejszy lub równy
  • GTR - większy (greater than)
  • GEQ - większy lub równy

Sprawdzanie errorlevel

if errorlevel numer - prawda gdy errorlevel >= numer

Uwaga: Wartość 0 oznacza sukces.

@echo off
setlocal

set /p WIEK=Podaj swoj wiek: 

if "%WIEK%"=="" (
    echo Nie podano wieku.
    goto :koniec
)

:: Porownanie liczbowe
if %WIEK% LSS 18 (
    echo Jestes osoba niepelnoletnia.
) else (
    if %WIEK% LSS 65 (
        echo Jestes osoba dorosla.
    ) else (
        echo Jestes emerytem.
    )
)
echo.

:: Sprawdzenie errorlevel
echo Próbuję skopiować nieistniejący plik...
copy C:\nie\istnieje.txt C:\temp > nul 2> nul

if errorlevel 1 (
    echo Wystapil blad!
    echo Kod bledu: %ERRORLEVEL%
) else (
    echo Operacja zakonczona sukcesem.
)

:koniec
pause
endlocal

Porównania liczbowe w cmd.exe różnią się od porównań tekstowych i wymagają użycia specjalnych operatorów słownych, takich jak EQU, NEQ, LSS, LEQ, GTR, GEQ. Operatory te są specyficzne dla cmd.exe i nie występują w innych językach programowania. Wewnątrz porównania liczbowego nie trzeba otaczać zmiennych cudzysłowami, ale wartości muszą być rzeczywistymi liczbami. Sprawdzanie errorlevel za pomocą if errorlevel n jest prawdziwe, gdy kod błędu jest większy lub równy n. Ta właściwość pozwala na sprawdzenie zakresu kodów błędów za pomocą kilku kolejnych instrukcji if. W praktyce if errorlevel 1 jest standardowym sposobem sprawdzenia, czy wystąpił jakikolwiek błąd. Operator %ERRORLEVEL% pozwala na bardziej precyzyjne porównania, na przykład sprawdzenie konkretnej wartości kodu błędu. Zagnieżdżanie instrukcji if pozwala na tworzenie złożonych logiki warunkowych.

Należy pamiętać, że if errorlevel sprawdza warunek >=, a nie ==. Oznacza to, że if errorlevel 1 jest prawdziwe również dla kodów błędów 2, 3 itd. Do sprawdzenia konkretnej wartości lepiej użyć if %ERRORLEVEL% equ n.

17Pętla for /L: Iteracja po zakresie liczb

Wykonywanie poleceń zadaną liczbę razy

Pętla for z przełącznikiem /L służy do iteracji po sekwencji liczb.

Składnia

for /L %%ZMIENNA in (start, krok, koniec) do (polecenie)

  • %%ZMIENNA: W skryptach podwójny procent (%%). W wierszu poleceń pojedynczy (%).
  • (start, krok, koniec): Sekwencja. Pętla zaczyna od `start`, zmienia o `krok`, kończy przy `koniec`.

Zastosowania praktyczne

  • Generowanie serii plików
  • Odliczanie czasu
  • Wielokrotne powtarzanie operacji
@echo off
setlocal

echo --- Odliczanie od 1 do 5 ---
for /L %%i in (1, 1, 5) do (
    echo Iteracja numer %%i
)
echo.

echo --- Liczby parzyste od 10 do 20 ---
for /L %%j in (10, 2, 20) do (
    echo Liczba parzysta: %%j
)
echo.

echo --- Odliczanie w dol (animacja) ---
for /L %%k in (5, -1, 1) do (
    echo Pozostalo: %%k sekund...
    timeout /t 1 > nul
)
echo Start!
echo.

:: Generowanie plikow z numerami
echo --- Generowanie plikow ---
for /L %%n in (1, 1, 3) do (
    echo Zawartosc pliku %%n > plik_%%n.txt
    echo Utworzono: plik_%%n.txt
)
dir plik_*.txt /b
del plik_*.txt

pause
endlocal

Pętla for /L jest podstawowym narzędziem do iteracji po zakresie liczb w cmd.exe, oferując podobną funkcjonalność jak pętla for w innych językach programowania. Składnia z trzema parametrami (start, krok, koniec) pozwala na precyzyjne sterowanie przebiegiem iteracji. Zmienna %%i w skryptach wsadowych musi być poprzedzona podwójnym znakiem procentu, podczas gdy w wierszu poleceń wystarczy pojedynczy. Praktyczne zastosowania pętli for /L obejmują generowanie plików z numerami, odliczanie czasu i wielokrotne powtarzanie operacji. Użycie ujemnego kroku pozwala na iterację w dół, co jest przydatne przy odliczaniu. Wewnątrz pętli można wykonywać dowolne polecenia cmd.exe, w tym wywoływać inne skrypty. Pętla for /L jest szczególnie przydatna przy testowaniu i prototypowaniu algorytmów.

W przypadku potrzeby iteracji po dynamicznej liście warto rozważyć użycie pętli for bez przełącznika, która domyślnie iteruje po zestawie plików lub wartościach. For /L jest zarezerwowane dla sekwencji liczbowych o znanym zakresie.

18Pętla for: Iteracja po plikach

Przetwarzanie wielu plików naraz

Jedno z najczęstszych zastosowań for - wykonywanie operacji na grupie plików.

Składnia

for %%ZMIENNA in (zestaw_plików) do (polecenie)

  • Maski: * (dowolny ciąg), ? (dowolny pojedynczy znak)
  • %%ZMIENNA: Nazwa kolejnego pliku

Modyfikatory zmiennej pętli

  • %%~fF - pełna ścieżka
  • %%~dF - dysk
  • %%~pF - ścieżka
  • %%~nF - nazwa bez rozszerzenia
  • %%~xF - rozszerzenie
  • %%~zF - rozmiar pliku
@echo off
setlocal

echo Tworze pliki tymczasowe...
echo test > file1.tmp
echo test > file2.tmp
echo test > data.log
echo.

echo --- Zmiana rozszerzenia z .tmp na .txt ---
for %%F in (*.tmp) do (
    echo Zmieniam "%%F" na "%%~nF.txt"
    ren "%%F" "%%~nF.txt"
)
echo.

echo --- Informacje o plikach ---
for %%F in (*.txt) do (
    echo ===========================
    echo Nazwa:     %%~nF
    echo Rozszerzenie: %%~xF
    echo Pełna ścieżka: %%~fF
    echo Rozmiar:   %%~zF bajtow
)
echo ===========================

del *.txt *.log > nul
pause
endlocal

Pętla for bez przełącznika jest najczęściej używaną formą pętli w skryptach wsadowych, służącą do iteracji po plikach i katalogach. Maski plików z gwiazdką i znakiem zapytania pozwalają na elastyczne dopasowywanie zestawów plików. Modyfikatory zmiennej pętli, takie jak %%~fF dla pełnej ścieżki czy %%~zF dla rozmiaru, znacząco rozszerzają możliwości przetwarzania plików. W praktyce pętla for z modyfikatorem nF (nazwa bez rozszerzenia) jest często używana do zmiany rozszerzeń plików lub tworzenia kopii zapasowych. Modyfikatory można ze sobą łączyć, na przykład %%~dpF daje dysk i ścieżkę pliku. Pętla for pozwala również na iterację po konkretnych plikach wymienionych z nazwy. Wewnątrz pętli można używać warunków if do filtrowania przetwarzanych plików.

Warto zapamiętać, że zmienna pętli for (%%F) jest dostępna tylko wewnątrz pętli i nie można jej odczytać po jej zakończeniu bez zapisania do osobnej zmiennej. Jest to częste źródło błędów u początkujących programistów.

19Pętla for /F: Przetwarzanie zawartości plików

Czytanie plików i wyników poleceń

Przełącznik /F pozwala na przetwarzanie zawartości - linia po linii z pliku lub wyniku polecenia.

Składnia

for /F ["opcje"] %%ZMIENNA in (źródło) do (polecenie)

  • Źródło:
    • ('command') - wynik polecenia
    • ("string") - ciąg znaków
    • (plik.txt) - zawartość pliku
  • Opcje:
    • "delims=xyz" - separatory
    • "tokens=1,2,*" - które tokeny
    • "skip=n" - pomiń n pierwszych linii
@echo off
setlocal EnableDelayedExpansion

:: --- Przetwarzanie wyniku polecenia ---
echo --- Lista plikow z dir ---
for /f "tokens=*" %%a in ('dir /b *.cmd 2^>nul') do (
    echo Plik: %%a
)
echo.

:: --- Przetwarzanie pliku CSV ---
echo jkowalski,Jan,Kowalski,IT > uzytkownicy.csv
echo anowak,Anna,Nowak,HR >> uzytkownicy.csv
echo mnowicki,Marek,Nowicki,Finanse >> uzytkownicy.csv

echo --- Dane uzytkownikow z CSV ---
for /f "tokens=1,2,3,4 delims=," %%a in (uzytkownicy.csv) do (
    echo Login: %%a, Imie: %%b, Nazwisko: %%c, Dzial: %%d
)
del uzytkownicy.csv
echo.

:: --- Parsowanie ciagu ---
echo --- Parsowanie daty ---
for /f "tokens=1,2,3 delims=/" %%a in ("2025/12/25") do (
    echo Dzien: %%a, Miesiac: %%b, Rok: %%c
)

pause
endlocal

Pętla for /F jest najbardziej zaawansowaną formą pętli w cmd.exe, umożliwiającą przetwarzanie plików linia po linii oraz parsowanie wyników poleceń. Opcje delims, tokens i skip pozwalają na dostosowanie sposobu parsowania do konkretnego formatu danych. Domyślnym separatorem pól jest spacja i tabulacja, ale można to zmienić za pomocą delims. Opcja tokens określa, które kolumny mają być przypisane do zmiennych pętli, a gwiazdka (*) oznacza pozostałą część linii. Przetwarzanie wyników poleceń w jednolitym cudzysłowie (polecenie) jest szczególnie przydatne przy analizie danych systemowych. Parsowanie ciągów znaków za pomocą ("tekst") pozwala na szybkie dzielenie tekstu na części. Pętla for /F jest nieoceniona przy przetwarzaniu plików CSV, logów i innych danych w formacie kolumnowym.

W praktyce for /F wymaga opóźnionego rozszerzania zmiennych, jeśli wewnątrz pętli modyfikujemy zmienne. Bez tego każda iteracja widzi tylko początkową wartość zmiennej, co prowadzi do błędów logicznych w skrypcie.

20Argumenty wiersza poleceń - podstawy

Parametry przekazywane do skryptu

Skrypty mogą otrzymywać argumenty z wiersza poleceń przez specjalne zmienne:

Zmienne argumentów

  • %0: Nazwa pliku skryptu
  • %1: Pierwszy argument
  • %2: Drugi argument
  • ...
  • %9: Dziewiąty argument
  • %*: Wszystkie argumenty (jako jeden ciąg)

Ważne uwagi

  • Argumenty oddzielane spacjami
  • Ciągi ze spacjami ujmuj w cudzysłowy
  • Można przekazać do 255 argumentów (ale bezpośredni dostęp tylko do %1-%9)
@echo off
setlocal

echo === Informacje o wywolaniu skryptu ===
echo Nazwa skryptu: %0
echo.
echo Wszystkie argumenty: %*
echo.

if "%1"=="" (
    echo Blad: Nie podano argumentow!
    echo Uzycie: %~n0 [arg1] [arg2] [arg3]
    goto :koniec
)

echo === Poszczegolne argumenty ===
echo Argument 1: %1
echo Argument 2: %2
echo Argument 3: %3
echo Argument 4: %4
echo Argument 5: %5
echo.

echo Pierwszy i drugi razem: %1 %2

:koniec
pause
endlocal

:: Wywolanie przykładowe:
:: moj_skrypt.bat jeden dwa "trzy slowa" cztery

Argumenty wiersza poleceń są podstawowym mechanizmem przekazywania danych do skryptów wsadowych, umożliwiającym sterowanie ich działaniem bez modyfikacji kodu. Zmienna %0 przechowuje nazwę skryptu, a %1 do %9 kolejne argumenty. Dostęp do argumentów powyżej 9 jest możliwy tylko za pomocą polecenia shift, które przesuwa indeksy argumentów. Argumenty zawierające spacje muszą być ujęte w cudzysłowy podczas wywołania skryptu. Zmienna %* zawiera wszystkie argumenty jako jeden ciąg znaków, co jest przydatne przy przekazywaniu ich dalej. Sprawdzanie, czy argument został podany, wykonuje się przez porównanie %1 z pustym ciągiem. Dobrą praktyką jest zawsze walidować liczbę i typ argumentów na początku skryptu.

W nowoczesnych skryptach wsadowych warto rozważyć użycie zaawansowanych technik parsowania argumentów, takich jak pętla shift z opcjami. Pozwala to na tworzenie skryptów akceptujących opcjonalne argumenty w dowolnej kolejności, podobnie jak w profesjonalnych narzędziach wiersza poleceń.

21Argumenty wiersza poleceń - modyfikatory

Modyfikatory rozszerzania argumentów

Do zmiennych argumentów stosuje się specjalne modyfikatory (prefiksy z tyldą ~):

ModyfikatorOpisPrzykład
~1Bez cudzysłowów"C:\plik" -> C:\plik
~f1Pełna ścieżkaC:\Folder\plik.txt
~d1Tylko dyskC:
~p1Tylko ścieżka\Folder\
~n1Tylko nazwaplik
~x1Tylko rozszerzenie.txt
~z1Rozmiar pliku1024
~dp1Dysk + ścieżkaC:\Folder\
~dpnx1Pełna nazwaplik.txt
@echo off
setlocal

if "%1"=="" (
    echo Użycie: %~n0 ^<ścieżka_do_pliku^>
    goto :koniec
)

echo === Analiza argumentu: %1 ===
echo.
echo %1          = %1
echo ~f1         = %~f1
echo ~d1         = %~d1
echo ~p1         = %~p1
echo ~n1         = %~n1
echo ~x1         = %~x1
echo ~z1         = %~z1
echo.
echo ~dp1        = %~dp1
echo ~dpnx1      = %~dpnx1

:koniec
pause
endlocal

:: Wywolanie przykładowe:
:: moj_skrypt.bat "C:\Moje Dokumenty\raport.pdf"

Modyfikatory argumentów w cmd.exe to potężne narzędzia do transformacji ścieżek i nazw plików bez użycia zewnętrznych programów. Znak tyldy (~) przed numerem argumentu usuwa otaczające cudzysłowy, co jest przydatne przy dalszym przetwarzaniu. Modyfikatory literowe, takie jak f (pełna ścieżka), d (dysk), p (ścieżka), n (nazwa), x (rozszerzenie), pozwalają na wyciągnięcie poszczególnych składników ścieżki. Modyfikator z zwraca rozmiar pliku, co jest przydatne przy sprawdzaniu zajętości dysku. Modyfikatory można ze sobą łączyć, tworząc złożone transformacje, na przykład ~dpnx1 daje pełną nazwę pliku bez ścieżki. W praktyce najczęściej używa się ~dp0 do uzyskania katalogu skryptu oraz ~n1 i ~x1 do rozdzielenia nazwy i rozszerzenia. Modyfikatory działają zarówno dla argumentów skryptu, jak i zmiennych pętli for.

Znajomość modyfikatorów jest niezbędna przy pisaniu skryptów, które operują na ścieżkach plików. Pozwalają one uniknąć używania zewnętrznych narzędzi takich jak sed czy awk do prostych transformacji ścieżek.

22Przekazywanie ścieżek do plików i katalogów

Obsługa ścieżek w skryptach

Ścieżki wymagają szczególnej uwagi, ponieważ mogą zawierać spacje i znaki specjalne.

Reguły bezpiecznego operowania ścieżkami

  1. Zawsze ujmuj ścieżki w cudzysłowy
  2. Używaj modyfikatorów ~
  3. Sprawdzaj istnienie przed operacją - if exist
  4. Obsługuj ścieżki względne i bezwzględne

Przydatne konstrukcje

  • %~f1 - zamienia ścieżkę względną na bezwzględną
  • cd /d "%~dp0" - zmiana do katalogu skryptu
  • %CD% - bieżący katalog roboczy
@echo off
setlocal EnableDelayedExpansion

:: Ustawienie katalogu skryptu jako biezacego
set "SCRIPT_DIR=%~dp0"
cd /d "%SCRIPT_DIR%"
echo Katalog skryptu: %SCRIPT_DIR%
echo Biezacy katalog: %CD%
echo.

:: Pobieranie argumentu jako sciezki
set "PLIK=%~1"

if not defined PLIK (
    echo Nie podano sciezki do pliku.
    goto :koniec
)

:: Sprawdzenie czy plik istnieje
if exist "%PLIK%" (
    echo Plik istnieje: %PLIK%
    echo.
    echo Informacje o pliku:
echo Pełna ścieżka: %~f1
    echo   Katalog:       %~dp1
    echo   Nazwa:         %~n1
    echo   Rozszerzenie:  %~x1
    echo   Rozmiar:       %~z1 bajtow
) else (
    echo Blad: Plik nie istnieje: %PLIK%
)

:koniec
pause
endlocal

Bezpieczne operowanie ścieżkami plików w skryptach wsadowych jest kluczowe dla niezawodności i bezpieczeństwa. Cudzysłowy wokół ścieżek zapobiegają problemom ze spacjami w nazwach katalogów, co jest częstym źródłem błędów. Modyfikatory ~ pozwalają na normalizację ścieżek, na przykład zamianę względnych na bezwzględne. Sprawdzanie istnienia pliku lub katalogu za pomocą if exist przed wykonaniem operacji jest standardem w profesjonalnych skryptach. Użycie cd /d "%~dp0" na początku skryptu zapewnia, że będzie on działał poprawnie niezależnie od miejsca wywołania. Zmienna %CD% zawsze wskazuje bieżący katalog roboczy, ale może się zmieniać w trakcie działania skryptu. Operacje na ścieżkach są szczególnie ważne w skryptach wdrożeniowych i backupowych.

W środowiskach sieciowych warto pamiętać o obsłudze ścieżek UNC (Universal Naming Convention) rozpoczynających się od \serwer\udział. cmd.exe poprawnie obsługuje takie ścieżki, ale wymagają one szczególnej uwagi przy przekazywaniu do niektórych poleceń.

23Zmienne specjalne %~dp0 i praca z katalogami

Zmienna %~dp0

%~dp0 to jeden z najbardziej przydatnych modyfikatorów:

  • d - dysk bieżącego skryptu, np. C:
  • p - ścieżka do katalogu skryptu, np. \Users\Jan\Scripts\
  • 0 - sam skrypt (%0)

Razem daje to pełną ścieżkę do katalogu skryptu, z końcowym backslashem.

Zastosowania

  • Ustawienie katalogu roboczego na katalog skryptu
  • Odnajdywanie plików konfiguracyjnych
  • Uniezależnienie skryptu od miejsca wywołania

Uwaga!

%~dp0 zawsze wskazuje na katalog skryptu, niezależnie od tego, skąd został wywołany!

@echo off
setlocal EnableDelayedExpansion

:: Zapisz informacje o skrypcie
set "SCRIPT_PATH=%~f0"
set "SCRIPT_DIR=%~dp0"
set "SCRIPT_NAME=%~nx0"

echo === Informacje o skrypcie ===
echo Sciezka:    %SCRIPT_PATH%
echo Katalog:    %SCRIPT_DIR%
echo Nazwa:      %SCRIPT_NAME%
echo.

:: Przejdz do katalogu skryptu
cd /d "%SCRIPT_DIR%"
echo Po zmianie - biezacy: %CD%
echo.

:: Szukaj pliku konfiguracyjnego w katalogu skryptu
set "CONFIG_FILE=%SCRIPT_DIR%config.ini"
if exist "%CONFIG_FILE%" (
    echo Znaleziono config: %CONFIG_FILE%
) else (
    echo Brak pliku konfiguracyjnego.
)
echo.

:: Lista plikow w katalogu skryptu
echo Pliki w katalogu skryptu:
for %%F in ("%SCRIPT_DIR%*") do (
    if not "%%~nxF"=="%~nx0" (
        echo   %%~nxF
    )
)

pause
endlocal

Zmienna %~dp0 jest jednym z najważniejszych narzędzi w arsenale programisty skryptów wsadowych, ponieważ zawsze wskazuje katalog, w którym znajduje się uruchomiony skrypt. Składa się z trzech części: d (dysk), p (ścieżka) i 0 (nazwa skryptu). Końcowy backslash w wartości %~dp0 jest ważny – bez niego łączenie ścieżek mogłoby dać nieprawidłowy wynik. Zastosowania %~dp0 obejmują ustawianie katalogu roboczego, odnajdywanie plików konfiguracyjnych i uniezależnianie skryptu od miejsca wywołania. Ważną cechą %~dp0 jest to, że działa niezależnie od tego, skąd skrypt został wywołany – zawsze wskazuje jego własny katalog. W połączeniu z pętlą for pozwala na przetwarzanie plików w katalogu skryptu. Zmienna %~dp0 jest szczególnie przydatna w skryptach przenośnych, które są dystrybuowane jako zestaw plików.

Warto pamiętać, że %~dp0 zwraca ścieżkę z bieżącego dysku, ale sam dysk jest uwzględniony w wartości. Jeśli skrypt znajduje się na dysku D:, to %~dp0 będzie zaczynać się od D:\. Umożliwia to bezproblemowe operacje na plikach niezależnie od dysku.

24Polecenie shift - obsługa wielu argumentów

Obsługa więcej niż 9 argumentów

Standardowo możemy odczytać tylko argumenty %1-%9. Aby obsłużyć więcej, używamy shift.

Działanie polecenia shift

shift przesuwa wartości argumentów w lewo:

  • %2 staje się %1
  • %3 staje się %2
  • ...
  • %10 staje się %9

Po wykonaniu shift oryginalny %1 jest tracony!

Praktyczne użycie

Pętla z shift pozwala przetworzyć dowolną liczbę argumentów.

@echo off
setlocal EnableDelayedExpansion

echo === Przetwarzanie wszystkich argumentow ===
echo Otrzymane: %*
echo.

:: Metoda 1: Petla z shift
echo --- Metoda 1: Petla z shift ---
:loop1
if "%~1"=="" goto :koniec1
echo Argument: %1
shift
goto :loop1
:koniec1
echo.

:: Metoda 2: FOR (wszystkie na raz)
echo --- Metoda 2: FOR ---
for %%A in (%*) do (
    echo Argument: %%A
)
echo.

:: Metoda 3: Z licznikiem
echo --- Metoda 3: Z licznikiem ---
set /a LICZNIK=0
for %%A in (%*) do (
    set /a LICZNIK+=1
    echo Argument !LICZNIK!: %%A
)
echo.
echo Lacznie: !LICZNIK! argumentow

pause
endlocal

:: Wywolanie:
:: moj_skrypt.bat raz dwa trzy cztery piec szesc

Polecenie shift jest niezbędne przy obsłudze większej liczby argumentów, niż pozwalają na to standardowe zmienne %1-%9. Mechanizm przesuwania argumentów w lewo powoduje, że %2 staje się nowym %1, %3 staje się %2, i tak dalej. Po wykonaniu shift oryginalny %1 jest bezpowrotnie tracony, więc przed przesunięciem należy zapisać jego wartość, jeśli będzie jeszcze potrzebna. W praktyce shift jest najczęściej używany w pętli, która przetwarza argumenty jeden po drugim aż do wyczerpania listy. Pętla z shift i goto :etykieta to klasyczny wzorzec przetwarzania argumentów w skryptach wsadowych. For po prostu iteruje po wszystkich argumentach bez konieczności używania shift. Mimo że shift jest wciąż używany w starszych skryptach, w nowych warto rozważyć for jako prostszą alternatywę.

Polecenie shift obsługuje również opcjonalny parametr /n, który pozwala na przesuwanie argumentów od konkretnego indeksu. Jest to przydatne w zaawansowanych skryptach, które muszą zachować kilka pierwszych argumentów jako stałe parametry.

25Etykiety i goto

Sterowanie przepływem wykonania

Skrypty wsadowe pozwalają na definiowanie etykiet i skakanie do nich za pomocą goto.

Składnia

  • Definiowanie: :moja_etykieta
  • Skok: goto moja_etykieta

Ostrzeżenie: Nadużywanie goto prowadzi do "spaghetti code". Używaj z umiarem!

Etykieta specjalna :eof

:eof (End Of File) oznacza koniec pliku. goto :eof kończy skrypt.

@echo off
setlocal

echo Witaj w prostym menu.
echo.
echo 1. Pokaz date
echo 2. Pokaz liste plikow
echo 3. Wyjscie
echo.
set /p WYBOR=Wybierz opcje (1, 2 lub 3): 

if "%WYBOR%"=="1" goto :pokaz_date
if "%WYBOR%"=="2" goto :pokaz_pliki
if "%WYBOR%"=="3" goto :koniec
echo Nieznana opcja.
goto :koniec

:pokaz_date
echo.
echo === Aktualna data ===
date /t
goto :koniec

:pokaz_pliki
echo.
echo === Lista plikow ===
dir /b
goto :koniec

:koniec
echo.
echo Koniec programu.
pause
endlocal

Etykiety i goto są najstarszym mechanizmem sterowania przepływem w skryptach wsadowych, wywodzącym się bezpośrednio z języków programowania ery mainframe. Etykieta to dowolna nazwa poprzedzona dwukropkiem, która oznacza miejsce w kodzie, do można skoczyć za pomocą goto. Specjalna etykieta :eof oznacza koniec pliku i jest często używana do zakończenia skryptu lub podprogramu. Nadmierne używanie goto prowadzi do tak zwanego spaghetti code, czyli kodu o nieczytelnej strukturze. Mimo to w skryptach wsadowych goto jest często niezbędne, ponieważ cmd.exe nie oferuje bardziej zaawansowanych konstrukcji sterujących. Praktyczne zastosowania goto obejmują implementację menu, obsługę błędów i tworzenie pętli. Etykiety w cmd.exe są niewrażliwe na wielkość liter, więc :KONIEC i :koniec oznaczają to samo miejsce.

W nowoczesnych skryptach warto ograniczyć użycie goto na rzecz strukturalnych konstrukcji, takich jak bloki if-else i pętle for. goto powinno być zarezerwowane dla sytuacji, gdy inne mechanizmy są niewystarczające, na przykład przy obsłudze błędów krytycznych.

26Podprogramy z call - tworzenie funkcji

Tworzenie "funkcji" wielokrotnego użytku

Możemy symulować funkcje za pomocą etykiet i polecenia call:

  • goto :etykieta: Skacze i nie wraca.
  • call :etykieta: Skacze, wykonuje, a potem wraca.

Zakończenie podprogramu

  • goto :eof - standardowy sposób
  • exit /b - alternatywa z kodem powrotu

Argumenty w podprogramach

Wewnątrz podprogramu %1, %2... odnoszą się do argumentów wywołania call.

@echo off
setlocal

echo === Glowna czesc ===
echo Wywołuje podprogram raz pierwszy...
echo Wróciłem. Wywołuje raz drugi...
echo Wywołuje funkcję z liczbami...
call :oblicz_sume 10 20
echo Suma: %ERRORLEVEL%

echo Koniec.
pause
goto :eof

:: ============================================
:wyswietl_info
echo.
echo   --- W podprogramie 'wyswietl_info' ---
echo   Czas: %TIME%
echo   Argument: %~1
echo   --- Koniec ---
echo.
goto :eof

:: ============================================
:oblicz_sume
set /a "SUMA=%~1 + %~2"
exit /b %SUMA%

Podprogramy wywoływane przez call są odpowiednikiem funkcji w innych językach programowania i pozwalają na wielokrotne używanie tego samego kodu. Różnica między goto a call jest fundamentalna: goto skacze do etykiety i nie wraca, podczas gdy call zapamiętuje miejsce wywołania i po dotarciu do :eof wraca do niego. Podprogramy mogą przyjmować argumenty dostępne jako %1, %2 itd., które są oddzielne od argumentów głównego skryptu. Zakończenie podprogramu następuje przez goto :eof lub exit /b, który dodatkowo ustawia kod powrotu. Podprogramy pozwalają na tworzenie modułowego kodu, który można testować niezależnie. W praktyce call jest używane do implementacji funkcji takich jak logowanie, walidacja danych czy obliczenia. Podprogramy można zagnieżdżać, czyli jeden podprogram może wywoływać kolejny za pomocą call.

Dobrą praktyką jest oddzielanie definicji podprogramów od głównej logiki skryptu za pomocą goto :eof na końcu głównej części. Dzięki temu interpreter nigdy nie wejdzie przypadkowo w definicję podprogramu bez wywołania przez call.

27Zaawansowane podprogramy - zwracanie wartości

Problem z zwracaniem wartości

Skrypty wsadowe nie mają mechanizmu bezpośredniego zwracania wartości. Istnieje kilka sposobów:

Metody zwracania wartości

  1. Przez ERRORLEVEL - exit /b wartosc
  2. Przez zmienną - zmiana zmiennej wewnątrz podprogramu
  3. Przez ENDLOCAL - sztuczka endlocal & set "VAR=%VAR%"

Najlepsza praktyka

Używając endlocal & set "ZMienna=%ZMienna%" można bezpiecznie przekazać wartość zmiennej lokalnej na zewnątrz.

@echo off
setlocal EnableDelayedExpansion

echo === Demonstracja roznych metod ===

:: Metoda 1: ERRORLEVEL
echo --- Metoda 1: ERRORLEVEL ---
call :get_random
echo Losowa liczba: !ERRORLEVEL!
echo.

:: Metoda 2: Zmienna przez parametr
echo --- Metoda 2: Zmienna ---
set "RESULT="
call :get_timestamp RESULT
echo Znacznik czasu: !RESULT!
echo.

:: Metoda 3: Natychmiastowy set
echo --- Metoda 3: ENDLOCAL trick ---
call :get_uppercase "hello world" RESULT2
echo Tekst wielkimi: !RESULT2!

pause
goto :eof

:: ============================================
:get_random
set /a "RAND=%RANDOM% %% 100"
exit /b !RAND!

:: ============================================
:get_timestamp
setlocal
set "ts=%DATE% %TIME%"
endlocal & set "%~1=%ts%"
goto :eof

:: ============================================
:get_uppercase
setlocal
set "text=%~1"
set "upper=!text:a=A!"
set "upper=!upper:e=E!"
set "upper=!upper:i=I!"
set "upper=!upper:o=O!"
set "upper=!upper:u=U!"
endlocal & set "%~2=%upper%"
goto :eof

Zwracanie wartości z podprogramów w cmd.exe jest bardziej złożone niż w innych językach, ale dostępne techniki pozwalają na skuteczne przekazywanie wyników. Metoda przez ERRORLEVEL jest najprostsza, ale ogranicza się do liczb całkowitych w zakresie 0-255. Metoda przez zmienną przekazaną jako referencja (%~1) pozwala na modyfikację zmiennej w miejscu wywołania, co jest symulacją przekazywania przez referencję. Sztuczka endlocal & set jest najbardziej zaawansowana i pozwala na bezpieczne przekazanie wartości z podprogramu do głównego skryptu, zachowując izolację zmiennych lokalnych. Każda z tych metod ma swoje zalety i wady, a wybór zależy od konkretnego zastosowania. W praktyce najczęściej używa się metody przez ERRORLEVEL dla wartości liczbowych i sztuczki endlocal dla tekstów. Kombinacja tych technik pozwala na implementację złożonych operacji zwracających wiele wartości.

Niezależnie od wybranej metody, warto dokumentować, jakiego typu wartości i w jaki sposób podprogram zwraca. Ułatwia to utrzymanie kodu i zapobiega błędom przy ponownym użyciu podprogramu w innych skryptach.

28Wywoływanie innych skryptów i programów

Różne sposoby uruchamiania programów

  • call - wywołuje skrypt i czeka na zakończenie
  • start - uruchamia w nowym oknie (nie czeka)
  • start /wait - uruchamia i czeka
  • cmd /c - wykonuje i kończy

call a bezpośrednie wywołanie

  • inny_skrypt.bat - przekazuje sterowanie i NIE wraca
  • call inny_skrypt.bat - przekazuje i wraca

Przekazywanie argumentów

Argumenty są domyślnie przekazywane. Można użyć %* aby przekazać wszystkie.

@echo off
setlocal

echo === Wywolywanie innych programow ===

:: Uruchomienie notatnika (nie czeka)
echo Uruchamiam Notatnik...
start notepad
echo Notatnik uruchomiony (w tle).
echo.

:: Uruchomienie z czekaniem
echo Uruchamiam kalkulator i czekam...
start /wait calc
echo Kalkulator zamkniety.
echo.

:: Wywolanie innego skryptu z argumentami
echo Wywołuje podskrypt z argumentami...
call sub_script.bat "arg1" "arg 2" 123
echo Podskrypt zakonczony.
echo.

:: Przekazanie wszystkich argumentow dalej
echo Przekazuje wszystkie argumenty...
call sub_script2.bat %*

pause
endlocal

Wywoływanie innych skryptów i programów z poziomu skryptu wsadowego pozwala na tworzenie modularnych systemów automatyzacji. Polecenie call jest preferowane do wywoływania innych skryptów wsadowych, ponieważ zapewnia powrót sterowania do skryptu wywołującego. Polecenie start uruchamia program w nowym oknie, co jest przydatne przy uruchamianiu aplikacji z graficznym interfejsem. Start /wait łączy zalety start i call – uruchamia program w nowym oknie i czeka na jego zakończenie. Polecenie cmd /c wykonuje polecenie w nowej instancji interpretera i kończy ją, co jest przydatne przy wykonywaniu pojedynczych poleceń. Bezpośrednie wywołanie innego skryptu (bez call) przekazuje mu sterowanie na stałe – skrypt wywołujący nie odzyskuje kontroli. Przekazywanie argumentów między skryptami jest automatyczne, ale można je kontrolować za pomocą %*. Profesjonalne skrypty często używają call do wywoływania bibliotek funkcji.

W środowiskach sieciowych warto pamiętać, że wywoływanie skryptów z udziałów sieciowych może być blokowane przez Group Policy lub zabezpieczenia systemu. W takich przypadkach należy skopiować skrypt lokalnie przed jego wykonaniem.

29Przekierowanie wejścia-wyjścia (I/O Redirection)

Czym jest przekierowanie?

Przekierowanie pozwala zmienić domyślne źródło danych wejściowych (STDIN) lub miejsce, gdzie trafiają dane wyjściowe (STDOUT, STDERR).

Standardowe strumienie

  • STDIN (0) - wejście (domyślnie klawiatura)
  • STDOUT (1) - wyjście (domyślnie ekran)
  • STDERR (2) - błędy (domyślnie ekran)

Operatory przekierowania

  • > plik - przekieruj STDOUT do pliku (nadpisuje plik)
  • >> plik - przekieruj STDOUT do pliku (dopisuje do końca)
  • < plik - czytaj z pliku zamiast z klawiatury
  • 2> blad.txt - przekieruj STDERR do pliku
  • 2>&1 - przekieruj STDERR do tego samego miejsca co STDOUT
  • > nul - wyślij wyjście "do kosza" (ukryj)
  • 2> nul - ukryj tylko błędy
  • > nul 2>&1 - ukryj wszystko (wyjście i błędy)
@echo off
setlocal EnableDelayedExpansion

echo === Przyklady przekierowan ===
echo.

:: Tworzenie pliku z wynikiem dir (nadpisz)
dir /b > lista_plikow.txt
echo Utworzono plik lista_plikow.txt

:: Dopisanie daty do istniejacego pliku
echo. >> lista_plikow.txt
echo --- Data utworzenia --- >> lista_plikow.txt
date /t >> lista_plikow.txt

:: Wyswietlenie zawartosci pliku
echo.
echo Zawartosc pliku:
type lista_plikow.txt
echo.

:: Wczytanie pierwszej linii do zmiennej
set /p WCZYTANA= nul 2> nul
echo Operacja zakończona (błędy ukryte).

:: Pokazanie błędów
echo.
echo Teraz te same z błędami:
copy C:\nie_ma\takiego.txt C:\temp 2>&1

del lista_plikow.txt
pause
endlocal

Przekierowanie strumieni w cmd.exe działa na tych samych zasadach co w innych systemach uniksowych, ale składnia została dostosowana do specyfiki Windows. Trzy standardowe strumienie (stdin, stdout, stderr) są obsługiwane za pomocą deskryptorów 0, 1 i 2. Operator > przekierowuje standardowe wyjście do pliku, tworząc go lub nadpisując, jeśli już istnieje. Operator >> dopisuje dane do istniejącego pliku, co jest przydatne przy tworzeniu logów. Przekierowanie stderr za pomocą 2> pozwala na oddzielenie komunikatów błędów od prawidłowych danych wyjściowych. Specjalne urządzenie nul działa jak "kosz na dane" i jest często używane do tłumienia niepotrzebnych komunikatów. Połączenie > nul 2>&1 jest standardowym sposobem całkowitego ukrycia zarówno wyjścia, jak i błędów. Przekierowanie wejścia za pomocą < pozwala na wczytanie danych z pliku.

Znajomość przekierowań jest niezbędna przy tworzeniu skryptów, które mają działać cicho (cicho, bez zbędnych komunikatów) lub zapisywać szczegółowe logi do plików. W połączeniu z potokami przekierowania dają ogromne możliwości przetwarzania danych.

30Potoki (pipes) i łączenie poleceń

Potoki (Pipes)

Znak | przekierowuje wyjście jednego polecenia na wejście kolejnego.

Przykład: polecenie1 | polecenie2 - wyniki polecenia1 stają się danymi wejściowymi polecenia2.

Łączenie poleceń

  • && - wykonaj drugie tylko gdy pierwsze się powiodło
  • || - wykonaj drugie tylko gdy pierwsze się nie powiodło
  • & - wykonaj oba niezależnie
  • ^ - kontynuacja linii
@echo off
setlocal

echo === Przyklady potokow ===
echo.

:: Sortowanie wynikow dir
echo Sortowane pliki *.cmd:
dir /b *.cmd 2>nul | sort

echo.
:: Zliczanie plikow
echo Liczba plikow .cmd:
dir /b *.cmd 2>nul | find /c /v ""

echo.
echo === Przyklady laczenia polecen ===
echo.

:: && - wykonaj tylko gdy sukces
echo Sprawdzam istniejacy plik:
type %~n0.cmd >nul 2>&1 && echo ISTNIEJE

:: || - wykonaj tylko gdy blad
echo.
echo Sprawdzam nieistniejący plik:
type nie_ma.txt >nul 2>&1 || echo NIE ISTNIEJE (oczekiwane)

:: & - wykonaj oba
echo.
echo Uzycie &: Polecenie 1 & Polecenie 2
echo Wyswietlam numer & echo Wersja: 1.0

pause
endlocal

Potoki w cmd.exe umożliwiają łączenie poleceń w łańcuchy przetwarzania, gdzie wynik jednego polecenia staje się danymi wejściowymi następnego. Jest to realizacja filozofii Unix, w której każde narzędzie wykonuje jedną konkretną funkcję. W cmd.exe potoki są szczególnie przydatne przy filtrowaniu i sortowaniu danych wyjściowych. Operatory && i || umożliwiają warunkowe wykonywanie poleceń w zależności od kodu powrotu poprzedniego polecenia. Operator && wykonuje następne polecenie tylko wtedy, gdy poprzednie zakończyło się sukcesem. Operator || wykonuje następne polecenie tylko przy błędzie poprzedniego. Operator & (pojedynczy) wykonuje polecenia niezależnie, jedno po drugim. Znak ^ (caret) służy do kontynuacji polecenia w następnej linii, co poprawia czytelność długich poleceń. W praktyce potoki są używane przy każdym zaawansowanym skrypcie.

Warto pamiętać, że potoki w cmd.exe są buforowane, co oznacza, że dane są przesyłane dopiero po zakończeniu pierwszego polecenia. Może to powodować opóźnienia przy przetwarzaniu dużych ilości danych i różni się od zachowania potoków w systemach Linux.

31Obsługa błędów i kody powrotu

Kody powrotu (Exit Codes)

Każde polecenie ustawia %ERRORLEVEL%:

  • 0 - sukces
  • >0 - błąd

Sprawdzanie ERRORLEVEL

  • if errorlevel n - prawda gdy >= n
  • if %ERRORLEVEL% neq 0 - prawda gdy błąd

Zakończenie skryptu z kodem

exit /b kod - kończy skrypt z kodem powrotu.

exit - kończy całą sesję cmd.exe.

@echo off
setlocal EnableDelayedExpansion

echo === Obsługa błędów ===

set "BLEDNY_PLIK=C:\nie_istnieje.txt"

echo --- Proba 1: Plik nie istnieje ---
copy "%BLEDNY_PLIK%" C:\temp >nul 2>&1
if errorlevel 1 (
    echo BLAD: Plik nie istnieje!
) else (
    echo SUKCES: Plik skopiowany.
)
echo.

echo --- Proba 2: z || ---
copy "%BLEDNY_PLIK%" C:\temp >nul && echo SUKCES || echo BLAD (oczekiwane)

echo.
echo --- Proba 3: z && ---
copy %~n0.cmd nul >nul && echo PLIK ISTNIEJE || echo BLAD

pause
endlocal

Obsługa błędów w skryptach wsadowych opiera się na zmiennej %ERRORLEVEL% i mechanizmie if errorlevel. Każde polecenie w cmd.exe ustawia kod powrotu, który informuje o powodzeniu operacji – 0 oznacza sukces, a wartości powyżej 0 oznaczają różne rodzaje błędów. Sprawdzanie errorlevel po każdym krytycznym poleceniu jest standardem w profesjonalnych skryptach. Operator if errorlevel n jest prawdziwy, gdy kod błędu jest większy lub równy n, co pozwala na wykrycie zakresu błędów. Alternatywnie można porównać %ERRORLEVEL% z konkretną wartością za pomocą operatorów EQU, NEQ itp. Polecenie exit /b kod ustawia kod powrotu skryptu i kończy jego działanie. W praktyce obsługa błędów powinna być implementowana na każdym etapie skryptu, nie tylko na końcu. Wczesne wykrycie błędu pozwala uniknąć kaskadowych problemów.

W złożonych skryptach warto rozważyć implementację centralnej obsługi błędów za pomocą podprogramu, który zapisuje błąd do logu i odpowiednio kończy skrypt. Zwiększa to czytelność kodu i ułatwia utrzymanie spójnego systemu raportowania błędów.

32Pętla for /R: Rekursywnie po katalogach

Rekursywne przechodzenie przez katalogi

Przełącznik /R (Recursive) pozwala na przechodzenie przez strukturę katalogów.

Składnia

for /R [katalog] %%ZMIENNA in (maska) do (polecenie)

  • /R katalog: Starting directory. Jeśli pominięte, używa bieżącego.
  • maska: Maska plików, np. *.txt

Praktyczne zastosowania

  • Zliczanie plików danego typu
  • Wyszukiwanie w całym drzewie
  • Masowa zmiana uprawnień
@echo off
setlocal EnableDelayedExpansion

set "START_DIR=%CD%"
set /a LICZNIK=0

echo === Rekurencyjne przechodzenie ===
echo Szukam plikow .txt w %START_DIR%...
echo.

for /R "%START_DIR%" %%F in (*.txt) do (
    set /a LICZNIK+=1
    echo !LICZNIK!: %%~nxF
    echo       Sciezka: %%~dpF
)
echo.

echo Znaleziono !LICZNIK! plikow .txt
echo.

:: Wyszukiwanie plikow konfiguracyjnych
echo === Pliki konfiguracyjne ===
for /R "%START_DIR%" %%F in ("*.ini" "*.cfg" "*.conf") do (
    echo Znaleziono: %%~nxF
)

pause
endlocal

Pętla for /R umożliwia rekurencyjne przeszukiwanie struktury katalogów, co jest niezwykle przydatne przy operacjach na drzewiastych strukturach plików. Przełącznik /R przyjmuje opcjonalny parametr określający katalog początkowy, a jeśli go pominiemy, używany jest bieżący katalog. Maska plików pozwala na filtrowanie według nazwy lub rozszerzenia. Wewnątrz pętli zmienna %%F zawiera pełną ścieżkę do pliku, a modyfikatory pozwalają na wyciągnięcie poszczególnych składników. Pętla for /R może przetwarzać wiele masek jednocześnie, na przykład pliki .ini, .cfg i .conf. W praktyce for /R jest używana do zliczania plików, zbierania statystyk i masowych operacji na plikach. Rekurencyjne przeszukiwanie jest wydajne, ale przy bardzo dużych strukturach katalogów może działać wolno.

Należy pamiętać, że for /R przeszukuje tylko podkatalogi, a nie same katalogi. Jeśli potrzebujemy przetwarzać również nazwy katalogów, musimy użyć polecenia dir z opcjami rekurencyjnymi i przetworzyć jego wynik za pomocą for /F.

33Tworzenie i obsługa tablic (pseudo-tablic)

Brak natywnych tablic w CMD

CMD/BAT nie ma natywnych tablic, ale można je symulować zmiennymi z indeksami: ELEMENT[0], ELEMENT[1].

Zasady symulacji tablic

  • Nazwy z [indeks]
  • set "TAB[0]=wartosc"
  • Dostęp przez !TAB[0]! (wymaga opóźnionego rozszerzania)
  • Pętla for do iteracji

Ograniczenia

  • Brak natywnych funkcji sortowania, wyszukiwania
  • Iteracja wymaga opóźnionego rozszerzania
@echo off
setlocal EnableDelayedExpansion

echo === Symulacja tablic ===
echo.

:: Definiowanie "tablicy" uzytkownikow
set "USER[0]=Jan Kowalski"
set "USER[1]=Anna Nowak"
set "USER[2]=Piotr Wisniewski"
set "USER[3]=Katarzyna Wozniak"
set "COUNT=4"

echo --- Wyswietlanie ---
for /L %%i in (0,1,3) do (
    echo Element %%i: !USER[%%i]!
)
echo.

echo --- Wyszukiwanie ---
set "SZUKANY=Anna Nowak"
for /L %%i in (0,1,3) do (
    if "!USER[%%i]!"=="%SZUKANY%" (
        echo Znalazlem: !USER[%%i]! na pozycji %%i
    )
)
echo.

echo --- Zmiana elementu ---
set "USER[1]=Anna Maria Nowak"
echo Zmieniony element 1: !USER[1]!

pause
endlocal

Mimo że cmd.exe nie oferuje natywnych tablic, można je symulować za pomocą odpowiedniego nazewnictwa zmiennych. Najpopularniejsza metoda polega na tworzeniu zmiennych z indeksami w nawiasach kwadratowych, na przykład USER[0], USER[1] itd. Dostęp do elementów tablicy wymaga opóźnionego rozszerzania zmiennych (wykrzykniki), ponieważ indeks jest zwykle przechowywany w innej zmiennej. Pętla for /L z licznikiem pozwala na iterację po wszystkich elementach tablicy. Wyszukiwanie elementu w tablicy wymaga liniowego przeglądania wszystkich elementów. Modyfikacja elementu po indeksie jest prosta i polega na ponownym przypisaniu wartości. Liczba elementów w tablicy musi być przechowywana w osobnej zmiennej, ponieważ cmd.exe nie oferuje mechanizmu zliczania. W praktyce tablica symulowana wystarcza do większości zastosowań w skryptach wsadowych.

W przypadku potrzeby bardziej zaawansowanych struktur danych warto rozważyć użycie PowerShella, który oferuje pełne wsparcie dla tablic, list i słowników. Tablice symulowane w cmd.exe są przeznaczone raczej dla prostych zastosowań.

34Zmienne środowiskowe i setx

Zmienne środowiskowe w Windows

  • Systemowe (Machine) - widoczne dla wszystkich
  • Użytkownika (User) - widoczne tylko dla aktualnego
  • Sesji (Process) - tymczasowe dla bieżącej sesji

Polecenia do obsługi

  • set ZMIENNA=wartosc - tymczasowo (tylko sesja)
  • setx ZMIENNA "wartosc" - trwale (User)
  • setx ZMIENNA "wartosc" /M - trwale (Machine, wymaga admina)

Uwaga: setx nie wpływa na bieżącą sesję! Zmiany widoczne po otwarciu nowego okna.

@echo off
setlocal

echo === Praca ze zmiennymi srodowiskowymi ===
echo.

:: Wyswietlenie wybranych zmiennych
echo Zmienna USERNAME: %USERNAME%
echo Zmienna COMPUTERNAME: %COMPUTERNAME%
echo Zmienna USERPROFILE: %USERPROFILE%
echo.

:: Ustawienie tymczasowe (tylko sesja)
set MOJA_ZMIENNA=TestowaWartosc
echo Ustawilem MOJA_ZMIENNA=%MOJA_ZMIENNA%

:: Odczyt z rejestru (trwale zmienne User)
echo.
echo Zmienna PATH (User) z rejestru:
reg query "HKCU\Environment" /v PATH 2>nul

:: setx - ODKOMENTUJ JESLI CHCESZ UZYC
:: setx MOJA_ZMIENNA "NowaWartosc"
:: Ustawienie systemowe (wymaga admina):
:: setx MOJA_ZMIENNA "NowaWartosc" /M

echo.
echo UWAGA: setx ustawia trwale ale nie wplywa na biezaca sesje.

pause
endlocal

Zmienne środowiskowe w Windows dzielą się na trzy poziomy: maszyny (widoczne dla wszystkich użytkowników), użytkownika (tylko dla bieżącego) i sesji (tymczasowe dla bieżącego procesu). Polecenie set zmienia zmienne tylko w bieżącej sesji cmd.exe, a zmiany są tracone po zamknięciu okna. Polecenie setx zapisuje zmienne trwale w rejestrze systemowym, ale zmiany są widoczne dopiero w nowo otwartych sesjach. setx z parametrem /M wymaga uprawnień administratora i zmienia zmienne na poziomie maszyny. Zmienne środowiskowe są dziedziczone przez wszystkie procesy potomne, co pozwala na przekazywanie konfiguracji bez plików. Zmienna PATH jest jedną z najważniejszych, ponieważ określa, gdzie system szuka plików wykonywalnych. Modyfikacja PATH za pomocą setx wymaga ostrożności, ponieważ błędna wartość może uniemożliwić uruchamianie programów.

W środowiskach korporacyjnych zmienne środowiskowe są często ustawiane przez Group Policy, co pozwala na centralne zarządzanie konfiguracją. Skrypty wsadowe mogą odczytywać te zmienne i dostosowywać swoje działanie do polityk organizacji.

35Polecenia sterujące: timeout, choice, color

timeout - pauza z automatycznym wznowieniem

timeout /t sekundy [/nobreak]

  • /t sekundy - czas oczekiwania
  • /nobreak - ignoruj naciśnięcie klawisza

choice - wybór z menu

choice /c opcje /m "tekst"

  • /c opcje - dozwolone klawisze, np. TNP
  • /m "tekst" - tekst zachęty
  • %ERRORLEVEL% zwraca numer wybranej opcji

color - kolory

color kod - kod to dwie cyfry szesnastkowe (tekst + tło).

@echo off
setlocal

title Demonstracja polecen sterujacych

:: Zmiana kolorow
color 17
echo Kolor niebieski na bialym
timeout /t 2 > nul
color 07
echo Powrot do zwyklych kolorow.
echo.

:: timeout z czekaniem
echo Za chwile nastapi odliczanie...
timeout /t 5 /nobreak
echo Czas minal!
echo.

:: choice - interaktywne menu
echo Wybierz opcje:
choice /c TNP /m "T-tak, N-nie, P-pomoc"

if %ERRORLEVEL% equ 1 (
    echo Wybrano: T - kontynuuj!
) else if %ERRORLEVEL% equ 2 (
    echo Wybrano: N - anulowano.
) else (
    echo Wybrano: P - wyswietlam pomoc...
)

pause
endlocal

Polecenia sterujące, takie jak timeout, choice i color, pozwalają na tworzenie bardziej przyjaznych użytkownikowi skryptów wsadowych. timeout jest nowocześniejszą alternatywą dla pause, oferującą automatyczne odliczanie i możliwość ignorowania naciśnięć klawiszy. Parametr /nobreak zapobiega przerwaniu odliczania przez użytkownika, co jest przydatne w skryptach automatycznych. choice oferuje bardziej zaawansowaną interakcję, pozwalając na zdefiniowanie dostępnych opcji i przechwycenie wyboru użytkownika. Kod wybranej opcji jest zwracany w %ERRORLEVEL%, co pozwala na warunkowe wykonanie dalszych działań. Polecenie color zmienia kolory tekstu i tła w oknie konsoli, co może poprawić czytelność komunikatów. W praktyce timeout jest często używany do wstrzymywania skryptów między operacjami, a choice do implementacji prostych menu.

Kody kolorów dla polecenia color składają się z dwóch cyfr szesnastkowych – pierwsza określa kolor tła, druga kolor tekstu. Warto eksperymentować z różnymi kombinacjami, ale należy unikać zestawień utrudniających czytelność.

36Operacje na plikach i katalogach

Podstawowe polecenia do operacji na plikach

  • copy źródło cel - kopiuje pliki
  • move źródło cel - przenosi/zmmienia nazwę
  • del /q plik - usuwa plik
  • ren stara nowa - zmienia nazwę
  • type plik - wyświetla zawartość

Operacje na katalogach

  • mkdir / md katalog - tworzy katalog
  • rmdir / rd katalog - usuwa pusty katalog
  • rmdir /s /q katalog - usuwa z zawartością
  • pushd katalog - zapisuje i zmienia
  • popd - wraca do poprzedniego
@echo off
setlocal EnableDelayedExpansion

:: Tworzenie struktury katalogow
echo Tworze tymczasowa strukture...
mkdir "test_dir" 2>nul
mkdir "test_dir\subdir1" 2>nul
mkdir "test_dir\subdir2" 2>nul
echo.

:: Tworzenie plikow testowych
echo Tworze pliki...
echo Zawartosc pliku 1 > "test_dir\plik1.txt"
echo Zawartosc pliku 2 > "test_dir\plik2.txt"
echo Zawartosc pliku 3 > "test_dir\subdir1\plik3.txt"
echo.

:: Kopiowanie
echo Kopiowanie *.txt do subdir2...
copy "test_dir\*.txt" "test_dir\subdir2\" > nul

:: Lista plikow
echo Pliki w test_dir\subdir2:
for %%F in ("test_dir\subdir2\*") do (
    echo   %%~nxF
)
echo.

:: Usuwanie calej struktury
echo Usuwam strukture...
rmdir /s /q "test_dir"
echo Usunięto.

pause
endlocal

Operacje na plikach i katalogach stanowią rdzeń większości skryptów administracyjnych w systemie Windows. Polecenia takie jak copy, move, del i ren są proste w użyciu, ale oferują różne opcje wpływające na ich zachowanie. Polecenie xcopy jest rozszerzoną wersją copy, oferującą kopiowanie z podkatalogami i zachowywaniem atrybutów. mkdir tworzy katalogi, a rmdir je usuwa, przy czym rmdir /s /q usuwa całe drzewo katalogów bez pytania o potwierdzenie. pushd zapamiętuje bieżący katalog i zmienia go na podany, a popd przywraca zapamiętany katalog. Są one szczególnie przydatne w skryptach nawigujących po strukturze katalogów. W praktyce operacje na plikach wymagają zawsze sprawdzenia, czy źródło i cel istnieją, oraz obsługi możliwych błędów. Profesjonalne skrypty używają również przekierowań do ukrywania zbędnych komunikatów.

Nowoczesne systemy Windows oferują również polecenie robocopy, które jest bardziej zaawansowane niż xcopy. Robocopy obsługuje wielowątkowe kopiowanie, automatyczne ponawianie przy błędach i szczegółowe raportowanie. Jest to preferowane narzędzie do tworzenia kopii zapasowych.

37Debugowanie skryptów CMD

Techniki debugowania

Debugowanie skryptów wsadowych jest trudniejsze, ale istnieją techniki pomocnicze:

Włączanie echo

  • @echo on - wyświetla każde polecenie
  • echo DEBUG: zmienna = %ZMIENNA%
  • pause - zatrzymuje wykonanie

Funkcja trace

Utwórz funkcję logującą, którą można włączać/wyłączać:

Przydatne triki

  • Komentowanie fragmentów (::)
  • Wyświetlanie zmiennych przed kluczowymi operacjami
  • echo.%CMDCMDLINE% - pełna linia poleceń
@echo off
setlocal EnableDelayedExpansion

:: Wlacz/wylacz debugowanie
set "DEBUG=1"
:: DEBUG=0 - wylaczone

goto :start

:debug
if %DEBUG% equ 1 (
    echo [DEBUG] %~1
)
goto :eof

:start
call :debug "Rozpoczynam skrypt"
echo.

set "TEST_VAR=wartosc_testowa"
call :debug "TEST_VAR = !TEST_VAR!"
echo.

call :debug "Sprawdzam ERRORLEVEL..."
call :debug "ERRORLEVEL = !ERRORLEVEL!"

if %DEBUG% equ 1 (
    echo.
    echo ==========================
    echo Stan zmiennych:
    echo DEBUG = !DEBUG!
    echo TEST_VAR = !TEST_VAR!
    echo ==========================
)

pause
endlocal

Debugowanie skryptów wsadowych jest trudniejsze niż w innych językach, ponieważ cmd.exe nie oferuje wbudowanego debuggera. Podstawową techniką jest włączanie wyświetlania poleceń za pomocą @echo on, co pokazuje każde wykonywane polecenie wraz z rozwiniętymi zmiennymi. Dodawanie tymczasowych poleceń echo wyświetlających wartości zmiennych w kluczowych punktach skryptu pozwala na śledzenie przepływu danych. Polecenie pause umieszczone w odpowiednich miejscach umożliwia zatrzymanie skryptu i analizę stanu w danym momencie. Tworzenie funkcji debugowania z warunkowym wyświetlaniem komunikatów to profesjonalna technika, która pozwala na włączanie i wyłączanie logowania bez modyfikacji całego kodu. Komentowanie podejrzanych fragmentów za pomocą :: pozwala na izolowanie i testowanie poszczególnych części skryptu. W praktyce najczęstszym źródłem błędów są problemy z cytowaniem i zasięgiem zmiennych, które można wykryć właśnie przez echo.

Warto również używać polecenia echo.%CMDCMDLINE% do wyświetlenia dokładnej linii poleceń, która uruchomiła skrypt. Pomaga to w diagnostyce problemów z przekazywaniem argumentów i cudzysłowami.

38Bezpieczeństwo skryptów - dobre praktyki

Zasady bezpiecznego pisania skryptów

  1. Zawsze używaj setlocal
  2. Sprawdzaj dane wejściowe
  3. Używaj cudzysłowów wokół zmiennych i ścieżek
  4. Usuwaj pliki tymczasowe w przypadku błędów
  5. Obsługuj błędy - sprawdzaj ERRORLEVEL
  6. Unikaj hardkodowanych haseł
  7. Testuj na bezpiecznym środowisku

Ochrona przed atakami

  • Injection - nie używaj bezpośrednio danych użytkownika
  • Path Traversal - waliduj ścieżki, nie pozwalaj na ../
@echo off
setlocal

set "SCRIPT_NAME=%~nx0"
set "ERRORS=0"

:: 1. Sprawdzenie argumentow
if "%~1"=="" (
    echo Blad: Brak argumentow.
    set /a ERRORS+=1
    goto :show_usage
)

:: 2. Sprawdzenie czy plik istnieje
if not exist "%~1" (
    echo Blad: Plik nie istnieje.
    set /a ERRORS+=1
    goto :cleanup
)

:: 3. Walidacja sciezki (brak ..)
echo %~f1 | findstr /C:".." > nul
if not errorlevel 1 (
    echo Błąd: Nieprawidłowa ścieżka.
    set /a ERRORS+=1
    goto :cleanup
)

set "SAFE_FILE=%~nx1"
echo Przetwarzanie: %SAFE_FILE%

:: 4. Obsluga bledow
copy "%SAFE_FILE%" "backup_%SAFE_FILE%" > nul 2>&1
if errorlevel 1 (
    echo Blad podczas kopiowania.
    set /a ERRORS+=1
)

:cleanup
echo.
if %ERRORS% equ 0 (
    echo Status: SUKCES
) else (
    echo Status: BLEDY (%ERRORS%)
    exit /b 1
)

:show_usage
echo Uzycie: %SCRIPT_NAME% ^

endlocal

Bezpieczeństwo skryptów wsadowych jest często pomijane, a nieprawidłowo napisany skrypt może stanowić poważne zagrożenie dla systemu. Podstawową zasadą jest używanie setlocal na początku każdego skryptu, co zapobiega zaśmiecaniu środowiska zmiennymi. Walidacja danych wejściowych jest kluczowa, ponieważ użytkownik może przekazać dane zawierające znaki specjalne, które zmienią zachowanie skryptu. Ataki typu command injection polegają na przekazaniu dodatkowych poleceń w argumencie, które są wykonywane przez skrypt. Używanie cudzysłowów wokół wszystkich ścieżek i zmiennych zapobiega problemom ze spacjami i znakami specjalnymi. Usuwanie plików tymczasowych nawet w przypadku błędu jest ważne dla utrzymania czystości systemu. Sprawdzanie ERRORLEVEL po każdym krytycznym poleceniu zapobiega kaskadowym błędom. Przechowywanie haseł w skryptach to zła praktyka – lepiej używać bezpiecznych mechanizmów uwierzytelniania.

W środowiskach produkcyjnych warto rozważyć podpisanie skryptów certyfikatem cyfrowym oraz przechowywanie ich w lokalizacjach z ograniczonym dostępem. Skrypty uruchamiane z uprawnieniami administratora są szczególnie narażone na nadużycia.

39Praktyczny przykład: Generator logów

System logowania dla skryptów

Utwórzmy kompletny system logowania, który może być używany w dowolnym skrypcie:

  • Zapis do pliku i/lub na ekran
  • Różne poziomy (INFO, WARN, ERROR)
  • Automatyczne znaczniki czasu
  • Konfigurowalne wyjście

Jak używać

Skopiuj definicje funkcji na początek skryptu i wywołuj call :log "tekst".

@echo off
setlocal EnableDelayedExpansion

:: ============================================
:: SYSTEM LOGOWANIA
:: ============================================
set "LOG_FILE="
set "LOG_TO_FILE=0"
set "LOG_TO_SCREEN=1"

:init_log
set "LOG_FILE=%~1"
if defined LOG_FILE set "LOG_TO_FILE=1"
goto :eof

:log
set "TIMESTAMP=%DATE% %TIME%"
if !LOG_TO_SCREEN! equ 1 (
    echo !TIMESTAMP! %~1 %~2
)
if !LOG_TO_FILE! equ 1 (
    echo !TIMESTAMP! %~1 %~2 >> "!LOG_FILE!"
)
goto :eof

:: ============================================
:: UZYCIE SYSTEMU LOGOWANIA
:: ============================================

call :init_log "moj_log.txt"

call :log "[INFO]" "Rozpoczynam skrypt"
call :log "[INFO]" "Wykonywanie operacji..."

set /a TEST=10/2
call :log "[INFO]" "Wynik: !TEST!"

call :log "[ERROR]" "Nie udalo sie wykonac operacji!"

call :log "[INFO]" "Koniec skryptu"

echo.
echo Log zapisany do: moj_log.txt
type moj_log.txt

pause
endlocal

System logowania to niezbędny element każdego profesjonalnego skryptu wsadowego, umożliwiający śledzenie jego działania i diagnozowanie problemów. Przedstawiony w przykładzie system logowania oferuje zapis komunikatów zarówno do pliku, jak i na ekran, z możliwością konfiguracji tych opcji. Znaczniki czasu dodawane do każdego wpisu pozwalają na precyzyjne odtworzenie sekwencji zdarzeń. Różne poziomy logowania (INFO, WARN, ERROR) umożliwiają filtrowanie komunikatów wg ważności. Konfigurowalne wyjście pozwala na dostosowanie systemu do konkretnych potrzeb bez modyfikacji głównego kodu. W praktyce system logowania jest szczególnie przydatny w skryptach uruchamianych automatycznie, gdzie nie ma możliwości na bieżąco obserwować ich działania. Profesjonalne skrypty powinny zawsze tworzyć logi w przypadku błędów.

Dobrą praktyką jest przechowywanie logów w oddzielnym katalogu z nazwą zawierającą datę, co ułatwia późniejsze przeszukiwanie i archiwizację. Warto również rozważyć czyszczenie starych logów, aby nie zajmowały niepotrzebnie miejsca na dysku.

40Praktyczny projekt: Skrypt backup z znacznikiem czasu

Kopia zapasowa z automatycznym znacznikiem czasu

Kompletny skrypt do tworzenia kopii zapasowych:

  1. Sprawdza poprawność argumentów
  2. Generuje znacznik czasu
  3. Kopiuje pliki z zachowaniem struktury
  4. Obsługuje błędy

Użycie

backup.cmd plik_lub_katalog [katalog_docelowy]

@echo off
setlocal EnableDelayedExpansion

set "SCRIPT_NAME=%~nx0"
set "DEFAULT_BACKUP=%USERPROFILE%\Documents\Backup"

:: --- Walidacja argumentow ---
if "%~1"=="" (
    echo Blad: Nie podano zrodla!
    echo Uzycie: %SCRIPT_NAME% ^ [cel]
    exit /b 1
)

set "SOURCE=%~f1"
if not exist "%SOURCE%" (
    echo Blad: Zrodlo nie istnieje!
    exit /b 1
)

set "DEST=%~f2"
if "%~2"=="" set "DEST=%DEFAULT_BACKUP%"

:: --- Generowanie znacznika czasu ---
set "TS=%DATE:~-4%%DATE:~-7,2%%DATE:~-10,2%"
set "TS=%TS%_%TIME:~0,2%%TIME:~3,2%%TIME:~6,2%"
set "TS=!TS: =0!"

:: --- Tworzenie kopii ---
set "BACKUP_NAME=%~n1_backup_%TS%%~x1"
set "BACKUP_PATH=%DEST%\%BACKUP_NAME%"

echo Tworze kopie zapasowa...
echo Zrodlo: %SOURCE%
echo Cel: %BACKUP_PATH%

if exist "%SOURCE%\*" (
    mkdir "!DEST!" 2>nul
    xcopy /e /i /h /y "%SOURCE%" "!BACKUP_PATH!\" > nul 2>&1
) else (
    mkdir "!DEST!" 2>nul
    copy /y "%SOURCE%" "!BACKUP_PATH!" > nul 2>&1
)

if !ERRORLEVEL! equ 0 (
    echo SUKCES: Kopia utworzona!
) else (
    echo BLAD: Nie udalo sie utworzyc kopii.
    exit /b 1
)

endlocal

Projekt skryptu backup z automatycznym znacznikiem czasu to praktyczne zastosowanie wiedzy zdobytej podczas kursu. Skrypt wykorzystuje operacje na podciągach do wygenerowania znacznika czasu z daty i czasu systemowego. Walidacja argumentów na początku skryptu zapobiega błędom przy nieprawidłowym wywołaniu. Sprawdzenie, czy źródło istnieje, jest krytyczne, ponieważ kopiowanie nieistniejącego pliku zakończyłoby się błędem. xcopy z opcjami /e, /i, /h, /y pozwala na kopiowanie katalogów z zachowaniem struktury i atrybutów. Obsługa błędów na każdym etapie skryptu zapewnia, że użytkownik otrzyma jasny komunikat w przypadku problemu. Znacznik czasu w nazwie pliku backupu zapewnia unikalność i ułatwia identyfikację interesującej nas kopii. Taki skrypt może być używany w codziennej pracy administratora do tworzenia szybkich kopii zapasowych.

Skrypt można rozbudować o dodatkowe funkcje, takie jak kompresja archiwum (zip), szyfrowanie kopii czy wysyłanie powiadomienia e-mail po zakończeniu. Podstawowa wersja stanowi dobry punkt wyjścia do tworzenia bardziej zaawansowanych narzędzi.

41Praktyczny projekt: Menu instalacyjne

Skrypt instalacyjny z menu

Profesjonalny skrypt z:

  • Sprawdzanie uprawnień administratora
  • Wybór katalogu instalacji
  • Tworzenie struktury katalogów
  • Kopiowanie plików
  • Tworzenie skrótów
  • Rejestrowanie w autostarcie
@echo off
setlocal EnableDelayedExpansion

set "APP_NAME=MojaAplikacja"
set "APP_VERSION=1.0.0"

:: Sprawdzenie uprawnien admina
net session >nul 2>&1
if %errorlevel% neq 0 (
    echo.
    echo UWAGA: Ten skrypt wymaga uprawnien admina!
    echo Kliknij prawym i wybierz "Uruchom jako admin"
    pause
    exit /b 1
)

echo.
echo ============================================
echo   INSTALATOR: %APP_NAME% v%APP_VERSION%
echo ============================================
echo.

set /p INSTALL_DIR="Katalog instalacji (domyslnie C:\%APP_NAME%): "
if not defined INSTALL_DIR set "INSTALL_DIR=C:\%APP_NAME%"

echo.
echo Tworze katalogi...
mkdir "%INSTALL_DIR%" 2>nul
mkdir "%INSTALL_DIR%\config" 2>nul
mkdir "%INSTALL_DIR%\logs" 2>nul
mkdir "%INSTALL_DIR%\data" 2>nul
echo Katalogi utworzone.

echo.
echo Tworze plik konfiguracyjny...
echo # Konfiguracja > "%INSTALL_DIR%\config\ustawienia.ini"
echo [app] >> "%INSTALL_DIR%\config\ustawienia.ini"
echo version=%APP_VERSION% >> "%INSTALL_DIR%\config\ustawienia.ini"
echo.

echo === INSTALACJA ZAKONCZONA ===
echo.
echo Zainstalowano w: %INSTALL_DIR%
echo.

pause
endlocal

Skrypt instalacyjny z menu to przykład profesjonalnego narzędzia, które może być używane do wdrażania aplikacji w środowisku korporacyjnym. Sprawdzenie uprawnień administratora za pomocą net session jest standardową techniką, ponieważ wiele operacji instalacyjnych wymaga podwyższonych uprawnień. Tworzenie struktury katalogów z organizacją na podkatalogi (config, logs, data) to dobra praktyka, która ułatwia utrzymanie aplikacji. Plik konfiguracyjny w formacie .ini jest prostym sposobem na przechowywanie ustawień aplikacji. Interaktywne pytanie o katalog instalacji z wartością domyślną zwiększa wygodę użytkowania. Skrypt instalacyjny może być rozbudowany o kopiowanie plików źródłowych, tworzenie skrótów i rejestrację w autostarcie. W praktyce takie skrypty są często używane do wdrażania aplikacji wewnętrznych w firmach. Automatyzacja instalacji oszczędza czas i redukuje ryzyko błędów ludzkich przy ręcznej instalacji.

W nowoczesnych środowiskach wdrożeniowych coraz częściej zamiast skryptów wsadowych używa się narzędzi takich jak SCCM, Group Policy Software Installation lub PowerShell Desired State Configuration. Skrypty wsadowe wciąż jednak sprawdzają się w prostszych scenariuszach.

42Zaawansowane: Rekurencyjne przeszukiwanie i operacje

Zaawansowane operacje na plikach

Łączenie pętli for /R z warunkami if pozwala na zaawansowane operacje:

  • Wyszukiwanie plików spełniających warunki
  • Zliczanie i sumowanie rozmiarów
  • Masowe zmiany nazw
  • Filtrowanie po dacie

Przykładowe zastosowania

  • Znajdowanie starych plików i ich archiwizacja
  • Zbieranie statystyk o katalogach
  • Automatyczne porządki w folderze pobrań
@echo off
setlocal EnableDelayedExpansion

set "START_DIR=%CD%"
set /a PLIKOW=0
set /a SUMA_ROZMIAR=0

echo === Analiza katalogow ===
echo Szukam wszystkich plikow w %START_DIR%...
echo.

for /R "%START_DIR%" %%F in (*) do (
    set /a PLIKOW+=1
    set /a SUMA_ROZMIAR+=%%~zF
    :: Wyswietl tylko pliki wieksze niz 1MB
    if %%~zF GTR 1048576 (
        echo Duzy plik: %%~nxF (%%~zF bajtow)
    )
)

echo.
echo ============================================
echo Podsumowanie:
echo Liczba plikow: !PLIKOW!
echo Suma rozmiarow: !SUMA_ROZMIAR!
set /a SREDNI=SUMA_ROZMIAR/PLIKOW
echo Sredni rozmiar: !SREDNI!
echo ============================================

pause
endlocal

Zaawansowane operacje na plikach z użyciem pętli for /R i warunków if pozwalają na tworzenie potężnych narzędzi do analizy systemu plików. Przetwarzanie każdego pliku w drzewie katalogów z jednoczesnym sprawdzaniem jego rozmiaru umożliwia identyfikację największych plików zajmujących miejsce na dysku. Zliczanie plików i sumowanie ich rozmiarów to podstawowe operacje używane w skryptach raportujących stan systemu. Filtrowanie plików po rozmiarze (na przykład większe niż 1 MB) pozwala na skupienie się na interesującej nas kategorii danych. W praktyce takie skrypty są używane do monitorowania dysków serwerów i wykrywania anomalii w strukturze plików. Masowe zmiany nazw plików według wzorca to kolejne częste zastosowanie. Automatyczne porządkowanie folderu pobrań czy dokumentów to przykład codziennego użycia takich skryptów.

Przy bardzo dużych strukturach katalogów (ponad 100000 plików) skrypty wsadowe mogą działać wolno. W takich przypadkach warto rozważyć użycie PowerShella lub wyspecjalizowanych narzędzi do analizy systemu plików.

43Zaawansowane: Praca z datą i czasem

Manipulacja datą i czasem

Zmienne %DATE% i %TIME% zwracają ciągi, które można parsować za pomocą operacji na podciągach.

Formaty daty (zależne od ustawień systemu)

  • 2025-03-21 (yyyy-mm-dd)
  • 21.03.2025 (dd.MM.yyyy)
  • 21-03-2025 (dd-MM-yyyy)

Format czasu

%TIME% zwraca np. 14:30:45,12 - godziny, minuty, sekundy, setne części sekundy.

Uwaga!

Format daty zależy od ustawień regionalnych Windows. Zawsze testuj skrypty na docelowym systemie!

@echo off
setlocal EnableDelayedExpansion

echo === Analiza daty i czasu ===
echo.

echo Systemowa data: %DATE%
echo Systemowy czas: %TIME%
echo.

:: Parsowanie daty (format: 2025-03-21)
:: UWAGA: format moze byc inny w zaleznosci od ustawien!
set "D=%DATE%"
echo Próbne parsowanie: !D!

:: Sprobuj wyciagnac rok, miesiac, dzien
:: To zalezy od formatu systemowego!
set "ROK=%DATE:~0,4%"
set "MIESIAC=%DATE:~5,2%"
set "DZIEN=%DATE:~8,2%"
echo Wydzielone: !ROK!-!MIESIAC!-!DZIEN!
echo.

:: Parsowanie czasu
set "GODZ=%TIME:~0,2%"
set "MIN=%TIME:~3,2%"
set "SEK=%TIME:~6,2%"
echo Czas: !GODZ!:!MIN!:!SEK!
echo.

:: Znacznik czasu do nazwy pliku (bez spacji)
set "TS=%DATE:~-4%%DATE:~-7,2%%DATE:~-10,2%_%TIME:~0,2%%TIME:~3,2%%TIME:~6,2%"
set "TS=!TS: =0!"
echo Znacznik do nazwy pliku: !TS!

pause
endlocal

Praca z datą i czasem w skryptach wsadowych wymaga uwzględnienia ustawień regionalnych systemu, które wpływają na format zwracany przez zmienne %DATE% i %TIME%. W polskiej wersji Windows format daty to zazwyczaj RRRR-MM-DD lub DD.MM.RRRR, podczas gdy w angielskiej może to być MM/DD/RRRR. Parsowanie daty za pomocą operacji na podciągach wymaga znajomości konkretnego formatu, co utrudnia tworzenie przenośnych skryptów. Znacznik czasu do nazwy pliku generuje się przez odpowiednie wycięcie fragmentów daty i czasu i połączenie ich w jeden ciąg bez spacji. Godzina może zawierać spację na początku (dla wartości jednocyfrowych), co wymaga zastąpienia jej zerem. Praktyczne zastosowania obejmują tworzenie unikalnych nazw plików backupu, logów i raportów. W skryptach produkcyjnych warto używać ustandaryzowanego formatu daty ISO 8601, który jest jednoznaczny niezależnie od lokalizacji.

Alternatywnym podejściem jest użycie polecenia wmic (Windows Management Instrumentation) do pobrania daty w ustandaryzowanym formacie, niezależnym od ustawień regionalnych. Jest to jednak wolniejsze i wymaga dostępności usługi WMI w systemie.

44Porównanie CMD z PowerShell i BASH

CMD vs PowerShell vs BASH

Każda powłoka ma swoje zalety i ograniczenia:

CechaCMDPowerShellBASH
Poziom trudnościNiskiSredni-WysokiSredni
ObiektowośćBrakPełnaBrak
Manipulacja tekstemOgraniczonaBardzo dobraBardzo dobra
Dostęp do .NETBrakPełnyBrak
WieloplatformowośćTylko WindowsWindows, Linux, MacWszystkie

Kiedy używać CMD?

  • Proste zadania automatyzacji w Windows
  • Skrypty uruchamiane na starszych systemach
  • Szybkie prototypowanie
:: CMD - proste i skuteczne dla podstawowych zadan
@echo off
setlocal
set "PLIK=%1"
if exist "%PLIK%" (
    echo Plik istnieje: %PLIK%
) else (
    echo Plik nie istnieje.
)
endlocal

:: PowerShell - lepsze dla zlozonych operacji
:: Get-Process | Where-Object {$_.CPU -gt 100}

:: BASH - dobre dla skryptow wieloplatformowych
:: if [ -f "$1" ]; then echo "File exists"; fi

Porównanie cmd.exe z PowerShell i BASH pozwala zrozumieć, kiedy warto używać poszczególnych narzędzi do automatyzacji w systemie Windows. CMD jest najprostszy i najlepszy do szybkich zadań, szczególnie na starszych systemach lub w środowiskach, gdzie PowerShell nie jest dostępny. PowerShell oferuje obiektowe podejście do danych, co znacznie ułatwia złożone operacje na plikach, rejestrze i usługach systemowych. BASH jest standardem w systemach Linux i macOS, ale dostępny również w Windows przez WSL (Windows Subsystem for Linux). Każda powłoka ma swoje mocne strony – CMD w prostocie i kompatybilności, PowerShell w możliwościach programistycznych, a BASH w przenośności. Wybór odpowiedniej powłoki zależy od konkretnego zadania, środowiska docelowego i umiejętności zespołu. W wielu organizacjach stosuje się mieszane podejście, używając różnych powłok do różnych zadań.

Znajomość wszystkich trzech powłok jest cenną umiejętnością dla administratora systemów, ponieważ pozwala na elastyczne dostosowanie narzędzi do konkretnych wymagań. Warto uczyć się PowerShella jako nowoczesnej alternatywy dla CMD, ale nie należy całkowicie rezygnować ze znajomości tradycyjnych skryptów wsadowych.

45Podsumowanie i źródła do dalszej nauki

Co omówiliśmy

  • Podstawy skryptów .bat/.cmd
  • Zmienne lokalne i globalne (setlocal)
  • Argumenty wiersza poleceń i modyfikatory
  • Przekazywanie ścieżek do plików/katalogów
  • Pętle for (podstawowa, /L, /F, /R)
  • Instrukcje warunkowe if
  • Podprogramy z call
  • Przekierowania i potoki
  • Obsługa błędów i kody powrotu
  • Dobre praktyki i bezpieczeństwo

Źródła do dalszej nauki

  • SS64 (dokumentacja poleceń): ss64.com/nt
  • Microsoft Learn: learn.microsoft.com
  • Rob van der Woude: robvanderwoude.com
  • Stack Overflow: stackoverflow.com/questions/tagged/batch-file
:: ============================================
:: SPRAWDZ WIEDZE - przykładowe zadania
:: ============================================

:: 1. Napisz skrypt przyjmujacy 2 liczby i wyswietlajacy ich sume

:: 2. Napisz skrypt ktory sprawdza czy plik istnieje
::    i wyswietla informacje o nim

:: 3. Napisz skrypt backupu z automatycznym
::    znacznikiem czasu

:: 4. Napisz menu z trzema opcjami:
::    - Wyswietl date
::    - Wyswietl liste plikow
::    - Zakoncz

:: 5. Napisz skrypt wyszukujacy wszystkie pliki
::    .txt w katalogu i podkatalogach

:: Powodzenia!

Podsumowanie kursu skryptów wsadowych cmd.exe obejmuje wszystkie kluczowe zagadnienia potrzebne do samodzielnego tworzenia skryptów automatyzujących w systemie Windows. Od podstawowych poleceń, przez zmienne i pętle, aż po zaawansowane techniki, takie jak opóźnione rozszerzanie i przekazywanie zmiennych przez endlocal. Zdobyta wiedza pozwala na pisanie skryptów do codziennych zadań administracyjnych, takich jak tworzenie kopii zapasowych, wdrażanie aplikacji czy monitorowanie systemu. Wskazane źródła zewnętrzne, takie jak SS64 i Rob van der Woude, są doskonałym uzupełnieniem materiału kursu i oferują szczegółową dokumentację wszystkich poleceń cmd.exe. Przykładowe zadania na końcu slajdu pozwalają na praktyczne sprawdzenie i utrwalenie zdobytej wiedzy. W codziennej pracy warto kontynuować naukę i śledzić nowe narzędzia do automatyzacji w systemie Windows. Umiejętność pisania skryptów wsadowych jest cenna nawet w erze PowerShella, ponieważ wiele starszych systemów wciąż polega na cmd.exe.

Dalszy rozwój w kierunku automatyzacji powinien obejmować naukę PowerShella oraz poznanie narzędzi DevOps, takich jak Ansible czy Puppet. Skrypty wsadowe cmd.exe stanowią doskonałą podstawę do zrozumienia ogólnych koncepcji programowania i automatyzacji.