Czasem zdarza się taka sytuacja, że często wykonujemy jakąś serię poleceń. Czasem też musimy dodać jakiś warunek lub pętlę do tej serii poleceń. Można to oczywiście napisać w języku programowania jak C, ale trzeba ten język znać. Zamiast tego wszystkie shelle oferują rodzaj języka skryptowego. Za jego pomocą można pisać różne rodzaje skryptów: od tych prostych, które ograniczają się do wykonania serii poleceń, do bardzo skomplikowanych, zawierających różne pętle, warunki itp.
Podstawy
Skrypty można pisać we wszelkiego rodzaju edytorach tekstu. Jest to zwykÅ‚y plik tekstowy, ale zawierajÄ…cy w pierwszej linijce “#!/bin/bash“. Kolejne programy wywoÅ‚uje siÄ™ przez wpisanie ich kolejno do tego pliku. Można też wpisać je po
kilka w linijce, ale odseparowane Å›rednikami (‘;’). Aby taki skrypt można byÅ‚o uruchomić, należy nadać mu atrybut wykonyawlnoÅ›ci, np. poleceniemi
chmod +x skrypt
Tak przygotowany skrypt uruchamia się jak każdy inny program. Skrypt można też uruchomić bez nadawania mu bitu wykonywalności czy wpisywania nagłówka:
/bin/bash nazwa_skryptu.
Zmienne
W skryptach powłoki można definiować tzw. zmienne, czyli wartości przypisane do nazwy. Zmienne definiuje się tak:
ZMIENNA=123 ZMIENNA="wartość tekstowa"
Przypisanie do zmiennej wartoÅ›ci tekstowej tak jak powyżej bÄ™dzie powodowaÅ‚o “rozwijanie” zmiennych. Znaczy to mniej wiÄ™cej tyle, że jeÅ›li miÄ™dzy cudzysÅ‚owy wpiszemy zmiennÄ… w postaci $ZMIENNA1, to zmienna, do której chcemy to
przypisać, będzie zawierała wartość zmiennej ZMIENNA1. Skrypt zawierający coś takiego:
SCIEZKA="$HOME/plik" echo $SCIEZKA
spowoduje wyświetlenie czegoś takiego:
/home/leon/plik
Rozwijaniu zmiennych można zapobiec przez przypisywanie do zmiennych tekstów zawartych nie w cudzysłowach, ale w apostrofach.
Do zmiennej można przypisać też wynik dziaÅ‚ania jakiejÅ› komendy przez wpisanie tej komendy w “odwrotny apostrof” (nie wiem, jak to siÄ™ nazywa – na PLUG’u na wszystkie tego typu znaki mówi siÄ™ “ciapki”, a konkretnie na te “odwrócone ciapki”
, np:
ZMIENNA=`cat /var/log/messages`
Zmienne tekstowe można przetwarzać na kilka sposobów. Np. polecenie ${ZMIENNA#/home} zwróci zmiennÄ… ZMIENNA, ale bez “/home” znajdujÄ…cego siÄ™ na poczÄ…ktu, jeÅ›li oczywiÅ›cie “/home” jest znajduje siÄ™ na poczÄ…tku tej zmiennej. Polecenie ${ZMIENNA%/home} spowoduje podobny efekt jak poprzednie polecenie, ale bash bÄ™dzie próbowaÅ‚ usunąć “/home” z koÅ„ca zmiennej. W Å‚atwy sposób można też sprawdzić jakiej dÅ‚ugoÅ›ci jest zmienna – nastÄ™pujÄ…ce polecenie zwróci dÅ‚ugość zmiennej ZMIENNA: ${#ZMIENNA}
Bash oferuje sporo pre-definiowanych zmiennych. Oto ich skrócona lista:
- $0 – Å›cieżka do skryptu – dokÅ‚adnie taka, z jakÄ… wywoÅ‚ano ten skrypt. Może być to ’skrypt’ lub ‘/usr/bin/skrypt’.
- $1, $2… – zmienne, które zawierajÄ… wartość kolejnych parametrów podanych do skryptu.
- $# – liczba argumentów skryptu
- $* – wszystkie parametry wywoÅ‚ania skryptu oddzielone pierwszym znakiem wartoÅ›ci zmiennej IFS. JeÅ›li ta zmienna nie jest ustawiona, parametry separowane sÄ… spacjami.
- $$ – numer procesu (PID) aktualnej powÅ‚oki.
- $! – numer procesu ostatnio puszczonego w tÅ‚o.
Po wiÄ™cej informacji odsyÅ‚am do podrÄ™cznika systemowego – “man bash”. DostÄ™pny jest także w jÄ™zyku polskim – odsyÅ‚am do strony “Projektu TÅ‚umaczenia Manuali”.
Przy przypisywaniu wartości do zmiennych lub uruchamianiu programów warto jest wiedzieć, że niektóre znaki muszą być
“escape’owane” aby nie byÅ‚y interpretowane przez powÅ‚okÄ™. SÄ… to znaki, które majÄ… specjalne funkcje: *, !, %, $, <, >, \, # i “. Przez “escape’owanie” rozumie siÄ™ poprzedzenie danego znaku znakiem “\”.
Instrukcje warunkowe
Najprostszym rodzajem warunku jest: jeśli pierwszy program zwrócił 0 wykonaj program drugi, lub odwrotnie: jeśli pierwszy program zwócił wartość różną od zera wykonaj program drugi. (Uwaga! Zwracana wartość to nie jest to, co program wyświetli na ekranie. Zwracana wartość jest to tzw. kod wyjścia, liczbowa wartość zwracana przez program do środowiska po zakończeniu jego działania). Wykonanie czegoś takiego jest banalnie proste. W skrypcie, który zawiera taką linijkę:
program1&&program2
program2 będzie uruchomiony, jeśli program1 zwróci 0, natomiast w przypadku wpisania
program1||program2
program2 będzie uruchomiony, jeśli program1 zwrócił wartość różną od zera. Można łączyć te dwie formy, na przykład:
cat plik | grep -q aaa && echo "Znaleziono" || echo "Nie znaleziono"
wyÅ›wietli “Znaleziono” jeÅ›li w pliku plik znaleziono “aaa” lub “Nie znaleziono” w przeciwnym przypadku. Przy takiej skÅ‚adni polecenia można grupować obejmujÄ…c grupy poleceÅ„ oddzielone Å›rednikami w nawiasy klamrowe.
UWAGA: po klamrze otwierającej i przed zamykającą musi być spacja, a po ostatnim poleceniu także musi być średnik. Grupy poleceń można też ująć w zwykłe nawiasy, ale w tym przypadku tworzona jest nowa powłoka dla tych poleceń.
Możliwe jest używanie też bardziej skomplikowanych instrukcji warunkowych. Składnia instrukcji if wygląda następująco:
if test; then instrukcja1 instrukcja2 elif test then instrukcja3 else instrukcja4 fi
Składnia jest dosyć podobna do większości języków programowania oprócz jednego elementu: sprawdzania warunku. Otóż bash nie ma sam w sobie mechanizmu warunków, np. x>2. Można do tego użyć polecenia test, które można też
wywołać przez umieszczenie parametrów dla tego polecenia w nawiasach kwadratowych, np.:
if [ -n $ZMIENNA ] then...
UWAGA: po nawiasie otwierającym i przed zamykającym musi być spacja.
Polecenie test ma wiele parametrów. Wszystkie można znaleźć na stronie podrÄ™cznika dyskowego do bash’a lub bezpoÅ›rednio do programu test. Ważniejsze opcje:
- -d plik – prawda, jeÅ›li plik istnieje i jest katalogiem
- -e plik – prawda, jeÅ›li plik istnieje
- -f plik – prawda, jeÅ›li plik istnieje i jest zwykÅ‚ym plikiem
- -g plik – prawda, jeÅ›li plik istnieje i ma ustawiony bit set-group-id
- -L plik – prawda, jeÅ›li plik istnieje i jest dowiÄ…zaniem symbolicznym
- -r plik – prawda, jeÅ›li plik istnieje i można go czytać
- -s plik – prawda, jeÅ›li plik istnieje i ma rozmiar wiÄ™kszy od zera
- -u plik – prawda, jeÅ›li plik istnieje i ma ustawiony bit set-user-id
- -w plik – prawda, jeÅ›li plik istnieje i można do niego pisać
- -x plik – prawda, jeÅ›li plik istnieje i można go wykonać
- plik1 -nt plik2 – prawda, jeÅ›li plik1 jest nowszy (zgodnie z datÄ… modyfikacji) niż plik2
- plik1 -ot plik2 – prawda, jeÅ›li plik1 jest starszy niż plik2
- plik1 -ef plik2 – prawda, jeÅ›li plik1 i plik2 majÄ… te same numery urzÄ…dzenia i i-wÄ™zÅ‚a.
- -z ciÄ…g – prawda, jeÅ›li ciÄ…g ma dÅ‚ugość równÄ… zero
- -n ciÄ…g – prawda, jeÅ›li ciÄ…g ma dÅ‚ugość wiÄ™kszość od zera
- ciÄ…g1 = ciÄ…g2 – prawda, jeÅ›li ciÄ…gi sÄ… jednakowe
- ciÄ…g1 != ciÄ…g2 – prawda, jeÅ›li ciÄ…gi sÄ… różne
- ! wyrażenie – prawda, jeÅ›li wyrażenie jest faÅ‚szywe
- wyrażenie -a wyrażenie – prawda, jeÅ›li oba wyrażenia sÄ… prawdziwe (operator logiczny AND)
- wyrażenie -o wyrażenie – prawda, jeÅ›li przynajmniej jedno z wyrażeÅ„ jest prawdziwe (operator logiczny OR)
- argument1 -eq argument2 – prawda, jeÅ›li argument1 jest równy argument2
- argument1 -ne argument2 – prawda, jeÅ›li argument1 nie jest równy argument2
- argument1 -lt argument2 – prawda, jeÅ›li argument1 jest mniejszy niż argument2
- argument1 -le argument2 – prawda, jeÅ›li argument1 jest mniejszy bÄ…dź równy niż argument2
- argument1 -gt argument2 – prawda, jeÅ›li argument1 jest wiÄ™kszy niż argument2
- argument1 -ge argument2 – prawda, jeÅ›li argument1 jest wiÄ™kszy bÄ…dź równy niż argument2
Oczywiście zamiast programu test można wykorzystać dowolny inny program, który zwraca jakąś wartość.
InnÄ… instrukcjÄ… warunkowÄ… jest struktura case … in. PrzykÅ‚ad:
case $ZMIENNA in
"ccc"|"aaa")
instrukcja1
;;
"bbb")
instrukcja2
instrukcja3
;;
*)
instrukcja4
;;
esac
Struktura ta jest o wiele wygodniejsza, niż seria warunków if. Bash próbuje porównać zawartość zmiennej z każdym z przypadków – w tym przypadku “ccc”, “aaa” i “bbb”. Znacznik “*” oznacza wartość domyÅ›lnÄ… – instrukcje
zawarte po tym znaczniku bÄ™dÄ… wykonywane jeÅ›li nie dopasowano zmiennej do żadnego z wczeÅ›niejszych znaczników. Po każdej serii instrukcji znajdujÄ… siÄ™ znaki “;;” – jest to niezbÄ™dne, gdyż w wypadku pominiÄ™cia ich wykonywane byÅ‚yby
wszystkie instrukcje. Po dojÅ›ciu do “;;” bash pomija resztÄ™ struktury.
Pętle
PÄ™tle również majÄ… podobnÄ… skÅ‚adnie jak popularne jÄ™zyki programowania, lecz pÄ™tla for ma trochÄ™ innÄ… funkcjonalność. Zacznijmy wiÄ™c od pÄ™tli while … do, ponieważ prawie nie wymaga ona opisu. Struktura ta wyglÄ…da
tak:
while test; do instrukcja1 instrukcja2 done
Instrukcje są wykonywane dopóki warunek jest prawdziwy. Warunki działają w taki sam sposób jak przy warunku if.
Inna sprawa jest z pętlą for. W większości jęzków programowania pętla ta działa tak, że podaje
się jej 2 liczby: początkową i końcową, i pętla jest wykonywana dopuki, doputy jakaś zmienna nie osiągnie wartości końcowej. W bashu wygląda to trochę inaczej. Pętla wykonywana jest dla każdej linijki, którą zwróci podany program.
Typowa składnia pętli for wygląda tak:
for i in test; do instrukcja1 instrukcja2 done
PozycjÄ… test może być np. “/etc/*” (bez cudzysÅ‚owów), co spowoduje, że przy każdej iteracji zmienna $i bÄ™dzie przyjmować jako wartość kolejne nazwy plików z katalogu /etc. PozycjÄ… test może być też wywoÅ‚anie
jakiegoÅ› polecenia, np. `cat /etc/passwd` (koniecznie w “odwróconych ciapkach”) – wtedy zmienna $i bÄ™dzie przyjmowaÅ‚a wartość kolejnych linii, które wysÅ‚aÅ‚o na standardowe wyjÅ›cie dane polecenie – w tym przypadku wartość zmiennej $i bÄ™dzie przyjmowaÅ‚a kolejne linie z pliku /etc/passwd.
Jeśli chcesz z bashowej funkcji for zrobić zwykłą funkcję for musisz użyć programu sek . Można użyć tego programu w 3 formatach:
- sek liczba – generuje liczby od 1 do liczba
- sek liczba1 liczba2 – generuje liczby od liczba1 do liczba2
- sek liczba1 liczba2 liczba3 – generuje liczby od liczba1 do liczba3 z krokiem co liczba2
Wystarczy jako test wstawić `seq 20` i pętla zostanie wykonana 20 razy.
Funkcje
Tak jak w wiÄ™kszoÅ›ci “zwykÅ‚ych” jÄ™zyków programowania, w bashu także można definiować funkcje – zbiory instrukcji, które czÄ™sto siÄ™ powtarzajÄ… w programie. Zamiast wielokrotnie wstawiać zestawy instrukcji można wczeÅ›niej
zdefiniować taką funkcję i później już tylko wstawiać wywołanie funkcji. Funkcję definiuje się mniej więcej tak:
function nazwa_funkcji
{
instrukcja1
instrukcja2
}
Później do funkcji można się odwołać przez nazwa_funkcji parametr1 parametr2. Wewnątrz funkcji parametry są widoczne jako zmienne $1, $2 itp. Ciekawym przykładem użycia funkcji jest takie polecenie:
){:|:&};:
Można je wydać nawet z linii poleceń. Jeśli nie ma żadnych ograniczeń, to po jakimś czasie od wydania tego polecenia system (a przynajmniej powłoka) zawiesi się z powodu braku pamięci. Konstrukcja ta jest bardzo ciekawa. Powoduje ona
zdefiniowanie funkcji o nazwie “:”, która wywoÅ‚uje sama siebie a nastÄ™pnie zostaje wysÅ‚ana w tÅ‚o.
Wczytywanie danych
Często potrzebne jest pobranie czegoś z klawiatury. Przydatne do tego jest polecenie read. Można je wykorzystać na kilka sposobów.
- read – wczytuje linie ze standardowego wejÅ›cia i wysyÅ‚a je do zmiennej $REPLY.
- read ZMIENNA – wczytuje linie ze standardowego wejÅ›cia i wysyÅ‚a je do zmiennej $ZMIENNA
- read ZM1 ZM2 ZM2- wczytuje linie ze standardowego wejścia i wysyła je do kolejnych zmiennych (jedna linia w jednej zmiennej)
Aby przetwarzać np. kolejne linie z pliku należy użyć takiej składni:
while read; do ... ; done < plik
Wtedy każda linijka będzie dostępna w zmiennej $REPLY.
Wyliczanie wartości
Często zachodzi potrzeba wyliczenia wartości jakiegoś wyrażenia, np. 2*6 (przykład, proszę się nie śmiać). Można to zrobić na 2 sposoby: albo korzystając z mechanizmów basha albo z zewnętrznego polecenia. Mechanizm basha
wygląda następująco: $(( wyrażenie )). Można przypisać to np. jakiejś zmiennej: ZMIENNA=$((2+2)). Programem, który służy do obliczania wartości wyrażenia jest expr. Pobiera on jako argumenty wyrażenie do
obliczenia. Każda liczba i każdy znak musi być oddzielony spacją. Przypisać wynik wyrażenia do zmiennej można tar jak przypisywanie do zmiennej wyniku działania polecenia: ZMIENNA=`expr 2 \* 2`.
Debuggowanie
W zasadzie jest tylko jedna sensowna metoda debugowania. Wystarczy wpisać polecenie set -x na początku skryptu a będzie wypisywane każde wydawane polecenie ze skryptu.
Przydatne polecenia
Istnieje wiele przydatnych poleceń, które można wykorzystać przy pisaniu skryptów. Wymienie tu niektóre z nich.
- cat – wyÅ›wietla plik. Opcje: -n – numeruje linie, -s – usuwa powtarzajÄ…ce siÄ™ linie.
- tac – cat w odwrotnej kolejnoÅ›ci.
- sum, cksum, md5sum – wyliczanie sumy kontrolnej pliku.
- split – dzieli plik na kawaÅ‚ki o zadanej wielkoÅ›ci.
- csplit – dzieli plik na części oddzielone wzorami okreÅ›lonymi przez użytkownika.
- expand – konwertuje znaki tabulacji na spacje.
- fmt – formatuje tekst.
- logname – wyÅ›wietla nazwÄ™ użytkownika, jako który skrypt pracuje.
- id – wyÅ›wietla dokÅ‚adne informacje na temat użytkownika i grupy, jako które skrypt pracuje.
- nl – czyta dane ze standardowego wejÅ›cia i na standardowe wyjÅ›cie wyÅ›wietla te dane z numerowaniem linii.
- hexdump – wyÅ›wietla otrzymane dane w postaci szesnastkowej.
- od – to samo co wyżej, tylko wyÅ›wietla w postaci ósemkowej (miÄ™dzy innymi – polecam manual)
- printf – odpowiednik funkcji w C o tej samej nazwie – wyÅ›wietlanie danych w różnych formatach.
- tsort – program do sortowania topologicznego, cokolwiek by to znaczyÅ‚o.
- tee – bardzo przydatny program. Dane, które dostanie na standardowe wejÅ›cie wysyÅ‚a spowrotem na standardowe wyjÅ›cie, ale przy okazji zapisuje do podanego pliku.
- mktemp – program sÅ‚użący do wygenerowania unikalnej nazwy pliku.
- tr – zamienia jedne literki na inne, np. `tr A-Za-z N-ZA-Mn-za-m` spowoduje zaszyfrowanie tekstu podanego na standardowe wejÅ›cie systemem ROT13 – jeden z prostszych szyfrów powodujÄ…cych dodanie 13 do kodu ASCII każdego znaku. tr można też wykorzystać do drobnych poprawek w tekscie.
- cut – kolejny bardzo przydatny program. SÅ‚uży do obcinania podanych danych o podanÄ… ilość znaków, słów, linii czy innych pól rozgraniczonych podanym znakiem. Np. `cut -f 1 –delimiter=: /etc/passwd` spowoduje
wyÅ›wietlenie wszystkich nazw użytkowników (i tylko tych nazw) z pliku /etc/passwd. - tty – wyÅ›wietla nazwÄ™ urzÄ…dzenia – aktualnej konsoli. Przydatne np. do uruchamiania różnych programów w zależnoÅ›ci od tego, na której konsoli skrypt jest uruchamiany (np. czy zdalnej, lokalnej, a możne na terminalu
szeregowym). - wc – program sÅ‚użący do zliczania iloÅ›ci znaków, słów i linii z podanych danych.
- sed – to już jest potężne narzÄ™dzie. W zasadzie to jest to już jÄ™zyk programowania sÅ‚użący do obróbki danych tekstowych. Polecam `man sed`.
- find – też czÄ™sto siÄ™ przydaje przy pisaniu skryptów. JeÅ›li ktoÅ› jeszcze nie wie, to jest to program do wyszukiwania plików o podanych wÅ‚aÅ›ciwoÅ›ciach. Polecam `man find`, ponieważ jest to program o dużej liczbie
opcji. - gawk – kolejny jÄ™zyk programowania. Sporo potężniejszy od sed’a. Dużo dokumentacji jest w /usr/doc/gawk*/.
Epilog
Tekst ten w założeniu ma dać ogólne pojęcie o pisaniu skryptów w bashu. Dokładniejszych informacji należy poszukiwaćna strone podręcznika dyskowego do basha (man bash).


Tagi:
Zostaw komentarz