5 Skrypty - wstęp do programowania w powłoce

5.5 Powłoka tcsh

5.5.1 Zmienne

Zmienne definiuje się przy pomocy polecenia: set (zmienne lokalne), setenv (zmienne środowiskowe) lub @ (zmienne lokalne liczbowe).
$ set napis="Ala ma kota"
$ setenv HOME="/home/student"
$ @ wynik=10
Nazwa zmiennej może zawiać 20 znaków - dowolne litery, cyfry (cyfra nie może być pierwszym znakiem nazwy zmiennej) oraz znak podkreślenia.
Wartość umieszczoną w zmiennej wydobywamy umieszczając $ przed nazwą zmiennej
$ echo $napis
$ Ala ma kota
$ echo $HOME
$ /home/student
$ echo $wynik
$ 10
Zmienne kasujemy poleceniem unset oraz odpowiednio (zmienne środowiskowe) unsetenv
$ unset napis
$ unset wynik
$ unsetenv HOME
Należy pamiętać, że wartości umieszczone w zmiennych są ciągami znaków. Zmienna $wynik zawiera napis składający się ze znaków ’1’ i ’0’. Możliwe jest jednak wykonywanie prostych operacji arytmetycznych.

Tablice

Tablice deklarujemy w następujący sposób:
$ set dzien=(poniedzialek wtorek sroda czwartek piatek sobota niedziela)
$ echo $dzien
$ poniedzialek wtorek sroda czwartek piatek sobota niedziela
Elementy tablicy numerowane są liczbami naturalny (startując od 1), np.$dzien[3] odnosi się do trzeciego elementu tablicy $dzien
$ echo $dzien[3] $dzien[5]
$ sroda piatek
$ echo $dzien[2-4]
$ wtorek sroda czwartek
$ set dzien[7]=sunday
$ echo $dzien
$ poniedzialek wtorek sroda czwartek piatek sobota sunday
Inny przykład:
$ set liczby=( `seq 1 10`)
$ echo $liczby
$ 1 2 3 4 5 6 7 8 9 10

Wyrażenie $#zmienna i $?zmienna

Za pomocą wyrażenia $#zmienna możemy dowiedzieć się ile elementów zawiera tablica zmiennych, np:
$ set dzien=(poniedzialek wtorek sroda czwartek piatek sobota niedziela)
$ echo $#dzien
$ 7
Operator $?zmienna zwraca wartość 1 jeśli zmienna jest zadeklarowana - 0 w przeciwnym wypadku, np.:
$ echo $?dzien
$ 1
$ unset dzien
$ echo $?dzien
$ 0
Polecenie unset usuwa zmienną, stąd operacja $? w tym wypadku zwraca 0.

Tablica $argv oraz zmienne $*.

Zmienna $argv jest tablicą zawierającą parametry z jakimi został wywołany skrypt. Np. jeżeli uruchomimy skrypt:
$ nazwa_skryptu -n 10 ala
wówczas w zmiennej $argv umieszczone zostaną wszystkie parametry, odpowiednio: $argv[1] zawiera ”-n”, argv[2] - ”10”, $argv[3] ”ala”. W podobny sposób można używać zmiennych $1 $2 $3 itd., które również zawierają wartości kolejnych argumentów z wywołania skryptu jednak dodatkowo w zmiennej $0 zawarta jest nazwa wywoływanego skryptu.

Przykład - skrypt argv.sh:

#!/bin/tcsh
set n = $#argv
echo "Liczba argumentów = $n"
echo "Nazwa skryptu: $0"
set i=1
while ( $i <= $n )
   echo "Argument $i = $argv[$i]"
   set i=`expr $i + 1`
end
exit 0

Uruchomienie skryptu:
$ ./argv.sh ala ma kota

Liczba argumentów = 3
Nazwa skryptu: ./argv.sh
Argument 1 = ala
Argument 2 = ma
Argument 3 = kota

Argumenty wywołania skryptu zawarte są także w zmiennej $* a kolejne argumenty dostępne są w zmiennych $1, $2, $3, itd.

5.5.2 Operacje arytmetyczne i warunki logiczne

Powłoka tcsh pozwala na wykonywanie prostych operacji arytmetycznych za pomocą instrukcji @.
Przykład:
$ @ suma = 2 + 2
$ echo $suma
$ 4
$ @ liczba = $suma * 2
$ echo $liczba
$ 8
$ @ suma ++
$ echo $suma
$ 5
Operator ++ zwiększa liczbę zawartą w zmiennej o 1. W tcsh mamy do dyspozycji operatory występujące w języku C, ich lista zawarta jest w tabeli 1:

operator opis przykład
Arytmetyczne
+ dodawanie @ a = $liczba + 1
- odejmowanie @ a = $liczba - 2
/ dzielenie @ a = $liczba / 2
* mnożenie @ a = $liczba * $liczba
% reszta z dzielenia @ a = $liczba % 2
Relacje
> większy od $liczba > 3
< mniejszy od $liczba < 3
== równy $liczba == $zmienna
!= różny od $liczba != 2
Logiczne
&& AND $liczba < 3 && $liczba < 7
|| OR $liczba == 1 || $liczba == 2
! NOT !( $liczba < 2 )
Operatory skracające zapis
++ @ a = $a + 1 @ a ++
-- @ a = $a - 1 @ a --
+= @ a = $a + $liczba @ a += $liczba
-= @ a = $a - $liczba @ a -= $liczba
*= @ a = $a * $liczba @ a *= $liczba
/= @ a = $a / $liczba @ a /= $liczba
Table 1: Operatory arytmetyczne i logiczne dostępne w powłoce tcsh.

Wartością wyrażeń zawierających operatory relacji jest 0 (fałsz) gdy wyrażenie nie jest prawdziwe lub 1 (prawda) gdy wyrażenie jest spełnione.

Proste operacje arytmetyczne można także wykonywać za pomocą instrukcji expr. Tak jak w każdym poleceniu argumenty instrukcji expr muszą byc oddzielone od siebie pustymi znakami.
Przykłady zastosowania:
$ set a=`expr 1 + 1` $ set porownanie=`expr $a == 2` $ set a=`expr $a + 5`

Obliczenia na liczbach zmiennopozycyjnych (rzeczywistych) nalezy wykonywac przy pomocy odpowiednich narzędzi (kalkulatorów).
Kalkulator dowolnej precyzji bc, przykład:
$ echo '1.5 * 3.1' | bc
$ set a=`echo '3.14*2' | bc`
$ set wynik=`echo ``scale=2; 1/$a'' | bc`
Zmienna scale w kalkulatorze bc określa liczbę miejsc po przecinku w wyświetlanym wyniku.

5.5.3 Instrukcje sterujące

Warunek if

Składnia warunku:
 
if ( wyrażenie ) then
       instrukcje
endif
 

Jeśli wyrażenie jest prawdziwe wówczas wykonywane są instrukcje zawarte po słowie then.
Przykład:

#!/bin/tcsh
if( $#argv == 0) then
   echo "Nie podałeś żadnych argumentów "
endif
exit 0

Bardziej rozbudowane wyrażenie warunkowe:
 
if ( wyrażenie ) then
       instrukcje 1
else
       instrukcje 2
endif
 
Instrukcje zawarte w bloku rozpoczynającym się od else są wykonywane gdy wyrażenie nie jest spełnione. Np.:

#!/bin/tcsh
if( $#argv == 0 ) then
   echo "Nie podałeś argumentów"
else
   echo "Podałeś argumenty: $argv"
endif
exit 0

Za pomocą wyrażenia if możemy sprawdzać różne własności plików. Np.:

#!/bin/tcsh
if( -e $argv[1] ) then
   echo "Plik o nazwie $argv[1] istnieje"
else
   echo "Nie ma takiego pliku"
endif

Wyrażenie -e nazwa_pliku powoduje sprawdzenie czy plik o danej nazwie istnieje.
W analogiczny sposób możemy sprawdzić inne własności plików. Najważniejsze z nich zawarte są w tabeli (5.5.3)

-e plik istnieje
-f plik istnieje i jest zwykłym plikiem
-d plik istnieje i jest katalogiem
-r użytkownik posiada prawo do czytania pliku
-w użytkownik posiada prawo do zmiany zawartości pliku
-x użytkownik posiada prawo do wykonywania pliku
-o użytkownik jest właścicielem pliku

Przykład - skrypt o nazwie which.sh poszukuje podanego polecenia w jednym z katalogów zawartych w zmiennej $path i jeśli go znajdzie to wyświetla pełną ścieżkę do tego polecenia (czyli działa analogicznie do programu whereis):

#!/bin/tcsh
if ( $#argv != 1 ) then
   echo "Skrypt określa położenie polecenia"
   echo "Skladnia: $0 polecenie"
   exit 1
endif

foreach katalog ( $path )
   set plik=${katalog}/$argv[1]
   if ( -f $plik && -x $plik ) then
      echo "Znalazlem: $plik"
      exit 0
   endif
end
echo "Nie znalazlem"
exit 2

Uruchomienie skryptu:
$ ./which.sh finger
Znalazlem: /usr/bin/finger
$ ./whitch.sh fing
Nie znalazlem

Jeszcze bardziej rozbudowane wyrażenie warunkowe:
 
if ( wyrażenie 1 ) then
       instrukcje 1
else if ( wyrażenie 1 ) then
              instrukcje 2
endif
 

Przykład:

#!/bin/tcsh
if ( $#argv != 1 ) then
  echo "Podaj liczbe calkowita lub  -h pomoc"
  exit 1
endif
if ( $1 == "-h" ) then
  echo "Skrypt demonstruje uzycie zlozonego warunku if."
  echo "Dla danej liczby calkowitej dowiesz sie czy jest wieksza, mniejsza czy rowna 0."
else if ( $1 > 0) then
    echo "Podales liczbe dodatnia."
else if ( $i < 0 ) then
    echo "Podales liczbe ujemna"
else if ($i == 0 ) then
    echo "Podales zero."
else
  echo Podales argument $1 - nie wiem co z tym zrobic.
endif
exit 0

Pętla while

Składnia pętli while:
 
while ( wyrażenie )
       instrukcje
end
 
Pętla jest wykonywana dopóki spełnione jest wyrażenie.
Przykład - skrypt o nazwie silnia.sh liczący silnię:

#!/bin/tcsh
if ( $#argv != 1 ) then
   echo "Skrypt oblicza silnie z podanej liczby całkowitej"
   echo "Skladnia: $0 liczba"
   exit 1
endif
set i=1
set silnia=1
while( $i < $argv[1])
   @ i = $i + 1
   @ silnia = $silnia * $i
end
echo "Silnia z liczby $argv[1] wynosi $silnia"
exit 0

Uruchomienie skryptu:
$ ./silnia.sh 6
Silnia z liczby 6 wynosi 720

Pętla foreach

Pętla foreach wykonywana jest dla każdej wartości z zadanej lisy elementów.  
foreach zmienna ( lista elementów )
       instrukcje
end
 
W każdym kroku zmienna przybiera wartość kolejnego elementu z listy.
Przykład - skrypt home.sh dla każdego pliku (katalogu) zawartego w katalogu /home sprawdza uprawnienia do odczytu:

#!/bin/tcsh
set pliki=`ls /home`
foreach zmienna ( $pliki )
   if ( -r /home/$zmienna ) then
      echo "Katalog /home/$zmienna - moge z niego czytac"
   endif
end
exit 0

Uruchomienie skryptu:
$ ./home.sh
Katalog /home/212598 - moge z niego czytac
Katalog /home/grochu - moge z niego czytac
Katalog /home/pk2-8 - moge z niego czytac

Instrukcja switch

Instrukcja switch pozwala na wykonanie wybranych instrukcji w zależności od warości przyjmowanej przez pewną zmienną. Działanie bardzo podobne do instrukcji if jednak często wygodniejsze w użyciu.
 
switch ( zmienna )
case
wartość 1 :
       instrukcje 1
       breaksw
case
wartość 2 :
       instrukcje 2
       breaksw
default:
       instrukcje
endsw
 

Przykład:

#!/bin/tcsh
switch ( $1 )
case "-h" :
Ψecho "Opcja -h wyswietla pomoc. Sprobuj takze opcji -v."
Ψbreaksw
case "-v":
Ψecho "Opcja -v wyswietla ten oto komunikat."
Ψbreaksw
default:
Ψecho "Podales nieobslugiwana opcje $1"
endsw

Instrukcja skoku goto

Instrukcja goto powoduje przeskoczenie do linii skryptu oznaczonego pewną etykietą pomijając wszystkie pośrednie instrukcje
 
goto etykieta
       instrukcje 1
etykieta:
       instrukcje 2
 
Wszystkie instrukcje 1 zostaną pominięte.
Przykład - skrypt goto.sh:

#!/bin/tcsh
if ( $#argv == 0 ) then
  goto blad
endif
echo "Podales nastepujace argumenty wywolania sktyptu:"
echo "$argv"
echo "Milego dnia"
exit 0
blad:
echo "Nie podales argumentow - Nastapil skok do etykiety blad"
exit 1

Uruchomienie skryptu:
$ ./goto.sh pewien argument
Podales nastepujace argumenty wywolania sktyptu:
pewien argument
Milego dnia
$ ./goto.sh
Nie podales argumentow - Nastapil skok do etykiety blad

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 $?.

Przykład - skrypt exit.sh:

#!/bin/tcsh
if ( $#argv == 0 ) then
echo "Podaj nazwe pliku jako argument"
exit 1
endif
if ( -f $argv[1] ) then
echo "Ok, plik $argv[1] istnieje"
exit 0
else
echo "Blad, plik $argv[1] nie istnieje"
exit 2
endif

Uruchomienie skryptu:
$ ./exit.sh
Podaj nazwe pliku jako argument
$ echo $?
1
$ ./exit.sh pewienplik
Blad, plik pewienplik nie istnieje
$ echo $?
2
$ ./exit.sh /bin/ls
Ok, plik /bin/ls istnieje
$ echo $?
0