Środowisko programisty/Bash - pisanie skryptów: Różnice pomiędzy wersjami
→Atrybuty plików: Pierwsza wersja |
|||
(Nie pokazano 33 wersji utworzonych przez 2 użytkowników) | |||
Linia 1: | Linia 1: | ||
== Atrybuty plików == | == Atrybuty plików == | ||
W | W systemie typu Unix jest podział na użytkowników i grupy. Każdy użytkownik może przynależeć do kilku grup. Do wyświetlania przynależności do grup służy polecenie groups. | ||
bashtest@host:~$ groups | bashtest@host:~$ groups | ||
users | users | ||
Linia 9: | Linia 9: | ||
kubus : users cdrom floppy audio src video staff | kubus : users cdrom floppy audio src video staff | ||
bashtest@host:~$ | bashtest@host:~$ | ||
Bez argumentów wyświetla przynależność do grup aktualnego użytkownika. Z argumentami przynależność do grup podanych użytkowników. Na przykład użytkownik kubus przynależy do większej ilości grup, co daje mu większe prawa w systemie. | Bez argumentów wyświetla przynależność do grup aktualnego użytkownika. Z argumentami przynależność do grup podanych użytkowników. Na przykład użytkownik <code>kubus</code> przynależy do większej ilości grup, co daje mu większe prawa w systemie. | ||
Każdy plik/katalog należy do dokładnie jednego użytkownika i grupy. Z każdym plikiem/katalogiem związane są trzy rodzaje praw dostępu: | Każdy plik/katalog należy do dokładnie jednego użytkownika i grupy. Z każdym plikiem/katalogiem związane są trzy rodzaje praw dostępu: | ||
{| | {| | ||
| | | <code>r</code> || prawo do odczytu, | ||
|- | |- | ||
| | | <code>w</code> || prawo do modyfikacji (czyli do zapisu, bądź usunięcia), | ||
|- | |- | ||
| | | <code>x</code> || prawo do uruchomienia; w przypadku katalogu oznacza to prawo do zmiany bieżącego katalogu na ten katalog. | ||
|} | |} | ||
Prawa dostępu przydzielane są trzem kategoriom użytkowników: | Prawa dostępu przydzielane są trzem kategoriom użytkowników: | ||
Linia 23: | Linia 23: | ||
# inni użytkownicy z grupy, do której należy dany plik, | # inni użytkownicy z grupy, do której należy dany plik, | ||
# wszyscy pozostali użytkownicy. | # wszyscy pozostali użytkownicy. | ||
Aby wyświetlić informacje o właścicielach i prawach dostępu możemy użyć polecenia ls z opcją -l: | Aby wyświetlić informacje o właścicielach i prawach dostępu możemy użyć polecenia <code>ls</code> z opcją <code>-l</code>: | ||
bashtest@host:~$ ls -l | bashtest@host:~$ ls -l | ||
razem 128 | razem 128 | ||
Linia 34: | Linia 34: | ||
Po lewej stronie są prawa dostępu. Literka po lewej mówi o typie pliku, kolejne trzy literki pokazują prawa dostępu dla pierwszej kategorii użytkowników, kolejne trzy o drugiej kategorii użytkowników i ostatnie trzy literki o ostatniej kategorii. W trzeciej i czwartej kolumnie pokazany jest użytkownik i grupa do której należy dany plik/katalog. | Po lewej stronie są prawa dostępu. Literka po lewej mówi o typie pliku, kolejne trzy literki pokazują prawa dostępu dla pierwszej kategorii użytkowników, kolejne trzy o drugiej kategorii użytkowników i ostatnie trzy literki o ostatniej kategorii. W trzeciej i czwartej kolumnie pokazany jest użytkownik i grupa do której należy dany plik/katalog. | ||
<code>Mail</code> i <code>niedostępny_katalog</code> są katalogami (literka <code>d</code> po lewej). Katalog <code>Mail</code> jest dostępny tylko dla użytkownika <code>bashtest</code> (2-4 literki <code>rwx</code> oznaczają ustawione wszystkie prawa dostępu: do odczytu, zapisu i uruchamiania). Katalog <code>niedostępny_katalog</code> nie ma ustawionych praw do odczytu, zatem nie można wyświetlić jego zawartości, ale można zmienić na niego bieżący katalog, gdyż ma ustawione prawa do uruchomienia. Plik <code>plik_dla_pozostałych_userów</code> mogą odczytywać i modyfikować tylko użytkownicy inni niż <code>bashtest</code> należący do grupy <code>users</code>. Plik <code>program</code> jest programem i można go uruchamiać. | |||
Do zmiany właściciela służą polecenia <code>chown</code> i <code>chgrp</code>. Do zmiany praw dostępu służy polecenie <code>chmod</code>. | |||
== Pierwszy skrypt == | == Pierwszy skrypt == | ||
Przygotujmy plik <code>hello_world.sh</code> o następującej zawartości: | |||
#!/bin/sh | |||
echo "Hello world" | |||
Rozszerzenie <code>sh</code> jest standardowym rozszerzeniem skryptów napisanych w bashu. Nie jest ono konieczne, ale dobrze by było, żeby już sama nazwa pliku mówiła nam o jego typie. Pierwsza linijka jest podpowiedzią dla systemu, jak ma być uruchomiony ten plik. System użyje polecenia <code>/bin/sh</code> do interpretacji tego pliku. | |||
Spróbujmy uruchomić ten plik. | |||
bashtest@host:~$ hello_world.sh | |||
bash: hello_world.sh: command not found | |||
bashtest@host:~$ | |||
Takie polecenie nie zostało znalezione. System szuka danego polecenia wśród wszystkich katalogów zapamiętanych na zmiennej środowiskowej <code>PATH</code>. Zmienna środowiskowa jest to taka zmienna, która została zdefiniowana zanim jeszcze uruchomiliśmy interpreter. Własne zmienne środowiskowe, które zostaną przekazane programom przez nas uruchomionych można definiować za pomocą komendy <code>export</code>. Zobaczmy, co zawiera zmienna <code>PATH</code>. | |||
bashtest@host:~$ echo $PATH | |||
/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games | |||
bashtest@host:~$ | |||
Jak widzimy, nie zawiera ona bieżącego katalogu, w którym znajduje się nasz skrypt. Przy uruchamianiu polecenia, które nie znajduje katalogu podanym w <code>PATH</code>, trzeba podawać również ścieżkę (względną, bądź bezwzględna) przed nazwą pliku. W tym przypadku musimy podać katalog bieżący, co najprościej można zrobić przy użyciu kropki. | |||
bashtest@host:~$ ./hello_world.sh | |||
bash: ./hello_world.sh: Brak dostępu | |||
bashtest@host:~$ | |||
Tym razem dostaliśmy komunikat o złych prawach dostępu. Zobaczmy: | |||
bashtest@host:~$ ls -l hello_world.sh | |||
-rw-r--r-- 1 bashtest users 29 2006-08-07 15:45 hello_world.sh | |||
bashtest@host:~$ | |||
Ten plik nie ma ustawionych praw do uruchamiania. Możemy to zrobić używając polecenia <code>chmod</code>. Aby ustawić prawa uruchamiania tylko dla użytkownika <code>bashtest</code>, możemy użyć opcji <code>u+x</code>. Jeśli chcemy ustawić prawa uruchamiania dla wszystkich, używamy opcji <code>a+x</code>. W tym przypadku ustawimy prawa uruchamiania tylko dla nas. | |||
bashtest@host:~$ chmod u+x hello_world.sh | |||
bashtest@host:~$ ls -l hello_world.sh | |||
-rwxr--r-- 1 bashtest users 29 2006-08-07 15:45 hello_world.sh | |||
bashtest@host:~$ | |||
Teraz wygląda lepiej spróbujmy uruchomić nasz skrypt. | |||
bashtest@host:~$ ./hello_world.sh | |||
Hello world | |||
bashtest@host:~$ | |||
Udało się! | |||
== Komentarze == | == Komentarze == | ||
Komentarze zaczynają się od symbolu <code>#</code>. Wszystkie pozostałe znaki aż do końca linii są ignorowane. W pierwszej linii skryptu <code>helo_world.sh</code> mamy już taki komentarz, który jest zarazem informacją dla systemu. Dodajmy jeszcze dwa komentarze. | |||
#!/bin/sh | |||
# Przykładowy skrypt wypisujący napis "Hello world" | |||
echo "Hello world" # Tutaj wypisujemy co trzeba | |||
== Argumenty == | == Argumenty == | ||
Skrypty - podobnie jak dowolne programy - możemy uruchamiać podając im argumenty. Następujące zmienne o specjalnych nazwach pozwalają odczytywać argumenty: | |||
{| | |||
| <code>$#</code> || zwraca liczbę argumentów, | |||
|- | |||
| <code>$0</code> || zwraca nazwę pliku bieżącego programu, | |||
|- | |||
| <code>$1</code>, <code>$2</code>, ... || zwraca odpowiednio pierwszy argument, drugi argument, itd., | |||
|- | |||
| <code>$@</code> || rozwija się do listy wszystkich argumentów; przydatne jeśli chcemy przekazać wszystkie argumenty innemu programowi. Jeśli chcemy mieć pewność, że każdy argument będzie osobnym słowem, należy użyć cudzysłowów: <code>"$@"</code>; ma to znaczenie na przykład wtedy, gdy istnieje argument, który zawiera spację. | |||
|} | |||
Aby operować na dalszych argumentach pomocne jest polecenie <code>shift</code>, które usuwa pierwszy argument, a pozostałe przesuwa o jeden w lewo. Aby ''n''-krotnie wywołać polecenie <code>shift</code> wystarczy podać mu to ''n'' jako argument: <code>shift</code> ''n''. | |||
Na przykład dla skryptu <code>test_arg.sh</code> o zawartości | |||
#!/bin/sh | |||
# Testowanie argumentów | |||
echo "Uruchomiłeś program `basename $0`" | |||
echo Wszystkie: $@ | |||
echo "Pierwsze trzy: '$1', '$2', '$3'" | |||
shift 2 | |||
echo "shift 2" | |||
echo "Wszystkie: $@" | |||
echo "Pierwsze trzy: '$1', '$2', '$3'" | |||
mamy efekt | |||
bashtest@host:~$ ./test_arg.sh Raz Dwa "To jest zdanie" Cztery | |||
Uruchomiłeś program test_arg.sh | |||
Wszystkie: Raz Dwa To jest zdanie Cztery | |||
Pierwsze trzy: 'Raz', 'Dwa', 'To jest zdanie' | |||
shift 2 | |||
Wszystkie: To jest zdanie Cztery | |||
Pierwsze trzy: 'To jest zdanie', 'Cztery', ''''''' | |||
bashtest@host:~$ | |||
== Wyrażenia == | == Wyrażenia == | ||
Jak w każdym liczącym się języku, w bashu możemy wyliczać wartości wyrażeń arytmetycznych. Możemy zrobić to na kilka sposobów. | |||
=== expr === | |||
Najprostszym sposobem jest użycie polecenia <code>expr</code>. Trzeba przy tym pamiętać, żeby osobne tokeny (tzn. liczby i operatory arytmetyczne) były podawane w osobnych argumentach. Wynika to stąd, że <code>expr</code> potrafi też operować na łańcuchach znakowych (czym się nie będziemy w tej chwili zajmować), więc musi jakoś te łańcuchy dostawać, a jedyną droga to przez argumenty. | |||
Dostępnych jest pięć operatorów arytmetycznych: | |||
* dodawanie (<code>+</code>), | |||
* odejmowanie (<code>-</code>), | |||
* mnożenie (<code>*</code>), | |||
* dzielenie (<code>/</code>), | |||
* modulo - reszta z dzielenia (<code>%</code>). | |||
Ponadto możemy wykonywać porównania <code><</code>, <code><=</code>, <code>=</code>, <code>==</code> (synonim <code>=</code>), <code>!=</code>, <code>>=</code>, <code>></code>. W wyniku mamy 1, gdy relacja jest spełniona i 0 w przeciwnym przypadku. | |||
Trzeba też pamiętać by znaki specjalne poprzedzać backslashem lub brać w cudzysłowy. Przykłady: | |||
bashtest@host:~$ expr 2\*3 | |||
2*3 | |||
bashtest@host:~$ expr 2 \* 3 | |||
6 | |||
bashtest@host:~$ expr '2 * 3' | |||
2 * 3 | |||
bashtest@host:~$ expr 2 \* \(7 - 1\) | |||
expr: argument nieliczbowy | |||
bashtest@host:~$ expr 2 \* \( 7 - 1 \) | |||
12 | |||
bashtest@host:~$ a=5 | |||
bashtest@host:~$ a=`expr $a + 1` | |||
bashtest@host:~$ echo $a | |||
6 | |||
bashtest@host:~$ expr 3 \<= 4 | |||
1 | |||
bashtest@host:~$ expr 3 '<=' 1 | |||
0 | |||
bashtest@host:~$ | |||
=== $(( ... )) i (( ... )) === | |||
Znacznie wygodniejszą formą pisania wyrażeń jest forma <code>$(( wyrażenie ))</code>. W stosunku do <code>expr</code> w ciapkach ma prawie same zalety. Jest jeden problem, ta składnia może nie działać w innych shellach, czy w starszych wersjach Basha (ale kto teraz używa czegoś innego niż Bash). Pierwszą zaletą jest szybkość, tzn. użycie tej składni nie powoduje tworzenia nowego procesu (co ma miejsce w przypadku <code>`expr ...`</code>) i jest interpretowane bezpośrednio przez Basha. Po drugie przy odwoływaniu się do zmiennych nie musimy poprzedzać ich znakiem <code>$</code>, gdyż każdy identyfikator wewnątrz podwójnych nawiasów jest traktowany jak zmienna. Nie musimy także dbać o używanie odstępów i backslashowania znaków specjalnych. Trzecią zaletą jest bogatsza paleta operatorów arytmetycznych. Otóż wyrażenia arytmetyczne mogą tu zawierać dowolne operatory, które można znaleźć w języku C, np. inkrementacje/dekrementacje zmiennych (<code>ID++</code>, <code>--ID</code>), operacje bitowe (<code><<</code>, <code>&</code>, <code>~</code>), przypisania arytmetyczne (<code>=</code>, <code>+=</code>, <code>*=</code>), itp. Więcej o znaczeniu tych operacji i dozwolonych działaniach można znaleźć w kursie języka C lub w dokumentacji Basha. | |||
Składni <code>(( wyrażenie ))</code> używamy wtedy, gdy nie potrzebujemy wyniku, czyli wtedy, gdy wyrażenie nie jest częścią instrukcji, tylko jest sama w sobie instrukcją. Najlepiej będzie, jak przyjrzymy się przykładom. | |||
Kilka sposobów na zwiększenie zmiennej o 1: | |||
bashtest@host:~$ a=0 | |||
bashtest@host:~$ a=$((a + 1)) | |||
bashtest@host:~$ ((a=a+1)) | |||
bashtest@host:~$ ((a++)) | |||
bashtest@host:~$ ((a += 1)) | |||
bashtest@host:~$ echo $a | |||
4 | |||
bashtest@host:~$ | |||
Inne przykłady: | |||
bashtest@host:~$ echo "1 + ... + $x = $((x * (x + 1) >> 1))" | |||
1 + ... + 5 = 15 | |||
bashtest@host:~$ echo $((x++)) | |||
5 | |||
bashtest@host:~$ echo $((++x)) | |||
7 | |||
bashtest@host:~$ echo $((x += x > 0)) | |||
8 | |||
bashtest@host:~$ echo "x = $x" | |||
x = 8 | |||
bashtest@host:~$ | |||
=== let === | |||
<code>let</code> jest wbudowanym poleceniem Basha i używamy go, podając mu jako argumenty wyrażenia do przetworzenia. | |||
let wyrażenie1 wyrażenie2 ... | |||
równoważne jest ciągowi poleceń | |||
((wyrażenie1)) | |||
((wyrażenie2)) | |||
... | |||
Przykład: | |||
bashtest@host:~$ x=0 | |||
bashtest@host:~$ let x+=2 "x += 4" | |||
bashtest@host:~$ echo $x | |||
6 | |||
bashtest@host:~$ | |||
Trzeba pamiętać, że wyrażenie zawierające odstępy trzeba ujmować w cudzysłowy, aby formowały jeden argument. | |||
== Wczytywanie wejścia == | |||
W skryptach czasami jest potrzeba wczytania czegoś ze standardowego wejścia. Możemy chcieć pobrać od użytkownika jakąś informację. Możemy też chcieć wczytywać standardowe wejście i stopniowo je przetwarzać. Do tych celów jest polecenie <code>read</code>. | |||
<code>read</code> wywołane bez argumentów wczytuje jedną linię ze standardowego wejścia na zmienną o nazwie <code>REPLY</code>. Jeśli podamy jeden argument, <code>read</code> wczyta tą linię na zmienną o nazwie takiej samej, jak zawartość argumentu. Jeśli podamy więcej argumentów reprezentujących nazwy zmiennych, <code>read</code> na pierwsze zmienne będzie wczytywał pojedyncze słowa, a na ostatnią wczyta pozostałość bieżącej linii do jej końca. Prześledźmy to na przykładzie. | |||
Dla skryptu | |||
#!/bin/sh | |||
read | |||
echo $REPLY | |||
read a | |||
echo $a | |||
read a b c | |||
echo "a='$a', b='$b', c='$c'" | |||
read x | |||
echo "'$x'" | |||
i dla wejścia | |||
Pierwsza linia (pamiętać o cudzysłowach przy odwoływaniu się do $REPLY) | |||
Druga linia (teraz pamiętamy - "$a") | |||
Raz Dwa Trzy Cztery | |||
Czwarta linia jest pusta, a to jest piąta linia | |||
otrzymamy wynik | |||
Pierwsza linia (pamiętać o cudzysłowach przy odwoływaniu się do $REPLY) | |||
Druga linia (teraz pamiętamy - "$a") | |||
a='Raz', b='Dwa', c='Trzy Cztery' | |||
'' | |||
== Podawanie wejścia poleceniu w skrypcie == | |||
Gdy wykonujemy polecenie, czasami chcemy zadać mu konkretne wejście. Możemy to zrobić na przykład za pomocą komendy <code>echo</code>: | |||
echo "Nasze wejście" | polecenie | |||
Użycie <code>echo</code> dla wejść, które mają składać się z wielu linii jest jednak kłopotliwe. W tym celu w Bashu jest możliwość podania fragmentu skryptu jako wejście do polecenia. Służy do tego symbol specjalny <code><<</code>. Takie "przekierowanie" <code><< SŁOWO</code> mówi, że wejście dla uruchamianego polecenia ma być czytane z aktualnego wejścia tak długo, aż zostanie napotkany napis <code>SŁOWO</code>. Na przykład wynikiem skryptu | |||
#!/bin/sh | |||
echo "Moje ulubione liczby:" | |||
sort -n << LICZBY | |||
120 | |||
10 | |||
2006 | |||
314159 | |||
0 | |||
LICZBY | |||
echo "Od najmniejszej do największej, rzecz jasna" | |||
jest | |||
Moje ulubione liczby: | |||
0 | |||
10 | |||
120 | |||
2006 | |||
314159 | |||
Od najmniejszej do największej, rzecz jasna | |||
== Status wyjścia == | == Status wyjścia == | ||
Każdy program po ukończeniu zwraca swój kod wyjścia. Można go pobrać używając specjalnej zmiennej <code>$?</code>. | |||
bashtest@host:~$ ls *.txt | |||
test.txt | |||
bashtest@host:~$ echo $? | |||
0 | |||
bashtest@host:~$ ls *.nieznane | |||
ls: *.nieznane: Nie ma takiego pliku ani katalogu | |||
bashtest@host:~$ echo $? | |||
2 | |||
bashtest@host:~$ | |||
Zgodnie z konwencją jeśli polecenie wykonało się z sukcesem, kodem wyjścia jest 0, a jeśli w wyniku wykonania pojawiły się błędy lub polecenie skończyło się porażką, zwracany jest kod różny od zera. | |||
Normalnie własny skrypt kończy się ze statusem wyjścia równym zero. Możemy zakończyć skrypt w dowolnym miejscu z wybranym przez nas statusem wyjścia, stosując polecenie <code>exit</code>. Na przykład instrukcja <code>exit 1</code> powoduje natychmiastowe zakończenie skryptu z kodem wyjścia 1. | |||
== Instrukcje warunkowe == | == Instrukcje warunkowe == | ||
=== if === | |||
Instrukcja <code>if</code> w najprostszej postaci ma następującą składnię: | |||
if polecenie_warunek; then | |||
instrukcje | |||
fi | |||
Jej działanie jest następujące. Wykonywane jest polecenie <code>polecenie_warunek</code>. Jeśli kod wyjścia tego polecenia jest 0, wykonywane są instrukcje między <code>then</code>, a <code>fi</code>. Jeśli kod wyjścia polecenia był niezerowy, wykonywanie instrukcji <code>if</code> jest zakończone i interpreter przechodzi do wykonywania instrukcji znajdujących się po słowie kluczowym <code>fi</code>. | |||
Widzimy, że rolę warunków logicznych spełniają tu po prostu zwykłe polecenia, a prawda lub fałsz jest to odpowiednio status wyjścia równy zero lub status wyjścia różny od zera. | |||
Składnia <code>if</code> z użyciem <code>else</code>: | |||
if polecenie_warunek; then | |||
instrukcje1 | |||
else | |||
instrukcje2 | |||
fi | |||
Jeśli warunek jest prawdziwy, wykonywane są <code>instrukcje1</code>, w przeciwnym razie wykonywane są <code>instrukcje2</code>. Przykład: | |||
if cd $katalog; then | |||
echo "Jesteśmy w katalogu $katalog" | |||
else | |||
echo "Nie udało się wejść do katalogu $katalog" | |||
fi | |||
Pełna składnia <code>if</code> jest następująca: | |||
if warunek1; then | |||
instrukcje1 | |||
elif warunek2; then | |||
instrukcje2; | |||
... | |||
else | |||
instrukcje_else; | |||
fi | |||
Część z <code>else</code> jest opcjonalna. <code>instrukcje1</code> są wykonane, jeśli jest spełniony <code>warunek1</code>, w przeciwnym razie, jeśli jest spełniony <code>warunek2</code>, to wykonywane są <code>instrukcje2</code>, itd. Na końcu, jeśli żaden warunek nie jest spełniony, wykonywane są <code>instrukcje_else</code>. | |||
=== Wyrażenia logiczne === | === Wyrażenia logiczne === | ||
=== | Powstaje pytanie, jak tworzyć polecenia, które sprawdzają jakieś sensowne warunki np. porównywanie liczb. Do tego celu służy polecenie <code>test</code>. Potrafi ono porównywać łańcuchy znakowe, liczby i sprawdzać istnienie plików. | ||
Jeśli chodzi o porównywanie łańcuchów znakowych, mamy następujące możliwości. <code>-z ŁAŃCUCH</code> sprawdza, czy długość łańcucha jest równa zero, a <code>-n ŁAŃCUCH</code>, sprawdza, czy długość łańcucha jest różna od zera. Ponadto możemy porównywać dwa łańcuchy np. <code>ŁAŃCUCH1 < ŁAŃCUCH2</code>. Porównanie jest leksykograficzne. Możliwe operatory to <code>==</code>, <code>!=</code>, <code><</code>, <code>></code>. | |||
=== | Do porównywania dwóch liczb są inne operatory: <code>-eq</code>, <code>-ne</code>, <code>-lt</code>, <code>-le</code>, <code>-gt</code>, <code>-ge</code>, których odpowiedniki matematyczne to =, <>, <, <=, >, >=. | ||
Na przykład poniższe polecenia zwrócą prawdę (tj. status wyjścia równy 0): | |||
test -z "" | |||
test abc \< def | |||
test 3 \> 17 | |||
test 3 -lt 17 | |||
Można także sprawdzać istnienie i typ plików, na przykład: | |||
if test -a $plik; then | |||
echo "$plik istnieje" | |||
if test -f $plik; then | |||
echo "$plik jest zwykłym plikiem" | |||
elif test -d $plik; then | |||
echo "$plik jest katalogiem" | |||
fi | |||
fi | |||
Polecenie | |||
test warunek | |||
można też pisać w postaci | |||
[ warunek ] | |||
Taka forma jest po prostu wygodniejsza. | |||
Warto wiedzieć, że instrukcja arytmetyczna <code>(( ... ))</code> też zwraca status. Zwraca 0, jeśli wartość wyrażenia jest niezerowa, i zwraca 1, jeśli wartość wyrażenia wynosi 0. Pozwala to w Bashu stosować w bardzo wygodny sposób porównania, dokładnie tak samo jak w C. | |||
bashtest@host:~$ if (( 0 )); then echo prawda; else echo fałsz; fi | |||
fałsz | |||
bashtest@host:~$ if (( 1 )); then echo prawda; else echo fałsz; fi | |||
prawda | |||
bashtest@host:~$ if (( 3 < 4 )); then echo prawda; else echo fałsz; fi | |||
prawda | |||
bashtest@host:~$ if (( 0 < -1 )); then echo prawda; else echo fałsz; fi | |||
fałsz | |||
bashtest@host:~$ if (( 3 * 6 - 2 * 9 )); then echo prawda; else echo fałsz; fi | |||
fałsz | |||
bashtest@host:~$ if (( 1/0 )); then echo prawda; else echo fałsz; fi | |||
bash: ((: 1/0 : division by 0 (error token is " ") | |||
fałsz | |||
bashtest@host:~$ | |||
Najprostszymi poleceniami, które zwracają prawdę i fałsz, prostszymi nawet niż <code>(( 1 ))</code> i <code>(( 0 ))</code> są <code>true</code> i <code>false</code>, co przydaje się na przykład w pętlach. |
Aktualna wersja na dzień 08:52, 3 wrz 2007
Atrybuty plików
W systemie typu Unix jest podział na użytkowników i grupy. Każdy użytkownik może przynależeć do kilku grup. Do wyświetlania przynależności do grup służy polecenie groups.
bashtest@host:~$ groups users bashtest@host:~$ groups bashtest root kubus bashtest : users root : root kubus : users cdrom floppy audio src video staff bashtest@host:~$
Bez argumentów wyświetla przynależność do grup aktualnego użytkownika. Z argumentami przynależność do grup podanych użytkowników. Na przykład użytkownik kubus
przynależy do większej ilości grup, co daje mu większe prawa w systemie.
Każdy plik/katalog należy do dokładnie jednego użytkownika i grupy. Z każdym plikiem/katalogiem związane są trzy rodzaje praw dostępu:
r |
prawo do odczytu, |
w |
prawo do modyfikacji (czyli do zapisu, bądź usunięcia), |
x |
prawo do uruchomienia; w przypadku katalogu oznacza to prawo do zmiany bieżącego katalogu na ten katalog. |
Prawa dostępu przydzielane są trzem kategoriom użytkowników:
- użytkownicy, do których należy dany plik,
- inni użytkownicy z grupy, do której należy dany plik,
- wszyscy pozostali użytkownicy.
Aby wyświetlić informacje o właścicielach i prawach dostępu możemy użyć polecenia ls
z opcją -l
:
bashtest@host:~$ ls -l razem 128 drwx------ 2 bashtest users 4096 2006-07-08 09:37 Mail d-wx--x--x 2 bashtest users 4096 2006-08-07 15:28 niedostępny_katalog ----rw---- 1 bashtest users 5 2006-08-07 15:30 plik_dla_pozostałych_userów -rwxr-xr-x 1 root root 109552 2006-08-07 15:32 program -rw-r--r-- 1 bashtest users 13 2006-08-01 15:18 test.txt bashtest@host:~$
Po lewej stronie są prawa dostępu. Literka po lewej mówi o typie pliku, kolejne trzy literki pokazują prawa dostępu dla pierwszej kategorii użytkowników, kolejne trzy o drugiej kategorii użytkowników i ostatnie trzy literki o ostatniej kategorii. W trzeciej i czwartej kolumnie pokazany jest użytkownik i grupa do której należy dany plik/katalog.
Mail
i niedostępny_katalog
są katalogami (literka d
po lewej). Katalog Mail
jest dostępny tylko dla użytkownika bashtest
(2-4 literki rwx
oznaczają ustawione wszystkie prawa dostępu: do odczytu, zapisu i uruchamiania). Katalog niedostępny_katalog
nie ma ustawionych praw do odczytu, zatem nie można wyświetlić jego zawartości, ale można zmienić na niego bieżący katalog, gdyż ma ustawione prawa do uruchomienia. Plik plik_dla_pozostałych_userów
mogą odczytywać i modyfikować tylko użytkownicy inni niż bashtest
należący do grupy users
. Plik program
jest programem i można go uruchamiać.
Do zmiany właściciela służą polecenia chown
i chgrp
. Do zmiany praw dostępu służy polecenie chmod
.
Pierwszy skrypt
Przygotujmy plik hello_world.sh
o następującej zawartości:
#!/bin/sh echo "Hello world"
Rozszerzenie sh
jest standardowym rozszerzeniem skryptów napisanych w bashu. Nie jest ono konieczne, ale dobrze by było, żeby już sama nazwa pliku mówiła nam o jego typie. Pierwsza linijka jest podpowiedzią dla systemu, jak ma być uruchomiony ten plik. System użyje polecenia /bin/sh
do interpretacji tego pliku.
Spróbujmy uruchomić ten plik.
bashtest@host:~$ hello_world.sh bash: hello_world.sh: command not found bashtest@host:~$
Takie polecenie nie zostało znalezione. System szuka danego polecenia wśród wszystkich katalogów zapamiętanych na zmiennej środowiskowej PATH
. Zmienna środowiskowa jest to taka zmienna, która została zdefiniowana zanim jeszcze uruchomiliśmy interpreter. Własne zmienne środowiskowe, które zostaną przekazane programom przez nas uruchomionych można definiować za pomocą komendy export
. Zobaczmy, co zawiera zmienna PATH
.
bashtest@host:~$ echo $PATH /usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games bashtest@host:~$
Jak widzimy, nie zawiera ona bieżącego katalogu, w którym znajduje się nasz skrypt. Przy uruchamianiu polecenia, które nie znajduje katalogu podanym w PATH
, trzeba podawać również ścieżkę (względną, bądź bezwzględna) przed nazwą pliku. W tym przypadku musimy podać katalog bieżący, co najprościej można zrobić przy użyciu kropki.
bashtest@host:~$ ./hello_world.sh bash: ./hello_world.sh: Brak dostępu bashtest@host:~$
Tym razem dostaliśmy komunikat o złych prawach dostępu. Zobaczmy:
bashtest@host:~$ ls -l hello_world.sh -rw-r--r-- 1 bashtest users 29 2006-08-07 15:45 hello_world.sh bashtest@host:~$
Ten plik nie ma ustawionych praw do uruchamiania. Możemy to zrobić używając polecenia chmod
. Aby ustawić prawa uruchamiania tylko dla użytkownika bashtest
, możemy użyć opcji u+x
. Jeśli chcemy ustawić prawa uruchamiania dla wszystkich, używamy opcji a+x
. W tym przypadku ustawimy prawa uruchamiania tylko dla nas.
bashtest@host:~$ chmod u+x hello_world.sh bashtest@host:~$ ls -l hello_world.sh -rwxr--r-- 1 bashtest users 29 2006-08-07 15:45 hello_world.sh bashtest@host:~$
Teraz wygląda lepiej spróbujmy uruchomić nasz skrypt.
bashtest@host:~$ ./hello_world.sh Hello world bashtest@host:~$
Udało się!
Komentarze
Komentarze zaczynają się od symbolu #
. Wszystkie pozostałe znaki aż do końca linii są ignorowane. W pierwszej linii skryptu helo_world.sh
mamy już taki komentarz, który jest zarazem informacją dla systemu. Dodajmy jeszcze dwa komentarze.
#!/bin/sh # Przykładowy skrypt wypisujący napis "Hello world" echo "Hello world" # Tutaj wypisujemy co trzeba
Argumenty
Skrypty - podobnie jak dowolne programy - możemy uruchamiać podając im argumenty. Następujące zmienne o specjalnych nazwach pozwalają odczytywać argumenty:
$# |
zwraca liczbę argumentów, |
$0 |
zwraca nazwę pliku bieżącego programu, |
$1 , $2 , ... |
zwraca odpowiednio pierwszy argument, drugi argument, itd., |
$@ |
rozwija się do listy wszystkich argumentów; przydatne jeśli chcemy przekazać wszystkie argumenty innemu programowi. Jeśli chcemy mieć pewność, że każdy argument będzie osobnym słowem, należy użyć cudzysłowów: "$@" ; ma to znaczenie na przykład wtedy, gdy istnieje argument, który zawiera spację.
|
Aby operować na dalszych argumentach pomocne jest polecenie shift
, które usuwa pierwszy argument, a pozostałe przesuwa o jeden w lewo. Aby n-krotnie wywołać polecenie shift
wystarczy podać mu to n jako argument: shift
n.
Na przykład dla skryptu test_arg.sh
o zawartości
#!/bin/sh # Testowanie argumentów echo "Uruchomiłeś program `basename $0`" echo Wszystkie: $@ echo "Pierwsze trzy: '$1', '$2', '$3'" shift 2 echo "shift 2" echo "Wszystkie: $@" echo "Pierwsze trzy: '$1', '$2', '$3'"
mamy efekt
bashtest@host:~$ ./test_arg.sh Raz Dwa "To jest zdanie" Cztery Uruchomiłeś program test_arg.sh Wszystkie: Raz Dwa To jest zdanie Cztery Pierwsze trzy: 'Raz', 'Dwa', 'To jest zdanie' shift 2 Wszystkie: To jest zdanie Cztery Pierwsze trzy: 'To jest zdanie', 'Cztery', '' bashtest@host:~$
Wyrażenia
Jak w każdym liczącym się języku, w bashu możemy wyliczać wartości wyrażeń arytmetycznych. Możemy zrobić to na kilka sposobów.
expr
Najprostszym sposobem jest użycie polecenia expr
. Trzeba przy tym pamiętać, żeby osobne tokeny (tzn. liczby i operatory arytmetyczne) były podawane w osobnych argumentach. Wynika to stąd, że expr
potrafi też operować na łańcuchach znakowych (czym się nie będziemy w tej chwili zajmować), więc musi jakoś te łańcuchy dostawać, a jedyną droga to przez argumenty.
Dostępnych jest pięć operatorów arytmetycznych:
- dodawanie (
+
), - odejmowanie (
-
), - mnożenie (
*
), - dzielenie (
/
), - modulo - reszta z dzielenia (
%
).
Ponadto możemy wykonywać porównania <
, <=
, =
, ==
(synonim =
), !=
, >=
, >
. W wyniku mamy 1, gdy relacja jest spełniona i 0 w przeciwnym przypadku.
Trzeba też pamiętać by znaki specjalne poprzedzać backslashem lub brać w cudzysłowy. Przykłady:
bashtest@host:~$ expr 2\*3 2*3 bashtest@host:~$ expr 2 \* 3 6 bashtest@host:~$ expr '2 * 3' 2 * 3 bashtest@host:~$ expr 2 \* \(7 - 1\) expr: argument nieliczbowy bashtest@host:~$ expr 2 \* \( 7 - 1 \) 12 bashtest@host:~$ a=5 bashtest@host:~$ a=`expr $a + 1` bashtest@host:~$ echo $a 6 bashtest@host:~$ expr 3 \<= 4 1 bashtest@host:~$ expr 3 '<=' 1 0 bashtest@host:~$
$(( ... )) i (( ... ))
Znacznie wygodniejszą formą pisania wyrażeń jest forma $(( wyrażenie ))
. W stosunku do expr
w ciapkach ma prawie same zalety. Jest jeden problem, ta składnia może nie działać w innych shellach, czy w starszych wersjach Basha (ale kto teraz używa czegoś innego niż Bash). Pierwszą zaletą jest szybkość, tzn. użycie tej składni nie powoduje tworzenia nowego procesu (co ma miejsce w przypadku `expr ...`
) i jest interpretowane bezpośrednio przez Basha. Po drugie przy odwoływaniu się do zmiennych nie musimy poprzedzać ich znakiem $
, gdyż każdy identyfikator wewnątrz podwójnych nawiasów jest traktowany jak zmienna. Nie musimy także dbać o używanie odstępów i backslashowania znaków specjalnych. Trzecią zaletą jest bogatsza paleta operatorów arytmetycznych. Otóż wyrażenia arytmetyczne mogą tu zawierać dowolne operatory, które można znaleźć w języku C, np. inkrementacje/dekrementacje zmiennych (ID++
, --ID
), operacje bitowe (<<
, &
, ~
), przypisania arytmetyczne (=
, +=
, *=
), itp. Więcej o znaczeniu tych operacji i dozwolonych działaniach można znaleźć w kursie języka C lub w dokumentacji Basha.
Składni (( wyrażenie ))
używamy wtedy, gdy nie potrzebujemy wyniku, czyli wtedy, gdy wyrażenie nie jest częścią instrukcji, tylko jest sama w sobie instrukcją. Najlepiej będzie, jak przyjrzymy się przykładom.
Kilka sposobów na zwiększenie zmiennej o 1:
bashtest@host:~$ a=0 bashtest@host:~$ a=$((a + 1)) bashtest@host:~$ ((a=a+1)) bashtest@host:~$ ((a++)) bashtest@host:~$ ((a += 1)) bashtest@host:~$ echo $a 4 bashtest@host:~$
Inne przykłady:
bashtest@host:~$ echo "1 + ... + $x = $((x * (x + 1) >> 1))" 1 + ... + 5 = 15 bashtest@host:~$ echo $((x++)) 5 bashtest@host:~$ echo $((++x)) 7 bashtest@host:~$ echo $((x += x > 0)) 8 bashtest@host:~$ echo "x = $x" x = 8 bashtest@host:~$
let
let
jest wbudowanym poleceniem Basha i używamy go, podając mu jako argumenty wyrażenia do przetworzenia.
let wyrażenie1 wyrażenie2 ...
równoważne jest ciągowi poleceń
((wyrażenie1)) ((wyrażenie2)) ...
Przykład:
bashtest@host:~$ x=0 bashtest@host:~$ let x+=2 "x += 4" bashtest@host:~$ echo $x 6 bashtest@host:~$
Trzeba pamiętać, że wyrażenie zawierające odstępy trzeba ujmować w cudzysłowy, aby formowały jeden argument.
Wczytywanie wejścia
W skryptach czasami jest potrzeba wczytania czegoś ze standardowego wejścia. Możemy chcieć pobrać od użytkownika jakąś informację. Możemy też chcieć wczytywać standardowe wejście i stopniowo je przetwarzać. Do tych celów jest polecenie read
.
read
wywołane bez argumentów wczytuje jedną linię ze standardowego wejścia na zmienną o nazwie REPLY
. Jeśli podamy jeden argument, read
wczyta tą linię na zmienną o nazwie takiej samej, jak zawartość argumentu. Jeśli podamy więcej argumentów reprezentujących nazwy zmiennych, read
na pierwsze zmienne będzie wczytywał pojedyncze słowa, a na ostatnią wczyta pozostałość bieżącej linii do jej końca. Prześledźmy to na przykładzie.
Dla skryptu
#!/bin/sh read echo $REPLY read a echo $a read a b c echo "a='$a', b='$b', c='$c'" read x echo "'$x'"
i dla wejścia
Pierwsza linia (pamiętać o cudzysłowach przy odwoływaniu się do $REPLY) Druga linia (teraz pamiętamy - "$a") Raz Dwa Trzy Cztery Czwarta linia jest pusta, a to jest piąta linia
otrzymamy wynik
Pierwsza linia (pamiętać o cudzysłowach przy odwoływaniu się do $REPLY) Druga linia (teraz pamiętamy - "$a") a='Raz', b='Dwa', c='Trzy Cztery'
Podawanie wejścia poleceniu w skrypcie
Gdy wykonujemy polecenie, czasami chcemy zadać mu konkretne wejście. Możemy to zrobić na przykład za pomocą komendy echo
:
echo "Nasze wejście" | polecenie
Użycie echo
dla wejść, które mają składać się z wielu linii jest jednak kłopotliwe. W tym celu w Bashu jest możliwość podania fragmentu skryptu jako wejście do polecenia. Służy do tego symbol specjalny <<
. Takie "przekierowanie" << SŁOWO
mówi, że wejście dla uruchamianego polecenia ma być czytane z aktualnego wejścia tak długo, aż zostanie napotkany napis SŁOWO
. Na przykład wynikiem skryptu
#!/bin/sh echo "Moje ulubione liczby:" sort -n << LICZBY 120 10 2006 314159 0 LICZBY echo "Od najmniejszej do największej, rzecz jasna"
jest
Moje ulubione liczby: 0 10 120 2006 314159 Od najmniejszej do największej, rzecz jasna
Status wyjścia
Każdy program po ukończeniu zwraca swój kod wyjścia. Można go pobrać używając specjalnej zmiennej $?
.
bashtest@host:~$ ls *.txt test.txt bashtest@host:~$ echo $? 0 bashtest@host:~$ ls *.nieznane ls: *.nieznane: Nie ma takiego pliku ani katalogu bashtest@host:~$ echo $? 2 bashtest@host:~$
Zgodnie z konwencją jeśli polecenie wykonało się z sukcesem, kodem wyjścia jest 0, a jeśli w wyniku wykonania pojawiły się błędy lub polecenie skończyło się porażką, zwracany jest kod różny od zera.
Normalnie własny skrypt kończy się ze statusem wyjścia równym zero. Możemy zakończyć skrypt w dowolnym miejscu z wybranym przez nas statusem wyjścia, stosując polecenie exit
. Na przykład instrukcja exit 1
powoduje natychmiastowe zakończenie skryptu z kodem wyjścia 1.
Instrukcje warunkowe
if
Instrukcja if
w najprostszej postaci ma następującą składnię:
if polecenie_warunek; then instrukcje fi
Jej działanie jest następujące. Wykonywane jest polecenie polecenie_warunek
. Jeśli kod wyjścia tego polecenia jest 0, wykonywane są instrukcje między then
, a fi
. Jeśli kod wyjścia polecenia był niezerowy, wykonywanie instrukcji if
jest zakończone i interpreter przechodzi do wykonywania instrukcji znajdujących się po słowie kluczowym fi
.
Widzimy, że rolę warunków logicznych spełniają tu po prostu zwykłe polecenia, a prawda lub fałsz jest to odpowiednio status wyjścia równy zero lub status wyjścia różny od zera.
Składnia if
z użyciem else
:
if polecenie_warunek; then instrukcje1 else instrukcje2 fi
Jeśli warunek jest prawdziwy, wykonywane są instrukcje1
, w przeciwnym razie wykonywane są instrukcje2
. Przykład:
if cd $katalog; then echo "Jesteśmy w katalogu $katalog" else echo "Nie udało się wejść do katalogu $katalog" fi
Pełna składnia if
jest następująca:
if warunek1; then instrukcje1 elif warunek2; then instrukcje2; ... else instrukcje_else; fi
Część z else
jest opcjonalna. instrukcje1
są wykonane, jeśli jest spełniony warunek1
, w przeciwnym razie, jeśli jest spełniony warunek2
, to wykonywane są instrukcje2
, itd. Na końcu, jeśli żaden warunek nie jest spełniony, wykonywane są instrukcje_else
.
Wyrażenia logiczne
Powstaje pytanie, jak tworzyć polecenia, które sprawdzają jakieś sensowne warunki np. porównywanie liczb. Do tego celu służy polecenie test
. Potrafi ono porównywać łańcuchy znakowe, liczby i sprawdzać istnienie plików.
Jeśli chodzi o porównywanie łańcuchów znakowych, mamy następujące możliwości. -z ŁAŃCUCH
sprawdza, czy długość łańcucha jest równa zero, a -n ŁAŃCUCH
, sprawdza, czy długość łańcucha jest różna od zera. Ponadto możemy porównywać dwa łańcuchy np. ŁAŃCUCH1 < ŁAŃCUCH2
. Porównanie jest leksykograficzne. Możliwe operatory to ==
, !=
, <
, >
.
Do porównywania dwóch liczb są inne operatory: -eq
, -ne
, -lt
, -le
, -gt
, -ge
, których odpowiedniki matematyczne to =, <>, <, <=, >, >=.
Na przykład poniższe polecenia zwrócą prawdę (tj. status wyjścia równy 0):
test -z "" test abc \< def test 3 \> 17 test 3 -lt 17
Można także sprawdzać istnienie i typ plików, na przykład:
if test -a $plik; then echo "$plik istnieje" if test -f $plik; then echo "$plik jest zwykłym plikiem" elif test -d $plik; then echo "$plik jest katalogiem" fi fi
Polecenie
test warunek
można też pisać w postaci
[ warunek ]
Taka forma jest po prostu wygodniejsza.
Warto wiedzieć, że instrukcja arytmetyczna (( ... ))
też zwraca status. Zwraca 0, jeśli wartość wyrażenia jest niezerowa, i zwraca 1, jeśli wartość wyrażenia wynosi 0. Pozwala to w Bashu stosować w bardzo wygodny sposób porównania, dokładnie tak samo jak w C.
bashtest@host:~$ if (( 0 )); then echo prawda; else echo fałsz; fi fałsz bashtest@host:~$ if (( 1 )); then echo prawda; else echo fałsz; fi prawda bashtest@host:~$ if (( 3 < 4 )); then echo prawda; else echo fałsz; fi prawda bashtest@host:~$ if (( 0 < -1 )); then echo prawda; else echo fałsz; fi fałsz bashtest@host:~$ if (( 3 * 6 - 2 * 9 )); then echo prawda; else echo fałsz; fi fałsz bashtest@host:~$ if (( 1/0 )); then echo prawda; else echo fałsz; fi bash: ((: 1/0 : division by 0 (error token is " ") fałsz bashtest@host:~$
Najprostszymi poleceniami, które zwracają prawdę i fałsz, prostszymi nawet niż (( 1 ))
i (( 0 ))
są true
i false
, co przydaje się na przykład w pętlach.