Narzędzia użytkownika

Narzędzia witryny


materialy:podstawy-administracji:skrypty-bash

Język skryptowy Bash

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:

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. Czy plik /etc/shadow istnieje (Exist)?

# test -e /etc/shadow

Można spotkać również inny zapis tej komendy (znaczące są również nawiasy kwadratowe):

[ -e /etc/shadow ]

Przykład:

#!/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 sprawdza, czy plik podany w parametrze istnieje"
      exit 0
fi
 
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?
  • [ $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 powinny być budowane skrypty i w przypadku błędów należy 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


Masz potrzebę, aby powyższa strona była rozbudowana? - Napisz do nas!.

materialy/podstawy-administracji/skrypty-bash.txt · ostatnio zmienione: 2014/09/24 00:30 przez mzalewski

(C) 2017 ITMZ Mariusz Zalewski