Środowisko programisty/Bash - pisanie skryptów: Różnice pomiędzy wersjami
m →Status wyjścia: Literówka |
|||
(Nie pokazano 10 wersji utworzonych przez jednego użytkownika) | |||
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 | 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 | Przygotujmy plik <code>hello_world.sh</code> o następującej zawartości: | ||
#!/bin/sh | #!/bin/sh | ||
echo "Hello world" | echo "Hello world" | ||
Rozszerzenie | 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. | Spróbujmy uruchomić ten plik. | ||
bashtest@host:~$ | bashtest@host:~$ hello_world.sh | ||
bash: | bash: hello_world.sh: command not found | ||
bashtest@host:~$ | bashtest@host:~$ | ||
Takie polecenie nie zostało znalezione. System szuka danego polecenia wśród wszystkich katalogów zapamiętanych na zmiennej środowiskowej | 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 | bashtest@host:~$ echo $PATH | ||
/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games | /usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games | ||
bashtest@host:~$ | 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. | 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 | bashtest@host:~$ ./hello_world.sh | ||
bash: ./hello_world.sh: Brak dostępu | bash: ./hello_world.sh: Brak dostępu | ||
Linia 61: | Linia 61: | ||
-rw-r--r-- 1 bashtest users 29 2006-08-07 15:45 hello_world.sh | -rw-r--r-- 1 bashtest users 29 2006-08-07 15:45 hello_world.sh | ||
bashtest@host:~$ | bashtest@host:~$ | ||
Ten plik nie ma ustawionych praw do uruchamiania. Możemy to zrobić używając polecenia | 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:~$ chmod u+x hello_world.sh | ||
bashtest@host:~$ ls -l hello_world.sh | bashtest@host:~$ ls -l hello_world.sh | ||
Linia 74: | Linia 74: | ||
== Komentarze == | == Komentarze == | ||
Komentarze zaczynają się od symbolu #. Wszystkie pozostałe znaki aż do końca linii są ignorowane. W pierwszej linii skryptu | 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 | #!/bin/sh | ||
Linia 85: | Linia 85: | ||
Skrypty - podobnie jak dowolne programy - możemy uruchamiać podając im argumenty. Następujące zmienne o specjalnych nazwach pozwalają odczytywać 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 | 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 | Na przykład dla skryptu <code>test_arg.sh</code> o zawartości | ||
#!/bin/sh | #!/bin/sh | ||
Linia 123: | Linia 123: | ||
=== expr === | === expr === | ||
Najprostszym sposobem jest użycie polecenia | 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: | Dostępnych jest pięć operatorów arytmetycznych: | ||
* dodawanie (+), | * dodawanie (<code>+</code>), | ||
* odejmowanie (-), | * odejmowanie (<code>-</code>), | ||
* mnożenie (*), | * mnożenie (<code>*</code>), | ||
* dzielenie (/), | * dzielenie (<code>/</code>), | ||
* modulo - reszta z dzielenia (%). | * modulo - reszta z dzielenia (<code>%</code>). | ||
Ponadto możemy wykonywać porównania <, <=, =, == (synonim =), !=, >=, >. W wyniku mamy 1, gdy relacja jest spełniona i 0 w przeciwnym przypadku. | 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: | Trzeba też pamiętać by znaki specjalne poprzedzać backslashem lub brać w cudzysłowy. Przykłady: | ||
Linia 156: | Linia 156: | ||
=== $(( ... )) i (( ... )) === | === $(( ... )) i (( ... )) === | ||
Znacznie wygodniejszą formą pisania wyrażeń jest forma | 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 | 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: | Kilka sposobów na zwiększenie zmiennej o 1: | ||
Linia 184: | Linia 184: | ||
=== let === | === let === | ||
let jest wbudowanym poleceniem Basha i używamy go, podając mu jako argumenty wyrażenia do przetworzenia. | <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 ... | let wyrażenie1 wyrażenie2 ... | ||
równoważne jest ciągowi poleceń | równoważne jest ciągowi poleceń | ||
Linia 200: | Linia 200: | ||
== Wczytywanie wejścia == | == 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 | 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>. | ||
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 | <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 | Dla skryptu | ||
Linia 229: | Linia 229: | ||
== Podawanie wejścia poleceniu w skrypcie == | == 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: | 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 | 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. | 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 | #!/bin/sh | ||
echo "Moje ulubione liczby:" | echo "Moje ulubione liczby:" | ||
Linia 253: | Linia 253: | ||
== 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 $?. | 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 | bashtest@host:~$ ls *.txt | ||
test.txt | test.txt | ||
Linia 265: | Linia 265: | ||
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. | 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 | 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 == | ||
Linia 271: | Linia 271: | ||
=== if === | === if === | ||
Instrukcja if w | Instrukcja <code>if</code> w najprostszej postaci ma następującą składnię: | ||
if polecenie_warunek; then | if polecenie_warunek; then | ||
instrukcje | instrukcje | ||
fi | fi | ||
Jej działanie jest następujące. Wykonywane jest polecenie | 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. | 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: | Składnia <code>if</code> z użyciem <code>else</code>: | ||
if polecenie_warunek; then | if polecenie_warunek; then | ||
instrukcje1 | instrukcje1 | ||
Linia 285: | Linia 285: | ||
instrukcje2 | instrukcje2 | ||
fi | fi | ||
Jeśli warunek jest prawdziwy, wykonywane są | Jeśli warunek jest prawdziwy, wykonywane są <code>instrukcje1</code>, w przeciwnym razie wykonywane są <code>instrukcje2</code>. Przykład: | ||
if cd $katalog; then | if cd $katalog; then | ||
echo "Jesteśmy w katalogu $katalog" | echo "Jesteśmy w katalogu $katalog" | ||
Linia 292: | Linia 292: | ||
fi | fi | ||
Pełna składnia if | Pełna składnia <code>if</code> jest następująca: | ||
if warunek1; then | if warunek1; then | ||
instrukcje1 | instrukcje1 | ||
Linia 301: | Linia 301: | ||
instrukcje_else; | instrukcje_else; | ||
fi | fi | ||
Część z else jest opcjonalna. | 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 test. Potrafi ono porównywać łańcuchy znakowe, liczby i sprawdzać istnienie plików. | 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. | 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: -eq, -ne, -lt, -le, -gt, -ge, których odpowiedniki matematyczne to =, <>, <, <=, >, >=. | 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 | Na przykład poniższe polecenia zwrócą prawdę (tj. status wyjścia równy 0): | ||
test -z "" | test -z "" | ||
test abc \< def | test abc \< def | ||
Linia 333: | Linia 333: | ||
Taka forma jest po prostu wygodniejsza. | Taka forma jest po prostu wygodniejsza. | ||
Warto wiedzieć, że instrukcja arytmetyczna | 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 | bashtest@host:~$ if (( 0 )); then echo prawda; else echo fałsz; fi | ||
fałsz | fałsz | ||
Linia 349: | Linia 349: | ||
bashtest@host:~$ | 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.