Twój pierwszy skrypt
Po tej lekcji: kursant zamienia kilka komend wpisywanych ręcznie w plik-skrypt, który odpala jednym poleceniem, i rozumie, co się dzieje od wciśnięcia Entera do pierwszej linijki wyniku.
Wstęp
Nowy serwer przyszedł. Szef chce wiedzieć, co w nim siedzi. Wpisujesz sześć komend. Jutro przychodzi drugi serwer. Wpisujesz te same sześć komend. W piątek przychodzi dziesięć serwerów i nagle odkrywasz w sobie powołanie do hodowli alpak w Bieszczadach.
Albo robisz tak: wszystkie sześć komend zapisujesz raz w pliku. Od teraz wpisujesz jedno słowo i komputer sam robi resztę. Na każdym serwerze. Ile razy chcesz.
./raport.shTo jest skrypt. I dziś zrobimy z Twojego zadania domowego Twój pierwszy prawdziwy program. Bez instalowania czegokolwiek, bez kompilatora, w zwykłym edytorze tekstu.
Skrypt, czyli przepis kulinarny
Wyobraź sobie, że gotujesz rosół. Za pierwszym razem dzwonisz do babci i ona dyktuje: woda, mięso, marchewka, pietruszka, trzy godziny na małym ogniu. Za drugim razem znowu dzwonisz. Za trzecim babcia mówi: „Zapisz to sobie, dziecko”.
Skrypt to właśnie zapisany przepis. Zwykły plik tekstowy, w którym jedna pod drugą stoją komendy, dokładnie takie, jakie wpisujesz w terminalu. Bash czyta je po kolei, od góry do dołu, i wykonuje. Jak kucharz, który czyta przepis linijka po linijce.
Nie ma tu żadnej magii. Każdą komendę ze skryptu możesz wpisać ręcznie i zadziała tak samo. Skrypt tylko oszczędza Ci pisania i pomyłek. Bo człowiek o trzeciej w nocy myli > z >>, a plik się nie myli.
Piszemy raport.sh
Otwórz edytor. Może być nano, może być vi, może być nawet Midnight Commander, jeśli lubisz klikać klawiszami.
nano raport.shI wpisz:
#!/bin/bash
# raport.sh - zbiera podstawowe informacje o komputerze
# Autor: Ty. Wersja: 1. Data: dziś.
echo "=== Raport o maszynie ==="
date
echo "--- Nazwa komputera ---"
hostname
echo "--- Jądro ---"
uname -r
echo "--- Procesor ---"
LC_ALL=C lscpu | grep 'Model name' # LC_ALL=C: nagłówki po angielsku
nproc
echo "--- Pamięć ---"
free -h
echo "--- Dyski ---"
lsblkZapisz i wyjdź. W nano to Ctrl+O, Enter, Ctrl+X. W vi Esc, :wq. Jeśli nadal jesteś w vi, nie panikuj, lekcja dwunasty czeka.
Co tu jest nowego w porównaniu z zadaniem? Trzy rzeczy: pierwsza linijka z dziwnym #!, linijki z krzyżykiem na początku i echo z nagłówkami, żeby wynik dało się czytać bez lupy. A przy lscpu stoi LC_ALL=C. To mówi programowi: „Na ten jeden raz mów po angielsku”. Dzięki temu grep 'Model name' zadziała i na polskim, i na angielskim systemie.
Pierwsza linijka: #!
Pierwsza linijka to tak zwany shebang. Czyta się „szebang”, od angielskich nazw znaków: hash, czyli krzyżyk, i bang, czyli wykrzyknik.
To jest kartka przyklejona na przepisie: „Ten przepis jest dla kucharza od kuchni polskiej”. Kiedy system dostaje plik do uruchomienia, patrzy na pierwsze dwa znaki. Jeśli to #!, czyta resztę linijki i woła tego, kto tam jest wpisany. Tu: program /bin/bash. I to on dostaje Twój plik do przeczytania.
Możesz tam wpisać innego kucharza. #!/bin/sh to prosta, starsza powłoka, o której mówiliśmy w lekcji dziewiątym. #!/usr/bin/python3 to Python. Ten sam mechanizm, inny kucharz.
Jest jeszcze wersja, którą zobaczysz w wielu repozytoriach:
#!/usr/bin/env bashenv szuka Basha w folderach z PATH, zamiast zakładać, że leży w /bin. Przydaje się na systemach, gdzie Bash mieszka gdzie indziej, na przykład na Macu z nowszym Bashem z Homebrew. Na serwerach z Linuksem /bin/bash jest zawsze, więc obie wersje są w porządku.
macOS: systemowy /bin/bash na Macu to bardzo stara wersja 3.2, a domyślną powłoką jest zsh. Jeśli piszesz skrypty na Macu, #!/usr/bin/env bash z Bashem z Homebrew oszczędzi Ci niespodzianek.
Skąd się wziął ten znaczek? Dennis Ritchie, jeden z twórców Uniksa, napisał po latach, że pomysł podłapał od kogoś z konferencji o uniksie z Berkeley, a sam był chyba jednym z pierwszych, którzy to wdrożyli. Na początku lat osiemdziesiątych shebang działał już w systemach BSD. Czyli jest starszy niż większość adminów, którzy go dziś używają.
Komentarze, czyli notatki na marginesie
Wszystko po krzyżyku # Bash ignoruje. To notatki dla ludzi. Ołówkiem na marginesie przepisu: „Babcia daje więcej pieprzu”.
# To jest komentarz w osobnej linijce
nproc # a to komentarz na końcu komendyShebang też zaczyna się od krzyżyka, więc dla samego Basha jest zwykłym komentarzem. Czyta go nie Bash, tylko jądro systemu, zanim Bash w ogóle wystartuje. Sprytne, prawda? Jedna linijka, dwóch czytelników, każdy widzi w niej co innego.
Po co komentarze w sześciolinijkowym skrypcie? Bo za pół roku otworzysz ten plik i nie będziesz pamiętać, czemu tam jest LC_ALL=C. A za rok otworzy go ktoś inny i zapyta, kto to pisał. Komentarz w nagłówku od razu odpowie. Komentuj „dlaczego”, a nie „co”. Komentarz „wypisuje datę” nad date jest jak napis „drzwi” na drzwiach.
Pozwolenie na uruchomienie: chmod +x
Plik gotowy. Odpalamy:
./raport.shbash: ./raport.sh: Permission deniedNawet jako root. Pamiętasz klucze do pokojów z lekcji o uprawnieniach? Plik ma trzy rodzaje kluczy: do czytania, do pisania i do wykonywania, czyli r, w i x. Nowy plik z edytora dostaje tylko pierwsze dwa. System nie pozwoli uruchomić czegoś, co nie jest oznaczone jako program. I dobrze, bo inaczej każdy plik tekstowy z internetu byłby potencjalnym programem.
ls -l raport.sh
chmod +x raport.sh
ls -l raport.sh-rw-r--r-- 1 eryk eryk 382 Nov 3 16:24 raport.sh
-rwxr-xr-x 1 eryk eryk 382 Nov 3 16:24 raport.shPojawiły się iksy. chmod +x daje prawo wykonania wszystkim trzem: właścicielowi, grupie i reszcie (z poprawką na umask). Jeśli chcesz tylko dla siebie, chmod u+x. W cyferkach to zwykle chmod 755 albo chmod 700, jeśli skrypt jest prywatny.
I teraz:
./raport.sh=== Raport o maszynie ===
Tue Nov 3 16:24:51 CET 2026
--- Nazwa komputera ---
vm
--- Jądro ---
6.8.0-48-generic
--- Procesor ---
Model name: Intel(R) Xeon(R) Processor @ 2.10GHz
2
--- Pamięć ---
total used free shared buff/cache available
Mem: 7.8Gi 708Mi 6.8Gi 15Mi 604Mi 7.1Gi
...I to jest moment, w którym zostajesz programistą. Bez rozmowy kwalifikacyjnej, bez owocowych czwartków, ale jednak.
A jak chcesz wynik w pliku, to robisz to, co znasz od dawna:
./raport.sh > raport.txtJedna strzałka zamiast sześciu. Skrypt zachowuje się jak każda inna komenda: ma wyjście, da się je przekierować, wsadzić do rury, przepuścić przez grep.
Trzy sposoby uruchomienia
./raport.sh
bash raport.sh
raport.shPierwszy już znasz. Kropka i ukośnik znaczą „ten plik, tutaj, w bieżącym folderze”. Potrzebuje prawa x i shebanga.
Drugi to „Bashu, przeczytaj ten plik”. Tu prawo x nie jest potrzebne, bo uruchamiasz Basha, a nie plik. Plik jest tylko czytany, jak kartka podana urzędnikowi. Shebang wtedy nic nie znaczy. Gdybyś napisał sh raport.sh, przepis przeczyta inny kucharz, dash na Ubuntu, i w bardziej zaawansowanych skryptach coś się wysypie. Nasz akurat jest na tyle prosty, że zadziała.
Trzeci, bez kropki:
bash: raport.sh: command not foundBash nie szuka komend w bieżącym folderze. Szuka tylko w folderach z listy PATH, o której mówiliśmy przy ustawieniach Basha. To celowe zabezpieczenie: gdyby szukał w bieżącym folderze, ktoś mógłby podrzucić Ci do /tmp plik o nazwie ls i poczekać, aż tam wejdziesz.
Jeśli chcesz wołać swój skrypt jak zwykłą komendę, z każdego miejsca, wrzuć go do swojego prywatnego folderu na programy:
mkdir -p ~/bin
mv raport.sh ~/bin/raport[Ubuntu] domyślny ~/.profile dopisuje ~/bin i ~/.local/bin do PATH, ale tylko jeśli folder istnieje w chwili logowania. Czyli po założeniu folderu wyloguj się i zaloguj ponownie.
[RHEL] domyślne pliki startowe z /etc/skel też dopisują ~/.local/bin i ~/bin do PATH.
I jeszcze jedno: rozszerzenie .sh jest dla ludzi, nie dla systemu. Linux patrzy na shebang i prawo x, a nie na końcówkę nazwy. Komendy systemowe, które są skryptami, zwykle nie mają żadnego rozszerzenia.
Jak to działa pod spodem
Wciskasz Enter po ./raport.sh. Co się dzieje?
Bash, Twój urzędnik w okienku, rozdziela się na dwie kopie. To fork, znany z lekcji o procesach. Kopia prosi jądro: „Uruchom mi plik ./raport.sh”. To wywołanie systemowe execve.
Jądro sprawdza, czy masz prawo x. Potem czyta początek pliku. Widzi #! i według dokumentacji jądra uruchamia interpreter w taki sposób:
/bin/bash ./raport.shCzyli nowy Bash dostaje nazwę Twojego pliku jako argument. Ten nowy Bash czyta plik od góry. Pierwszą linijkę traktuje jak komentarz. Każdą kolejną wykonuje, czekając, aż skończy się poprzednia. Na końcu pliku kończy pracę, a Twój Bash w terminalu wraca z promptem.
Można to zobaczyć na własne oczy. Mały skrypt:
#!/bin/bash
# gdzie.sh - pokazuje, że skrypt to osobny proces
cd /tmp || exit
pwd
ps -o pid,ppid,cmd -p "$$" -p "$PPID"/home/eryk
/tmp
PID PPID CMD
6114 6109 bash
6115 6114 /bin/bash ./gdzie.sh
/home/erykPierwsze /home/eryk wypisałem ręcznie przed uruchomieniem, ostatnie po. W środku skrypt przeszedł do /tmp i to widzi. ps pokazuje dwa procesy: Twój Bash i pod nim osobny /bin/bash ./gdzie.sh, dokładnie tak, jak go uruchomiło jądro. A po skończeniu skryptu… dalej jesteś w swoim folderze domowym.
Dlaczego? Bo skrypt jest osobnym kucharzem w osobnej kuchni. Jeśli on przestawi garnki u siebie, Twoja kuchnia się nie zmienia. cd w skrypcie zmienia folder tylko skryptowi. Tak samo zmienne, które w nim ustawisz. Jeśli kiedyś naprawdę chcesz wykonać plik w swojej własnej powłoce, jest do tego source plik albo . plik, ale to temat na lekcja o dużych skryptach. || exit przy cd wyjaśnię za trzy lekcje, na razie przyjmij, że to pas bezpieczeństwa.
Ściąga flag
| Komenda | Flaga / forma | Co robi |
|---|---|---|
#!/bin/bash | pierwsza linijka | mówi jądru, kto ma czytać plik |
#!/usr/bin/env bash | pierwsza linijka | szuka Basha w PATH |
chmod | +x | daje prawo wykonania wszystkim |
chmod | u+x | daje prawo wykonania tylko właścicielowi |
chmod | 755 / 700 | publiczny / prywatny skrypt |
./skrypt.sh | — | uruchamia plik z bieżącego folderu (wymaga x) |
bash | skrypt.sh | Bash czyta plik (nie wymaga x) |
file | skrypt.sh | mówi, co to za plik i jakie ma końce linii |
cat | -A | pokazuje ukryte znaki, np. ^M na końcu linii |
shellcheck | skrypt.sh | sprawdza skrypt pod kątem błędów |
⚠ Typowy błąd
Piszesz skrypt na komputerze z Windowsem, w Notatniku, kopiujesz na serwer, dajesz chmod +x i:
bash: ./okna.sh: cannot execute: required file not foundJak to „not found”? Plik jest, Bash jest. Windows kończy linijki dwoma znakami: powrotem karetki i nową linią. Linux tylko nową linią. Ten dodatkowy niewidzialny znak siedzi na końcu shebanga i jądro szuka programu o nazwie /bin/bash z doklejonym niewidzialnym ogonkiem. Takiego programu nie ma. Na starszych Bashach komunikat jest bardziej wprost: bad interpreter: No such file or directory.
Jak to zobaczyć:
file okna.sh
cat -A okna.shokna.sh: Bourne-Again shell script, ASCII text executable, with CRLF line terminators
#!/bin/bash^M$
echo hej^M$To ^M to właśnie ogonek z Windowsa. Naprawa:
sed -i 's/\r$//' okna.shAlbo program dos2unix, który jest w repozytoriach i Ubuntu, i RHEL-a, w pakiecie o tej samej nazwie. A na przyszłość: w edytorze typu VS Code przełącz końce linii na LF, w prawym dolnym rogu.
I drugi klasyk: literówka w shebangu, na przykład #!/bin/bsh. Komunikat ten sam, „required file not found”. Jeśli więc Bash mówi, że nie ma pliku, który ewidentnie jest, patrz najpierw na pierwszą linijkę.
A na koniec dobry nawyk na całe życie: każdy skrypt przepuść przez ShellCheck. To darmowy program, który czyta skrypt i wytyka typowe błędy. [Ubuntu] sudo apt install shellcheck. [RHEL] pakiet ShellCheck z repozytorium EPEL. Nasz raport.sh przechodzi bez uwag. A plik bez shebanga od razu dostaje uwagę:
In bez.sh line 1:
echo jestem bez shebanga
^-- SC2148 (error): Tips depend on target shell and yours is unknown. Add a shebang or a 'shell' directive.ShellCheck nie wie, dla którego kucharza jest przepis, więc nie umie doradzić. Uczciwie.
✎ Zadanie
Napisz skrypt kto.sh, który wypisuje po kolei, każde z nagłówkiem zrobionym przez echo: kim jesteś (whoami), w jakim folderze jesteś (pwd), jaka jest data i godzina (date) oraz od jak dawna działa system (uptime -p). Dodaj shebang i komentarz z opisem. Uruchom go na dwa sposoby: przez ./kto.sh i przez bash kto.sh. Sprawdź ShellCheckiem. A potem odbierz mu prawo wykonania i sprawdź, który z dwóch sposobów dalej działa.
Pokaż rozwiązanie
#!/bin/bash
# kto.sh - krótka wizytówka sesji
echo "--- Kim jestem ---"
whoami
echo "--- Gdzie jestem ---"
pwd
echo "--- Kiedy ---"
date
echo "--- Od kiedy system działa ---"
uptime -pchmod +x kto.sh
./kto.sh
bash kto.sh
shellcheck kto.sh
chmod -x kto.sh
./kto.sh
bash kto.sh--- Kim jestem ---
eryk
--- Gdzie jestem ---
/home/eryk
--- Kiedy ---
Tue Nov 3 16:40:20 CET 2026
--- Od kiedy system działa ---
up 22 minutes
bash: ./kto.sh: Permission deniedPo zabraniu iksa ./kto.sh dostaje odmowę, a bash kto.sh dalej działa, bo wtedy uruchamiasz Basha, a plik jest dla niego tylko kartką do przeczytania. ShellCheck milczy, czyli jest dobrze. Milczący ShellCheck to jedyny przypadek w informatyce, kiedy cisza oznacza pochwałę.
Co dalej
Nasz raport ma jedną wadę: wszystko jest w nim wpisane na sztywno. W następnej lekcji dajemy skryptowi pamięć: zmienne, czyli pudełka z napisem, cudzysłowy, które są kopertami, i read, czyli sposób, żeby skrypt mógł Cię o coś zapytać.