Wstęp
Umiejętność programowania w językach skryptowych jest ważna dla administratorów systemów Linux/Unix/BSD ponieważ wiele procesów zachodzących w systemie (choćby te startowe) opiera się o skrypty i różne języki skryptowe. Języki skryptowe są często językami interpretowanymi, wobec czego ich głównymi atutami są: możliwość szybkiego podejrzenia źródła programu (pliki tekstowe), szybka modyfikacja (edytor tekstowy) i wprowadzanie modyfikacji w życie (brak kompilacji).
Najpopularniejszymi językami skryptowymi w zakresie działania systemów operacyjnych Linux są skrypty powłoki (np. sh, Bash). W systemie mogą być wykorzystane również inne języki interpretowane, których podstawy warto znać, np:
Bash - dokumentacja i kursy on-line
Dokumentacja godna polecenia przy nauce Basha:
- Kurs „Bash w przykładach” (język polski):
- Kurs Bash'a - programowanie w shellu (język polski)
- BASH Programming - Introduction HOW-TO (język angielski)
Skrypty Bash w pigułce
Pierwsza linia (shebang)
Pierwsza linia skryptu winna zawierać ścieżkę do interpretera poleceń, który wywołuje zapisane w skrypcie komendy. Ścieżka to powinna być poprzedzona znakami #!
(ang. shebang).
#!/sciezka/do/programu/skryptowego ...
Dla bash:
#!/bin/bash ...
Dla pythona:
#!/usr/bin/python ...
Hello world
#!/bin/bash echo "Hello World!"
Komentarze
Komentarze w skryptach - znak #
na początku linii.
#!/bin/bash # Skrypt data # Opis: Wyswietla aktualna date echo -n "Dzisiejsza data to " date
Uruchamianie skryptów
Nadanie praw uruchamiania właścicielowi:
$ chmod u+x /sciezka/do/skryptu
Jeśli użytkownik ma prawo uruchamiania skryptu (eXecute) to aby uruchomić skrypt wystarczy podać bezwzględną ścieżkę do skryptu:
$ /sciezka/do/skryptu
Uruchomienie skryptu z bieżącego katalogu (jeśli katalogu nie ma w ścieżce PATH):
$ ./skrypt
Jeśli skrypt ma być uruchamiany przy pomocy samej nazwy pliku, bez podawania ścieżki dostępu, można umieścić go w jednym z dwóch katalogów:
/usr/local/bin
- programy i skrypty dla wszystkich użytkowników/usr/local/sbin
- programy i skrypty administracyjne
W katalogach powyższych zamieszamy pliki tworzone lokalnie przez administratora. Są one domyślnie zamieszczone w zmienne PATH. Jeśli użytkownik zamieści swoje skrypty w określonym katalogu i mają one być wywoływane bez podawania pełnej ścieżki można dodać ten katalog do swojej zmiennej PATH.
Jeśli użytkownik nie ma prawa uruchamiania skryptu (eXecute) może go mimo to uruchomić. Istnieją na to przynajmniej dwa sposoby. Pierwszy to użycie nazwy pliku skryptu jako argumentu dla bash
:
$ /bin/bash /sciezka/do/skryptu
Drugi sposób, częściej spotykany w samych skryptach, które maja wywołać inne skrypty to includowanie
komend z pliku do aktualnego procesu Bash. Można to wykorzystać również w konsoli interpretera poleceń:
$ . /sciezka/do/skryptu
Ostatni przykład i uruchomienie skryptu różni się od poprzednich wywołań w kontekście procesu w którym komendy skryptu są wywoływane. Kolejne wiersze skryptu będą wywoływane w kontekście tego samego procesu Bash (tej samej wartości $BASHPID) w którym został skrypt uruchomiony. Jeśli w skrypcie użyta będzie komenda exit
to nastąpi zamknięcie procesu o PID konsoli w której includowano
skrypt - efektem jest zamknięcie terminala lub wylogowanie użytkownika. Należy tej metody używać ostrożnie i ze świadomością konsekwencji. Aby lepiej zrozumieć powyższe polecamy uruchomić testowo w systemie osobny terminal, wykonać poniższe komendy i zaobserwować, jaki będzie wynik ostatniej komendy. Proszę zwrócić uwagę na identyfikatory procesów Bash dla skryptów uruchamianych dwiema metodami:
itmz@l13 ~ $ echo echo PID procesu Bash: \$BASHPID >> skrypt.sh itmz@l13 ~ $ echo read -p \"Nacisnij Enter\" >> skrypt.sh itmz@l13 ~ $ echo exit >> skrypt.sh itmz@l13 ~ $ cat skrypt.sh echo PID procesu Bash: $BASHPID read -p "Nacisnij Enter" exit itmz@l13 ~ $ ls -l skrypt.sh -rw-rw-r-- 1 itmz itmz 60 maj 8 21:53 skrypt.sh itmz@l13 ~ $ echo $BASHPID 5046 itmz@l13 ~ $ /bin/bash skrypt.sh PID procesu Bash: 5106 Nacisnij Enter itmz@l13 ~ $ . skrypt.sh PID procesu Bash: 5046 Nacisnij Enter
Zmienne
Zmienne w Bash nie mają określonego typu, jednakże zmienne tekstowe zwyczajowo zamieszcza się w cudzysłowiach.
#!/bin/bash ZMIENNA_LICZBOWA=4 TEKST="zmienna tekstowa" AKTUALNA_DATA=`date` echo $ZMIENNA_LICZBOWA echo $TEKST echo $AKTUALNA_DATA
Zmienne i komendy w cudzysłowach
Użycie różnych typów cudzysłowów zmienia znaczenie użytych w nich zmiennych i komend:
itmz@l13 ~ $ echo "date" date itmz@l13 ~ $ itmz@l13 ~ $ echo `date` czw, 8 maj 2014, 22:16:32 CEST itmz@l13 ~ $ itmz@l13 ~ $ echo '$PATH' $PATH itmz@l13 ~ $ itmz@l13 ~ $ echo "$PATH" /usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games itmz@l13 ~ $
Interakcja z użytkownikiem - read
Najprostszym sposobem interakcji z użytkownikiem jest użycie komendy read
. Poniższy przykład pozwoli na przypisanie wartości podanej przez użytkownika do zmiennej:
#!/bin/bash read -e -p "Podaj nazwe serwera: " SERWER if [ -z "$SERWER" ]; then echo BLAD: Nic nie podales exit 1; fi echo Podales: $SERWER
read
jest komendą wbudowaną bash i wszelkie możliwe parametry znajdziesz w dokumentacji:
man builtins
Operacje arytmetyczne
#!/bin/bash echo -n "7x8=" echo $[7*8] echo -n "1024-512=" echo $[1024-512] ZM=100 echo -n "$ZM-40=" echo $[$ZM-40]
Parametry przekazywane przy wywołaniu skryptu
#!/bin/bash echo "Uruchomiles skrypt o nazwie $0" echo "Pierwszy parametr: $1" echo "Drugi: $2" echo "Lista wszystkich parametrow: $@"
Przekazywanie parametrów:
$ ./parametry raz dwa
Instukcja warunkowa if
#!/bin/bash if [ "$1" = "" ]; then echo "BLAD: nie podano parametru." echo "Uzyj --help, jesli nie znasz skladni." exit 1 elif [ "$1" = "--help" ]; then echo "Skrypt wyswietla tresc pierwszego" echo "podanego parametru." exit 0 fi echo $1
Uwaga. Przy porównywaniu stringów należy pamiętać, aby zmienną również umieścić w cudzysłowie.
Testy
Komenda test
możę sprawdzać uprawnienia do wskazanych plików oraz porównywać zmienne. Jej wynikiem jest wartość logiczna true lub false.
Przykłady:
Czy plik /etc/shadow istnieje (ang. exist)?
# test -e /etc/shadow
Można spotkać również inną formę zapisu tej komendy (nawiasy kwadratowe są znaczące):
[ -e /etc/shadow ]
#!/bin/bash if [ -e $1 ]; then echo Plik $1 istnieje else echo Plik $1 nie istnieje fi
Inne testy:
[ -x plik ]
- czy plik istnieje i czy masz do niego prawo uruchamiania (execute)?[ -r plik ]
- czy plik istnieje i czy czy masz do niego prawa odczytu (read)?[ $ZM1 -eq $ZM2 ]
- czy zmienna liczbowa ZM1 jest równa (equal) zmiennej ZM2?[ $ZM1 -lt $ZM2 ]
- czy zmienna liczbowa ZM1 jest mniejsza (less than) od zmiennej ZM2?[ $ZM1 -gt $ZM2 ]
- czy zmienna liczbowa ZM1 jest większa (great than) od zmiennej ZM2?[ $ZM1 -ne $ZM2 ]
- czy zmienna liczbowa ZM1 jest różna (not equal) od zminnej ZM2?[ $ZM1 -le $ZM2 ]
- czy zmienna liczbowa ZM1 jest mniejsza lub równa (less or equal) od zmiennej ZM2?[ $ZM1 -ge $ZM2 ]
- czy zmienna liczbowa ZM1 jest większa (great or equal) od zmiennej ZM2?
Pozostałe testy opisane są w dokumentacji komendy test
:
man test
Pętla for
for i in lista-zmiennych-oddzielonych-spacja; do # komendy wykorzystujace zmienna $i done
#!/bin/bash for i in 1 2 3 4 5; do echo $[ $i * 10 ] done
#!/bin/bash for i in `ls /etc`; do echo Plik: $i done
seq - sekwencja liczba
Ciąg kolejnych liczb „3 4 5 6 7 8” można zastąpić komendą seq 3 8
.
#!/bin/bash for i in `seq 3 8`; do echo $[ $i * 10 ] done
seq
można użyć do wyświetlania sekwencji o określonym skoku rosnąco lub malejąco podając w drugim argumencie interwał skoku:
seq 0 10 100 seq 10 -1 0
Pętla while
Pętla wykonuje kod dopóki spełniony jest warunek. Poniżej, dopóki zmienna licznik
nie będzie większy lub równy 0:
#!/bin/bash echo Odliczam... sleep 1 licznik=10 while [ $licznik -ge 1 ] do echo "$licznik..." licznik=$[$licznik-1] sleep 1 done echo 0: Rakieta wystartowała...
Kod wyjścia/błędu - exit
Ponieważ realizacja dowolnego kodu może napotkać problem, powinien on być prawidłowo obsłużony. Przykładowo w wyniku interakcji z użytkownikiem, który poda niewłaściwą nazwę do pliku skrypt powinien zareagować - powiadomić o błędzie. Najprostszy i najszybszą metodą jest zakończenie skryptu z odpowiednim statusem błędu.
Każda wywoływana komenda kończy swoje działanie z kodem wyjścia. Jeśli jest on równy 0 to oznacza, że komenda wykonała się prawidłowo, w przeciwnym wypadku wartość ta powinna być większa od 0. Wyświetlenie w Bash kodu wyjścia ostatnio uruchomionej komendy można zrealizować przez echo $?
itmz@l13 ~ $ ls /home/itmz if.sh s2.sh s3.sh skrypt.sh test-debug.sh while.sh itmz@l13 ~ $ echo $? 0 itmz@l13 ~ $ ls /home/innyuser ls: cannot access /home/innyuser: No such file or directory itmz@l13 ~ $ echo $? 2 itmz@l13 ~ $
Podobnie skrypty powinny informować o błędach - zwracać wartość niezerową:
#!/bin/bash if [ "$1" = "" ]; then echo "BLAD: nie podano parametru." echo "Uzyj --help, jesli nie znasz skladni." exit 1 elif [ "$1" = "--help" ]; then echo "Skrypt wyswietla tresc pierwszego" echo "podanego parametru." exit 0 fi echo $1
Rozwiązywanie problemów
Pisanie skryptów, szczególnie w fazie nauki, jest obarczone licznymi błędami. Dlatego warto użyć jakiejkolwiek formy debugowania
pisanych skryptów. Najprostszą metodą jest uruchomienie skryptu przez /bin/bash
z parametrem -x
:
$ /bin/bash -x skrypt.sh
Można również w samej treści skryptu w sposób kontrolowany wyświetlać informacje lub wywoływać komendy pomocne w późniejszej analizie działania skryptu:
#!/bin/bash DEBUG=0 if [ $DEBUG -eq 1 ]; then echo UWAGA: Jestem na poczatku skryptu; fi echo Tutaj jest działanie skryptu if [ $DEBUG -eq 1 ]; then echo UWAGA: Jestem na koncu skryptu - `date`; fi
Więcej o debugowaniu skryptów Bash
można przeczytać tutaj.
Zadania
- Przepisać zadania na to wiki - Mariusz Zalewski
Add Comment