14 Skrypty - wstęp do programowania w powłoce Bash

14.6 Instrukcje sterujące

Warunek if.

Składnia warunku:
 
if wyrażenie;
then
       instrukcje
fi
 

Gdy wyrażenie jest prawdziwe (zobacz wyrażenia warunkowe) wówczas wykonywane są instrukcje pomiędzy słowem then i fi.
Przykład:

#!/bin/bash
if [ $# -eq 0 ];
then
   echo "Nie podałeś żadnych argumentów "
fi
exit 0

Bardziej rozbudowane wyrażenie warunkowe:
 
if wyrażenie;
then
       instrukcje
else
       instrukcje 2
fi
 

Instrukcje zawarte w bloku rozpoczynającym się od else są wykonywane gdy wyrażenie nie jest spełnione. Np.:

#!/bin/bash
if [ $# -eq 0 ];
then
   echo "Nie podałeś żadnych argumentów. "
   echo "Podaj liczbe calkowita "
   echo "Sprobuj: $0 liczba"
   exit 1
fi
if [ $1 -lt 0 ];
then
   echo Liczba $1 jest mniejsza od zera
else
  echo Liczba $1 jest wieksza lub równa 0
fi
exit 0

Przykład - skrypt o nazwie rzut.sh wyświetlający słowo Orzeł lub Reszka z prawdopodobieństwem 1/2.

#!/bin/bash
if [ $(($RANDOM%2)) -eq 1 ];
then
   echo "Reszka"
else
  echo "Orzeł"
fi

Przykład - skrypt o nazwie podglad.sh który dla danego w argumencie pliku wyświetla jego zawartość za pomocą less, zaś jeżeli podany argument jest nazwą katalogu wówczas wyświetlana jest zawartość tego katalogu.

#!/bin/bash
if [ $# -lt 1 ];
then
   echo "Podaj plik lub katalog jako argument."
   echo "Uzycie: $0 plik"
   exit 1
fi
if [ -f $1 ];
then
  less $1
else
  if [ -d $1 ];
  then
     ls -l $1
  else
    echo "Blad: $1 nie jest plikiem ani katalogiem"
  fi
fi

Pętla for.

Pętla for wykonuje zadane instrukcje tyle razy ile jest elementów na podanej liście.  
for zmienna in lista;
do
       instrukcje
done
 

W każdej iteracji kolejny element z listy jest podstawiany do zmiennej.
Przykład użycia w linii komend:
$ for f in *; do echo "Plik $f zajmuje $(du -sb $f) bajtow"; done
dla każdego pliku w bieżącym katalogu wyświetli komunikat o ilości zajmowanych bajtów.

Przykład: skrypt zamieniający w nazwach plików duże litery na małe.

#!/bin/bash
if [[ $# -eq 0 || $1 == "-h" || $1 == "--help" ]];
then
  echo "Uzycie: $0 [-h] plik..."
  echo "Zamienia w nazwach podanych plikaow duze litery na male (np. Plik.TXT na plik.txt)."
  echo "Opcja -h wyswietla pomoc."
  exit 1
fi
for plik in $*
do
  if [ -e $plik ];
  then
    nowy_plik=$(echo $plik | tr ’[A-Z]’ ’[a-z]’)
    if [ $plik != $nowy_plik ];
    then
      echo "Zamieniam: $plik na $nowy_plik"
      mv $plik $nowy_plik
    fi
  else
    echo "Blad: $plik - nie ma takiego pliku"
  fi
done

Powłoka Bash umożliwia także użycie pętli for znanej z języka C.
 
for (( wyrażenie1 ; warunek ; wyrażenie2 ))
do
       instrukcje
done
 

Wszystkie instrukcje są wykonywane dopóki warunek jest spełniony. Początkowe wyrażenie1 jest uruchomione tylko raz przed rozpoczęciem pętli, zazwyczaj służy do zainicjowania zmiennych. Końcowe wyrażenie2 jest wykonywane na końcu każdej iteracji, zazwyczaj używane jest do zwiększenia (lub zmniejszenia) pewnego licznika.
Przykład: skrypt wyznaczający silnię

#!/bin/bash
if [[ $# -eq 0 || $1 == "-h" || $1 == "--help" ]];
then
  echo "Uzycie: $0 [-h] liczba"
  echo "Oblicza silnie podanej liczby."
  echo "Opcja -h wyswietla pomoc."
  exit 1
fi
silnia=1;
for (( i=2 ; i<=$1 ; i++ ))
do
  let silnia=silnia*i;
done
echo "Silnia wynosi $silnia"

Przykład: wielokrotne losowanie kostką

#!/bin/bash
if [[ $1 == "-h" || $1 == "--help" ]];
then
  echo "Rzut kostka"
  echo "Uzycie: $0 [-h] liczba"
  echo "Wyswietla wynik rzutu kostka powtorzenoge zadana liczbe razy."
  echo "Opcja -h wyswietla pomoc."
  exit 1
fi
ile=1
if [ $# -gt 0 ]; then ile=$1 ;fi
for (( i=1 ; i<=ile ; i++ )) do
  wynik=$(($RANDOM%6+1))
  echo $wynik
done

Pętla while.

Składnia pętli while:
 
while warunek
do
       instrukcje
done
 
Podane instrukcje są wykonywana dopóki warunek jest prawdziwy.
Przykład - stoper, odlicza sekundy od rozpoczęcia działania skryptu:

#!/bin/bash
if [[ $1 == "-h" || $1 == "--help" ]];
then
  echo "Stoper - po prostu uruchom bez argumentow."
  echo "Ctrl+C konczy odliczanie."
  echo "Opcja -h wyswietla pomoc."
  exit 1
fi
let s=0
while true; do
  echo $s
  sleep 1
  let s++
done

Instrukcja true zwraca zawsze wartość logiczną prawda. Analogicznie false daje odpowiedź negatywną.

Instrukcja case.

Instrukcja case pozwala na wykonanie wybranych instrukcji w zależności od wartości przyjmowanej przez pewną zmienną. Działanie bardzo podobne do instrukcji if jednak często wygodniejsze w użyciu, zwłaszcza gdy mamy więcej niż dwie możliwości do wyboru.
 
case zmienna in
       wartość_1)
              instrukcje 1
              ;;
       wartość_2)
              instrukcje 2
              ;;
       …
       *)
              instrukcje
              ;;
esac

 

Przykład - skrypt wyświetlajązy menu:

#!/bin/sh
while true
  do
     clear
     echo "============================"
     echo "[1] Wyświetl dzisiejszą datę"
     echo "[2] Wyswietl listę plików w bierzącym katalogu"
     echo "[3] Pokarz kalendarz"
     echo "[4] Pokarz listę zalogowanych uzytkownków"
     echo "[5] Zakończ"
     echo "============================"
     echo -n "Wybierz liczbę [1-5]: "
     read akcja
     case $akcja in
       1) echo "Dzisiejsza data: $(date)"
          ;;
       2) echo "Lista plików w katalogu $(pwd)"
          ls -l
          ;;
       3) cal  ;;
       4) echo "Lista zalogowanych" ; who ;;
       5) echo "Do widzenia"
          exit 0 ;;
       *) echo "Blad!!! Proszę wybrać wartość 1,2,3,4, lub 5";
  esac
  echo  "Wciśnij klawisz Enter"
  read
done

Instrukcja exit.

Instrukcja exit kończy działanie skryptu. Liczba całkowita umieszczona po instrukcji exit jest zwracana do powłoki jako wynik działania skryptu. W przypadku poprawnego wykonania skrypt powinien kończyć się wyrażeniem exit 0. Gdy skrypt nie został wykonany poprawnie wówczas po słowie exit wstawiamy dowolną liczbę różną od zera (wartość zwracanej liczby może w ten sposób sygnalizować rodzaj błędu który spowodował niepoprawne wykonanie skryptu). Wartość zwracana po słowie exit umieszczana jest w zmiennej $?.

Instrukcja read.

Instrukcja read pozwala wczytać linie tekstu do podanej zmiennej.
Przykład:

#!/bin/bash
echo Podaj imie i nazwisko
read dane
echo Witaj $dane

Instrukjca read użyta w pętli while umożliwia czytanie tekstu z pliku linia po linii, np.:

#!/bin/bash
echo Podaj nazwe pliku do wyswietlenia
read plik
if [ ! -r $plik];
then
  echo "Nie moge czytac z pliku $plik"
  exit 1
fi

while read linia
do
  echo $linia
done < $plik

Funkcje.

W powłoce bash możliwe jest definiowanie funkcji, czyli wyodrębnionych podprogramów oznaczonych pewną unikatową nazwą.
 
nazwa_funkcji( )
{
       instrukcje do wykonania
       return
}

 

Funkcja wykonywana jest w momencie gdy pojawi się wywołanie jej nazwy.
Przykład:

#!/bin/sh
pomoc()
{
  echo "Wyswietla liste i liczbe zalogowanych uzytkonikow"
  echo "Opcje:"
  echo "-h pomoc"
  echo "-l liczba zalogowanych użytkoników"
  echo "-w lista zalogowanych użytkoników"
  return
}
# lista niepowtarzajacych sie nazw zalogowanych uzytkownikow
lista()
{
  users=( $(who | cut -f 1 -d ’ ’ | sort | uniq ) )
  return
}
case $1 in
  "-h")  pomoc ;;
  "-l") lista
        echo "Liczba zalogowanych uzytkonikow = ${#users[*]} "
        ;;
  "-w") lista
        echo "Lista uzytkownikow: ${users[*]}" ;;
     *)Ψpomoc ;;
esac

Do funkcji możemy przekazać argumenty dodając je przy wywołaniu po nazwie funkcji. Kolejne argumenty umieszczone są w zmiennych $1, $2, $3, itd. Ilość argumentów dana jest poprzez $#, lista wszystkich argumentów zawarta jest w zmiennej $* a zmienna $0 zawiera nazwę funkcji.

#!/bin/sh
funkcja()
{
  echo "Argumenty funkcji $*"
  echo "Ilość argumentów funkcji $#"
  return 0
}
echo "Argumenty skryptu $*"
echo "Ilość argumentów skryptu $#"
echo Uruchamiam funkcje z argumentami: raz dwa trzy
funkcja raz dwa trzy
echo Uruchamiam funkcje z argumentami będącymi nazwami plików z bierzącego katalogu
funkcja *
exit 0

Zmienne użyte wewnątrz funkcji maja zakres globalny, tzn. ich wartość jest dostępna poza funkcją (np. zmienna users z pierwszego przykładu w tym paragrafie). Chcąc ograniczyć czas życia zmiennej wyłącznie do obszaru zdefiniowanego przez funkcję należy zadeklarować zmienną poprzedzając ja instrukcją local. Używanie zmiennych lokalnych może uchronić przed wieloma trudnymi do wykrycia błędami, więc gdzie tylko jest to możliwe, wewnątrz funkcji należy je stosować.
Przykład:

#!/bin/sh
funkcja()
{
  local zmienna="Zmienna lokalna"
  echo "Jestem wewnątrz funkcji"
  echo "zmienna=$zmienna"
  return 0
}
zmienna="Zmienna globalna"
echo "Zmienna=$zmienna"
funkcja
echo "Zmienna=$zmienna"
exit 0