Prowadzący: dr inż. Sebastian Bielski
Kontakt: bolo@mif.pg.gda.pl
Konsultacje:
(gmach
główny)
Podstawy systemów
operacyjnych i programowania -
informacje i
materiały:
Programowanie
Pętle, ilość powtórzeń nieokreślona
1. Powtórzenia wykonywane, gdy spełniony jest dany warunek logiczny (warunek sprawdzany przed wykonaniem).
Pascal
while (warunek) do
begin
instrukcje;
end;
Przykład:
k:=0;
while (k>=0) and (k<=10) do
begin
writeln('podaj k');
readln(k);
end;
C++
while (warunek) {
instrukcje; }
Ten sam przykład:
k=0;
while (k>=0 && k<=10) {
cout << "podaj k" << endl;
cin >>k;}
2. Powtórzenia wykonywane, gdy spełniony jest dany warunek logiczny (warunek sprawdzany po wykonaniu).
Pascal
repeat
instrukcje;
until (warunek)
Przykład:
k:=0;
suma:=0;
repeat
k:=k+1;
suma:=suma+k;
until suma>=100;
C++
do {
instrukcje; }
while (warunek);
Ten sam przykład:
k=0; suma=0;
do {
k=k+1; suma+=k;}
while (suma < 100);
Zwróćmy uwagę na fakt, że w Pascalu powtórzenia przestają się wykonywać w momencie, gdy warunek zostanie spełniony (czyli wykonują się, gdy warunek jeszcze nie jest spełniony), natomiast w C++ powtórzenia przestają się wykonywać w momencie, gdy warunek przestaje być spełniony (czyli wykonują się, gdy warunek jeszcze jest spełniony).
Zadania
1. Napisać program, który oblicza iloczyn wektorowy 2 wektorów 3-elementowych.
2. Napisać program, który odczytuje z klawiatury zmienną typu znakowego tak długo, dopóki wartość tej zmiennej jest inna niż 'a' lub 'A'.
3. Napisać program, który będzie obliczać i wypisywać wartość n! dla kolejnych liczb całkowitych tak długo, dopóki wynik będzie się mieścił w zakresie longint (czyli nie przekroczy 2147483647).
Linux
Uruchamiając terminal, rozpoczynamy
pracę w konkretnym katalogu.
Zazwyczaj jest to katalog główny danego użytkownika. W trakcie pracy
możemy wielokrotnie zmieniać katalog, w którym chcemy pracować.
Katalog, "w którym aktualnie jesteśmy", nazywamy katalogiem bieżącym.
Do sprawdzenia, co jest w danym momencie katalogiem bieżącym, używamy
komendy
pwd
Katalog bieżący zmieniamy, stosując polecenie cd:
cd
nazwa_katalogu_docelowego
Określając nazwę katalogu docelowego, możemy stosować ścieżkę
bezwzględną lub względną. Ścieżka bezwzględna wskazuje dokładną
lokalizację pliku lub katalogu. Ścieżka ta rozpoczyna się od katalogu
głównego "/". Przykład wykorzystania ścieżki bezwzględnej:
cd
/home/FS2007/
Scieżka względna określa położenie docelowego katalogu lub pliku
względem katalogu bieżącego. Przykłady użycia ścieżki względnej:
cd
../FS2007/tmp/ (przechodzimy do katalogu tmp, który
znajduje się w katalogu FS2007, który z kolei znajduje się w katalogu
nadrzędnym w
stosunku do katalogu bieżącego.)
cd ./abc/cde/
lub
cd abc/cde/
(przechodzimy do katalogu cde, który jest podkatalogiem katalogu abc,
który z kolei jest podkatalogiem katalogu bieżącego)
Do wyświetlania informacji o plikach i katalogach znajdujących się w
katalogu bieżącym (lub w innych katalogach) służy polecenie
ls
Jego ważna opcja to -l
ls -l
Po wykonaniu powyższej komendy na ekranie zobaczymy szczegółowe dane o
zasobach zamieszczonych w danym katalogu. Pierwszy znak pierwszej
kolumny określa, czy dany element jest plikiem (-) lub katalogiem (d).
Pozostałe znaki pierwszej kolumny zawierają informacje o atrybutach,
czyli prawach dostępu. Prawa dostępu mówią o tym, co właściciel danego
elementu (znaki 2-4), inni użytkownicy należący do tej samej grupy
użytkowników co właściciel (znaki 5-7), oraz wszyscy pozostali
użytkownicy (znaki 8-10), mogą zrobić z danym elementem. W danej trójce
znaków pierwszy oznacza prawo do odczytu (r) lub jego brak (-), drugi
znak oznacza prawo do zapisu, czyli do zmieniania danego elementu (w)
lub jego brak, trzeci znak oznacza prawo "do wykonywania", czyli np.
uruchamiania pliku będącego programem, (x), lub jego brak (-).
Właściciel może zmieniać atrybuty komendą chmod.
Niekiedy interesują nas tylko niektóre spośród plików z danego
katalogu. Na przykład, chcemy wyświetlić listę tych plików, których
nazwy rozpoczynają się literą a:
ls a* - gwiazdka reprezentuje dowolny ciąg znaków (również ciąg
pusty - o zerowej długości).
Inny przykład - interesują nas tylko pliki z rozszerzeniem txt:
ls *.txt
Chcemy wyświetlić listę plików, których drugim znakiem w nazwie jest
litera a, b lub c:
ls ?[abc]* - znak zapytania reprezentuje jeden dowolny znak (w naszym
przykładzie pierwszy znak nazwy może być jakikolwiek), nawias [ ]
zawiera listę znaków, spośród których jeden ma się pojawić na drugim
miejscu w nazwie. Ponieważ są to znaki, które występują kolejno w
alfabecie, można było w nawiasie podać przedział interesujących nas
znaków:
ls ?[a-c]*
Chcemy wyświetlić listę plikówz rozszerzeniem txt, których nazwy
rozpoczynają się cyfrą od 1 do 5, lub 7 lub 9:
ls [1-579]*.txt
Kopiowanie i przenoszenie plików
cp
plik_zrodlowy plik_docelowy - kopiowanie
mv
plik_zrodlowy plik_docelowy - przenoszenie, ewentualnie zmiana
nazwy - jeśli plik źródłowy i docelowy są w tym samym katalogu.
Podając nazwy plików źródłowych i docelowych, korzysta się ze ścieżek
bezwzględnych lub względnych.
Przekierowanie wyjścia
Standardowym wyjściem wielu komend jest ekran, np. po wpisaniu komendy
ls listę plików zobaczymy właśnie na ekranie. Informacje "wychodzące"
możemy przekierować np. do pliku albo do systemowego kosza. Robimy to
stosując symbol przekierowania >
ls > plik
Informacje zostaną zapisane w pliku o nazwie plik. Jeśli taki plik już
istniał, "stara" zawartość zostanie skasowana.
ls -l
>> plik
Jeśli plik już istniał, "stara" zawartość nie zostanie skasowana, a
nowe dane zostaną dopisane na końcu pliku.
Domyślnie przekazywane są informacje z kanału danych. Jest jeszcze
kanał informacji o błędach
ls -l
plik.txt 1> dane 2> bledy
ls -l plik.txt - generuje linię ze szczegółowymi informacjami na temat
pliku plik.txt. Informacje te (kanał danych - nr 1) zostaną zapisane w
pliku o nazwie dane. Ewentualne informacje o błędach, które miały
miejsce w trakcie wykonania instrukcji ls -l plik.txt (przykładowo -
plik.txt może nie istnieć), a zatem informacje z kanału błędów (nr 2),
zostaną zapisane w pliku bledy.
Polecenie cat
służy między innymi do wypisania zawartości pliku do standardowego
wyjścia.
cat plik.txt - plik zostanie wyświetlony na ekranie
cat plik.txt >> plik1.txt - zawartość pliku plik.txt zostanie
dopisana do pliku plik1.txt.
Polecenie grep
służy między innymi do wyszukania w danych pochodzących np. z
wyświetlenia pliku lub z polecenia ls linii, zawierających (lub nie)
pewnien wzorzec
grep
"informatyka" plik.txt - wypisze tylko te linie z pliku
plik.txt, które zawierają słowo informatyka
grep -v
"informatyka" plik.txt - wypisze tylko te linie z pliku
plik.txt, które nie zawierają słowa informatyka
grep ^d
plik.txt - wypisze tylko te linie z pliku plik.txt, które
zaczynają się znakiem d
grep d$
plik.txt - wypisze tylko te linie z pliku plik.txt, które kończą
się znakiem d
Potoki
"wyjście" z jednego polecenia, może byc od razu użyte jako "wejście" do
kolejnego polecenia
ls -l | grep
^d - spośród linii generowanych przez polecenie ls -l, wybrane
zostają tylko te, które rozpoczynają się znakiem d
cat plik.txt
| grep "informatyk" - por. informacje o poleceniach cat i grep
aliasy
Możemy stworzyć własne polecenia, które będą na przykład skrótami
poleceń lub ciągów poleceń.
alias ls1="ls
-ltr"
zdefiniowaliśmy nową instrukcję, której efekty są takie same jak ls
-ltr. W aliasach można stosować potoki:
alias ins1="ls
-l | grep
^d"
aliasy
działają do momentu wylogowania się, lub do momentu
anulowania instrukcją unalias
unalias ins1
Polecenie wc
Podaje informację na temat wielkości na przykład pliku.
wc -l plik.txt
wynikiem będzie liczba wierszy zawartych w pliku plik.txt
ls -l | grep
^d | wc -l
wynikiem
będzie liczba wierszy generowanych poleceniem ls -l | grep
^d
Polecenie cut
Załóżmy, że mamy plik 1.txt z zawartością
abx
cby
Zawartość tę można rozumieć jako dwa wiersze z dwiema kolumnami,
rozdzielonymi literą b.
cat 1.txt |
cut -db -f1
Na ekranie wyświetli się tylko pierwsza kolumna. -db
oznacza, że jako separator
wybrany jest znak b, -f1
oznacza, że chcemy wyświetlić tylko kolumnę nr
1. Plik wejściowy pozostanie nie zmieniony.
Plik 2.txt ma zawartość
qwe rty uio p[]
ad fh jl ;'
cat 2.txt |
cut "-d " -f3-4
Jako separator wybraliśmy spację, chcemy zobaczyć kolumny od 3 do 4.
Wynikiem będzie
uio p[]
jl ;'
Wynik możemy przekierować do nowego pliku:
cat 2.txt |
cut "-d " -f3-4 > 2_new.txt
Polecenie tr
Załóżmy, że mamy plik zam.txt z zawartością
abc def xyz
def ijk mno
cat zam.txt |
tr e q
Na ekranie zamiast e pojawi się q.
cat zam.txt |
tr e " "
Na ekranie zamiast e pojawi się spacja.
cat zam.txt |
tr def cvb
Na ekranie zamiast def pojawi się cvb.
cat zam.txt |
tr [def] t
Każda z liter d, e, bądź f zmieni się w t.
cat plik | tr
. ,
Przydatne np. w plikach z liczbami, aby zmienić separator części
dziesiętnej z kropki na przecinek.
cat zam.txt |
tr def cvb > zam1.txt
Zapisanie wyniku nie na ekranie, a w nowym pliku.
Instrukcja for - tworzenie pętli
for {zmienna
sterująca} in {lista} ; do {instrukcja}; done
Przykład
for i in a b;
do echo x; done
dwa razy wypisany zostanie "x" - dla każdej z dwóch wartości (a i b)
zmiennej sterującej i.
for i in a b;
do echo $i; done
wypisanie wartości zmiennej sterującej w kolejnych krokach (znak $
oznacza, że chodzi o wartość)
for i in 1
10; do echo $i; done
działa podobnie, jak powyżej
for i in `seq
1 10`; do echo $i; done
` ` - w tych apostrofach umieszczamy instrukcje, służące wygenerowaniu
złożonej listy wartości zmiennej sterującej. Polecenie seq 1 10
oznacza
generowanie kolejnych wartości od 1 do 10.
wyn=0; for i
in `seq 1 10`; do wyn=$((${wyn}+${i})); done ; echo $wyn
Stworzyliśmy zmienną wyn i nadaliśmy jej wartość 0. W każdym kroku
pętli wartość wyn zmieniamy w
tej sposób, że do aktualnej warości wyn dodajemy aktualną wartość
zmiennej sterującej i. Na końcu wyświetlamy ostateczną wartość wyn,
która w tym przypadku jest sumą liczb od 1 do 10.
Podwójne nawiasy (( ))
zawierają wyrażenie arytmetyczne. wyn=$(( ))
oznacza, że wyn przyjmuje wartość wyrażenia zawartego w nawiasach.
Zamiast (( ))
można użyć [
]
wyn=0; for i
in `seq 1 10`; do wyn=$[${wyn}+${i}]; done ; echo $wyn
Poniżej nieco inne użycie tej instrukcji (bez określenia, że chodzi o
wartość wyrażenia arytmetycznego)
wyn=0; for i
in `seq 1 10`; do wyn=${wyn}+${i}; done ; echo $wyn
Lista może być wynikiem jeszcze innych instrukcji, np.
for i in `ls
| grep ^a` ; do cp ${i} ${i}_back ; done
Wszystkie pliki rozpoczynające się literą a zostaną skopiowane, kopie
będą miały nazwy takie jak oryginały, ale dostawiona zostanie końcówka
_back.
W trakcie pracy system wykorzystuje
tzw. zmienne środowiskowe.
Wartości tych zmiennych
wpływają na sposób działania wykorzystywanych programów czy poleceń. W celu wyświetlenia
wszystkich używanych zmiennych środowiskowych
(i ich wartości), stosujemy polecenie
env
Na przykład zmienna USER przechowuje
nazwę użytkownika. Wartość zmiennej sprawdzamy następująco
echo $USER
Oprócz zmiennych środowiskowych
istnieją zmienne powłokowe.
Własne zmienne tworzymy
następująco
zmienna=wartosc
Jeżeli dodatkowo użyjemy polecenia export,
utworzymy zmienną w sposób bardziej
ogólny z punktu widzenia działania powłoki (np. nowa zmienna
będzie "widziana" przy użyciu
polecenia env)
export
zmienna2=wartosc2
lub dwuetapowo
zmienna2=wartosc2
export zmienna2
zmiana wartości zmiennej
zm1=12
zm2=13
zm1=zm1+zm2
- zm1 będzie miała wartość nie 25 a będzie to tekst "zm1+zm2"
zm1=$zm1+zm2
- do wartości zm1 zostanie dodany, czy raczej dostawiony, ciąg znaków "zm2". Jeżeli w systemie
istniałaby zmienna o nazwie zm1+zm2,
pojawiłaby się niejednoznaczność, jak interpretować to polecenie. Aby uniknąć niejednoznaczności,
można zastosować nawias {}:
zm1=${zm1}+zm2
- teraz na pewno do wartości zm1 zostanie dodany (dostawiony) ciąg znaków "zm2".
Uwaga: przy ćwiczeniu powyższych
poleceń warto po każdym z nich sprawdzić
aktualną wartość zmiennej zm1 (echo $zm1).
Zmienna ? jest przykład tzw.
zmiennej specjalnej. Przechowuje ona tzw. kod powrotu ostatniego wykonywanego polecenia; wynosi
on 0 - jeśli się polecenie się powiodło,
lub inną wartość jeśli nie.
Stwórzmy w katalogu bieżącym plik
abc.txt. Następnie użyjmy polecenia
ls -l abc.txt -
polecenie to się powiedzie, bo plik istnieje, a zatem sprawdzając wartość zmiennej ?
echo $?
otrzymamy 0.
Z kolei, jeśli w katalogu bieżącym
nie ma pliku abc111.txt, po użyciu poleceń
ls -l
abc111.txt
echo $?
otrzymamy 1.
Instrukcja test
Służy do sprawdzania warunków
logicznych. Składnia:
test warunek
Jeśli wynikiem sprawdzenia jest
prawda, kod powrotu będzie równy 0. W przeciwnym przypadku - kod powrotu będzie
równy 1. Przykład
test 2 -eq 3
- sprawdzamy czy 2 jest równe 3. Oczywiście, po użyciu polecenia echo $?
otrzymamy 1 (bo 2 nie jest równe 3). Inne przykłady użycia polecenia test:
test -f abc
- sprawdza, czy istnieje w katalogu bieżącym plik abc.
test -d abc
- sprawdza, czy plik abc jest katalogiem.
test -r abc
- sprawdza, czy użytkownik ma prawo do odczytu pliku abc.
test -w abc
- sprawdza, czy użytkownik ma prawo do zapisu pliku abc.
test -x abc
- sprawdza, czy użytkownik ma prawo do wykonania pliku (programu) abc.
test $a -eq 101
- sprawdza, czy wartość zmiennej a jest równa 101.
test $a -ne 101
- sprawdza, czy wartość zmiennej a jest różna od 101.
test $a -gt 101
- sprawdza, czy wartość zmiennej a jest większa od 101.
test $a -ge 101
- sprawdza, czy wartość zmiennej a jest większa od lub równa 101.
test $a -lt 101
- sprawdza, czy wartość zmiennej a jest mniejsza od 101.
test $a -le 101
- sprawdza, czy wartość zmiennej a jest mniejsza od lub równa 101.
Uwaga - tworzenie bardziej złożonych
warunków
! - oznacza negację
-a - oznacza koniunkcję
-o - oznacza alternatywę
test 2 -eq 2 -a $a
-eq 111 - sprawdza, czy jednocześnie prawdziwe są dwa warunki: 2 jest równe 2, wartość a
wynosi 111.
test 2 -eq 7 -o $a
-eq 1 - sprawdza, czy prawdziwy jest przy najmniej jeden z warunków: 2 jest równe 7, wartość
a wynosi 1.
test ! -f abc
- sprawdza, czy nie istnieje w katalogu bieżącym plik abc; ! -f abc jest zaprzeczeniem warunku -f
abc
Taki sam efekt jak w powyższych
przykładach można uzyskać bez pisania polecenia test, ale wówczas warunek
logiczny musi być zawarty w nawiasie [ ]:
[ -f abc ]
[ 2 -eq 7 -o
$a -eq 1 ]
itd.
Instrukcje warunkowe
&&
ins1 &&
ins2 - jeżeli po wykonaniu pierwszej instrukcji kod powrotu wynosi 0, wykona się druga instrukcja.
ls abc &&
rm abc - jeśli plik abc istnieje, zostanie skasowany.
||
ins1 || ins2
- jeśli pierwsza instrukcja wykona się prawidłowo, druga już się nie wykona. Jeśli natomiast po
wykonaniu pierwszej instrukcji kod
powrotu będzie różny od 0, wykona się druga instrukcja.
ls abcd || touch
abcde - jeśli plik abcd istnieje, druga instrukcja się nie wykona, jeżeli natomiast plik nie
istnieje, stworzony zostanie plik abcde.
if
if instr1;
then instr2; fi
instr1 -
powinna polegać na sprawdzaniu jakiegoś warunku logicznego.
if test ! -f
c1.txt ; then touch c1.txt ; fi - jeśli nie istnieje plik c1.txt, to zostanie stworzony. Inny sposób
na ten sam efekt:
if [ ! -f c1.txt ] ; then
touch c1.txt ; fi
Jeszcze inny sposób:
ls c1.txt || touch c1.txt
if test -f c2.txt
; then mv c2.txt c21.txt ; fi - jeśli istnieje plik c2.txt, to jego nazwa zmieni się na
c21.txt. Inny sposób:
if [ -f c2.txt ] ; then mv c2.txt
c21.txt ; fi
Jeszcze inny sposób:
ls c2.txt
&& mv c2.txt c21.txt
if test -f c1.txt
-o -f c21.txt ; then echo "OK" ; fi - jeżeli istnieje jeden z wymienionych plików -
pojawi się napis OK.
if [ -f c1.txt -o
-f c21.txt ] ; then echo "OK" ; fi - to samo, co powyżej.
if test ! -x
c3.out ; then chmod u+x c3.out ; fi - jeżeli użytkownik nie ma prawa wykonywania pliku c3.out, to
prawo to zostanie mu dodane.
Skrypty
Proste "programy", pliki textowe
składające się z komend powłokowych. Aby skrypt działał poprawnie, muszą być
spełnione warunki:
1. Pierwszy wiersz pliku powinien
wyglądać następująco:
#!/bin/bash
2. Użytkownik musi mieć prawo do
wykonania pliku. Należy więc po stworzeniu pliku użyć polecenia:
chmod u+x
nazwa_pliku
Skrypt, który wypisze na ekranie
"dzien dobry" i poda nazwę użytkownika (wartość zmiennej USER):
#!/bin/bash
echo "dzien dobry"
echo "jestes zalogowany jako" $USER
Skrypt, który poprosi o podanie 2
liczb i wyświetli ich sumę:
#!/bin/bash
echo "podaj 1 liczbe"
read a
echo "podaj 2 liczbe"
read b
echo suma wynosi: $(($a+$b))
Skrypt, który wyświetli sume liczb
podawanych w trakcie uruchamiania tego skryptu:
#!/bin/bash
echo suma wynosi: $(($1+$2))
Uwaga: powyższy skrypt uruchamiamy
następująco:
./nazwa_
skryptu 12 13
(12 i 13 to przykładowe liczby).
Skrypt wykorzystał 2 zmienne o nazwach 1 i 2, ich wartości odpowiadają wartościom
kolejnych liczb podawanych na
etapie uruchamiania skryptu.
Skrypt, który skopiuje plik. Nazwy
pliku źródłowego i pliku docelowego podawane się w trakcie uruchamiania
skryptu:
#!/bin/bash
cp $1 $2
Uwaga: powyższy skrypt uruchamiamy
następująco:
./nazwa_skryptu
nazwa_pliku_1 nazwa_pliku_2