Dydaktyka:
FeedbackTo jest stara wersja strony!
Powłoka uruchomiona bez argumentów działa w trybie interaktywnym.
Powłoka uruchomiona z nazwą pliku jako argumentem działa w trybie nieinteraktywnym.
W trybie interaktywnym powłoka czyta polecenia ze standardowego wejścia, w trybie
nieinteraktywnym z podanego pliku.
W trybie nieinteraktywnym powłoka nie wypisuje na ekran znaku zachęty i niektórych
komunikatów diagnostycznych.
Zadanie 1 Umieść w pliku plik
w osobnych liniach polecenie
echo hello world
oraz polecenie date
. Wykonaj sh plik
.
Zadanie 2 Umieść w pliku plik
polecenie readlink /proc/$$/exe
.
Ustaw uprawnienia wykonywania dla tego pliku (np. chmod +x plik
).
Wykonaj ten plik przez ./plik
. Potem uruchom inną powłokę (np.
csh
lub zsh
) i znów wykonaj ./plik
.
Jaka powłoka wykonuje domyślnie skrypty?
Jeżeli pierwsza linia pliku plik
zaczyna się od #!prog
,
to uruchamiając plik arg...
zostanie wykonane
prog plik arg...
.
Taka pierwsza linia ma nazwę shebang
(i jest respektowana zarówno przez powłoki, jak i np. jądro Linuksa).
Shebang musi zaczynać się od pełnej ścieżki do pliku wykonywalnego i powinien móc
zawierać opcje.
Jeśli plik plik
zaczyna się od #!prog a1...
,
to wykonanie plik a2...
uruchomi prog a1... plik a2...
.
Zadanie 3 Wpisz do pliku plik
kolejno:
• #!/bin/ls -la
• #!/usr/bin/bat
• #!/usr/bin/env cowsay
• #!/bin/rm
Nadaj uprawnienia wykonania dla plik
u i wykonaj go (z każdym kolejnym
shebangiem).
Skrypty powłoki zawierają zwykle shebang #!/bin/sh
(lub #!/bin/bash
,
jeśli skrypt korzysta z rozszerzeń Basha) i tradycyjnie nadaje im się rozszerzenie
.sh
.
W wielu językach skryptowych tradycyjnie używa się shebanga (np. python, perl,
ruby).
Zadanie 4 Napisz skrypt hello_world.sh
który po uruchomieniu wypisze
na ekranie hello world
.
Komentarze zaczynają się od #
(znajdującego się poza cudzysłowami,
niepoprzedzonego odwróconym ukośnikiem, etc.).
# to jest komentarz echo to nie jest komentarz # a to już jest echo to \# "też # nie jest" ${0#bash} komentarz
# to jest komentarz echo to nie jest komentarz # a to już jest echo to \# "też # nie jest" ${0#bash} komentarz
Parametry
to (omawiane wcześniej) zmienne oraz parametry pozycyjne i specjalne.
Parametry pozycyjne i specjalne można rozumieć jako specjalne zmienne które
można tylko odczytywać i których nazwy zaczynają się od czegoś innego niż
litera bądź podkreślnik.
Parametry pozycyjne to argumenty wykonania skryptu lub funkcji.
Do pierwszych dziewięciu można odnosić się przez:
$0
, $1
, $2
, ..., $9
kolejne muszą używać notacji z nawiasami wąsatymi:
${0}
, ${1}
, ${2}
, …, ${9}
, ${10}
, ${11}
, ${12}
, …, ${42}
, …
user@host ~ $ cat script.sh #!/bin/sh function funkcja(){ echo $1 $3 $2 } echo $1 $3 $2 funkcja sierotka ma rysia user@host ~ $ ./script.sh ala ma kota ala kota ma sierotka rysia ma user@host ~ $
Komendą shift
można usunąć pierwszy parametr i zmniejszyć pozostałym numery o jeden.
Zadanie 5 Napisz skrypt który wyświetli drugi i czwarty argument.
Odnoszące się do parametry pozycyjnych:
$# | ilość parametrów pozycyjnych |
$* $@ | parametry pozycyjne poddane dzieleniu na tokeny |
"$*" | parametry pozycyjne sklejone w jeden token |
"$@" | parametry pozycyjne, każdy osobnym tokenem |
user@host ~ $ bash -s "the_first_argument" "the second argument" user@host ~ $ echo $# 2 user@host ~ $ ls $* ls: nie ma dostępu do 'the_first_argument': Nie ma takiego pliku ani katalogu ls: nie ma dostępu do 'the': Nie ma takiego pliku ani katalogu ls: nie ma dostępu do 'second': Nie ma takiego pliku ani katalogu ls: nie ma dostępu do 'argument': Nie ma takiego pliku ani katalogu user@host ~ $ ls $@ ls: nie ma dostępu do 'the_first_argument': Nie ma takiego pliku ani katalogu ls: nie ma dostępu do 'the': Nie ma takiego pliku ani katalogu ls: nie ma dostępu do 'second': Nie ma takiego pliku ani katalogu ls: nie ma dostępu do 'argument': Nie ma takiego pliku ani katalogu user@host ~ $ ls "$*" ls: nie ma dostępu do 'the_first_argument the second argument': Nie ma takiego pliku ani katalogu user@host ~ $ ls "$@" ls: nie ma dostępu do 'the_first_argument': Nie ma takiego pliku ani katalogu ls: nie ma dostępu do 'the second argument': Nie ma takiego pliku ani katalogu
Pozostałe:
$0 | w skrypcie nazwa uruchamianego skryptu; w innym razie nazwa powłoki |
$$ | identyfikator procesu powłoki |
$? | kod wyjścia ostatnio uruchomionej komendy |
$! | identyfikator ostatnio przeniesionego w tło procesu |
$- | ustawione opcje powłoki (patrz: man set ) |
user@host ~ $ sleep 1m & [1] 21738 user@host ~ $ sleep 1s user@host ~ $ echo $! 21738 user@host ~ $ echo $? 0 user@host ~ $ false user@host ~ $ echo $? 1 user@host ~ $ python <<< 'exit(123)' user@host ~ $ echo $? 123 user@host ~ $ echo $$ 21731 user@host ~ $ ps PID TTY TIME CMD 21731 pts/9 00:00:00 bash 21738 pts/9 00:00:00 sleep 21742 pts/9 00:00:00 ps
Zadanie 6
Napisz skrypt który wyświetli w kolejnych liniach:
• wartość parametru $0
,
• swój PID,
• ilość argumentów,
• listę argumentów.
Zadanie 7 Uruchom skrypt z poprzedniego zadania podając w różny sposób ścieżkę do niego.
Zadanie 8
Wykorzystując komendę readlink -f
lub realpath
(które dostając jako
argument ścieżkę do pliku zwracają jego bezwzględną ścieżkę po rozwinięciu
symlinków) napisz skrypt który wyświetli ścieżkę do katalogu w którym się
znajduje.
Instrukcja warunkowa ma składnię:
if polecenie1 then kod wykonany jeśli polecenie1 zwróciło prawdę (wartość 0) elif polecenie2 then kod wykonany jeśli polecenie2 zwróciło prawdę else kod wykonany jeśli polecenie1 i polecenie2 nie zwróciło prawdy fiInstrukcje
if
i elif
sprawdzają wartość zwracaną przez polecenie
(a nie tekst wypisywany na standardowe wyjście czy wartość zmiennej).
elif
i else
są opcjonalne, elif
może pojawiać się
wielokrotnie.
Przykład bez używania znaków nowej linii:
if grep -q ^user: /etc/passwd; then echo "'user' present"; elif grep -q ^student: /etc/passwd; then echo "'user' absent, 'student' present"; else echo "neither present"; fi
Zadanie 9
Napisz skrypt który spróbuje usunąć plik o nazwie plik
i jeśli usunięcie
pliku się powiedzie, to wyświetli napis udało się
, a jeśli nie to wyświetli
nie udało się
.
Zadanie 10
Zmień skrypt tak, by zamiast pliku o nazwie plik
usuwał plik podany jako
argument skryptu.
Polecenie test
pozwala wykonywać m. inn. porównania zmiennych czy
sprawdzać istnienie / rodzaj pliku.
Poza nazwą test
to polecenie można wywołać przez [
.
Jedyna różnica między test
i [
polega na tym, że używając [
trzeba dodać na koniec ]
.
Np. test "ala" != "ala"
jest identyczne z [ "ala" != "ala" ]
.
Wyrażenie | Kiedy prawdziwe |
---|---|
[ "$X" ] [ -n "$X" ] | "$X" ma niezerową długość |
[ -z "$X" ] | "$X" jest puste (ma zerową długość) |
[ "$X" = "$Y" ] | Łańcuchy tekstu "$X" i "$Y" są identyczne |
[ "$X" != "$Y" ] | Łańcuchy tekstu "$X" i "$Y" są różne |
Uwaga, pułapka: wyrażenie będzie działać jeśli [ $X = $Y ]
$X
i $Y
rozwiną się każde do jednego słowa, ale jeśli któreś będzie puste bądź
będzie zawierać spację, to spowoduje błąd składniowy.
Wyrażenie | Kiedy prawdziwe |
---|---|
[ "$X" -eq "$Y" ] | liczba "$X" jest równa "$Y" |
[ "$X" -ne "$Y" ] | liczba "$X" jest różna "$Y" |
[ "$X" -lt "$Y" ] | liczba "$X" jest mniejsza "$Y" |
[ "$X" -le "$Y" ] | liczba "$X" jest mniejsza bądź równa "$Y" |
[ "$X" -gt "$Y" ] | liczba "$X" jest większa "$Y" |
[ "$X" -ge "$Y" ] | liczba "$X" jest większa bądź równa "$Y" |
Wyrażenie | Prawdziwe kiedy plik "$X" istnieje i… |
---|---|
[ -e "$X" ] | (po prostu istnieje) |
[ -s "$X" ] | nie jest pusty |
[ -f "$X" ] [ -d "$X" ] … | jest zwykłym plikiem jest katalogiem … |
[ -r "$X" ] [ -w "$X" ] [ -x "$X" ] | użytkownik może odczytywać plik użytkownik może zapisywać do pliku użytkownik może wykonywać plik |
! arg
neguje arg.
arg1 -a arg2
oznacza arg1 oraz arg2
arg1 -o arg2
oznacza arg1 lub arg2
nawiasy okrągłe ( … )
pozwalają grupować, ale trzeba je escape'ować żeby
bash nie traktował ich jako znaki specjalne.
Przykład:
test \( -z "$X" -o "$X" -le 4 \) -a ! -s "$Y" -a \( "$Z" -le 2 -o "$Z" -ge 12 \)
Zadanie 11
Napisz skrypt który sprawdzi czy został wywołany z dokładnie z dwoma argumentami
i czy pierwszy z nich jest (arytmetycznie) mniejszy od drugiego. Jeśli nie,
wyświetl właściwy komunikat o błędzie i wyjdź ze skryptu (komendą exit
).
Zadanie 12 Napisz skrypt który sprawdzi czy pierwszy argument jest zwykłym plikiem do którego bieżący użytkownik ma prawa zapisu. Jeśli nie, wyświetl odpowiedni komunikat i wyjdź. Jeśli tak, dopisz bieżącą datę do tego pliku.
Zadanie 13
Napisz skrypt który sprawdzi czy pierwsza linia pliku (podanego jako argument)
ma treść #!/bin/sh
i czy plik jest wykonywalny. Jeśli tak jest, wyświetl
nazwa pliku jest wykonywalnym skryptem powłoki
.
Zadanie 14 Zmodyfikuj powyższy skrypt tak, by w razie potrzeby nadawał prawa dostępu i dopisywał shebang do wskazanego pliku.
Bash wprowadza dwie dodatkowe konstrukcji powłoki do testów / porównań:
[[ … ]]
jako konstrukcja działająca jak ulepszone polecenie test
,(( … ))
do arytmetyki.
Wyrażenie arytmetyczne wewnątrz (( … ))
jest wyliczane i jeśli zwróci 0,
to (( … ))
zwraca fałsz, w przeciwnym razie prawdę.
Wewnątrz (( … ))
można używać m. inn. operatorów ==
,!=
, <
, <=
,
>
, >=
i … ? … : …
.
Działanie [[ … ]]
jest dość złożone. Ponad to co umie komenda [
,
wyrażenie w podwójnych kwadratowych nawiasach:
[
dzieli wyniki podstawień na słowa, [[
zostawia je bez zmian, przykład user@host ~ $ X="1 -eq 2" user@host ~ $ [ $X ] && echo T || echo F F # wykona porównanie 1 z 2 user@host ~ $ [ "$X" ] && echo T || echo F T # sprawdzi czy ciąg znaków '1 -eq 2' jest niepusty user@host ~ $ [[ $X ]] && echo T || echo F T # sprawdzi czy ciąg znaków '1 -eq 2' jest niepusty user@host ~ $ [[ "$X" ]] && echo T || echo F T # sprawdzi czy ciąg znaków '1 -eq 2' jest niepusty
[[
nie są wykonywane dopasowania do nazw plików, przykład user@host ~ $ [[ *.txt ]] && echo T || echo F T # sprawdzi czy ciąg znaków '*.txt' jest niepusty user@host ~ $ [ *.txt ] && echo T || echo F -bash: [: nums.txt: oczekiwano operatora jednoargumentowego F # bash podstawił nazwy plików '*.txt', więc [ dostało złe argumenty
=
i !=
dopasowują tekst do wzorca, a nie porównują teksty, przykład user@host ~ $ [ abc.log = *.log ] && echo T || echo F F user@host ~ $ [[ abc.log = *.log ]] && echo T || echo F T user@host ~ $
=~
dopasowujący do wyrażenia regularnego, przykład user@host ~ $ [[ abc.txt =~ ^a.*\.txt$ ]] && echo T || echo F T
-a
i -o
zastąpiono przez &&
i ||
, a nawiasów okrągłych nie trzeba escape'ować, np:if [[ -f file && ( ! -s file || ! -w file ) ]]; then echo "'file' exists and is not empty or not writeable"; fi
Instrukcja wyboru ma składnię:
case wartość in wzorzec1) polecenia1 ;; wzorzec2) polecenia2 ;; *) polecenia3 ;; esacWartość jest kolejno dopasowywana do wzorzec1, wzorzec2, … i przy znalezieniu pierwszego dopasowania wykonywane są odpowiednie polecenia. Dalsze dopasowania nie są brane pod uwagę.
;;
1).
*
.
Przykład bez używania znaków nowej linii:
case $(LANG= date "+%A") in Monday) echo "Ugh...";; S*day) tput bel; echo "Weekend!";; *) echo "a day.";; esac
Zadanie 15
Napisz skrypt który sprawdzi rozszerzenie pliku (podanego jako argument) i jeśli
plik ma rozszerzenie:
• pdf
to wykona pdftotext plik -
,
• zip
to wykona unzip -l plik
,
• jakiekolwiek inne, to wykona cat plik
.
We wszystkich pętlach działają słowa kluczowe break
i continue
do, odpowiednio, wyjścia poza pętlę i rozpoczęcia następnego przebiegu pętli.
Pętla for
ma składnię:
for zmienna in wartość1 wartość2 … do polecenia doneW kolejnych przebiegach pętli kolejne wartości z listy są przypisywane do zmiennej zmienna. Po wyjściu z pętli zmienna ma ostatnią z przypisanych w pętli wartości.
in wartość1 wartość2 …
spowoduje przyjęcie za listę wartości "$@"
.
Często w miejsce listy wartości podaje się podstawienie które jest rozkładane
na wiele wartości, np:
for NUM in {1..3} {7..9}
→ for NUM in 1 2 3 7 8 9
for NUM in $(seq -w 001 3)
→ for NUM in 001 002 003
for USERNAME in $(getent passwd | cut -f1 -d:)
→ for USERNAME in root student ...
for FILE in $(grep -il 'ldap' /etc/*.conf 2>/dev/null)
→ for FILE in /etc/autofs.conf /etc/ldap.conf ...
2)
Przykład bez używania znaków nowej linii:
for X in {1..3}; do echo -n "Przebieg $X: "; date +%N; done
Zadanie 16 Napisz skrypt, który dla każdego pliku podanego jako argument wyświetli: jego nazwę, jego pierwszą linię i jego ostatnią linię.
Zadanie 17
Napisz pętlę która wyświetli wszystkie potęgi dwójki o wykładnikach od 1 do
wybranej wartości.
(Wartość potęgi można wyliczyć np. wyrażeniem
$((2**wykładnik))
.)
Bash wspiera też składnię for
wyglądającą i działającą jak w C:
for ((i=0; i<5; i++)) do polecenia done
Przykład bez używania znaków nowej linii:
for ((i=3;i<=15;i+=3)); do for ((j=0;j<i;j++)); do echo -n '*'; done; echo; done
Pętla ''while'' ma składnię:
while polecenie1 do polecenia2 doneBliźniacza pętla
until
ma składnię:
until polecenie1 do polecenia2 donePętla
while
wykonuje polecenia2
do momentu aż polecenie1
zwraca prawdę. until
wykonuje polecenia2
do momentu aż polecenie1
zwraca fałsz.
Przykład bez używania znaków nowej linii:
X=7; until [ $X -eq 1 ]; do [ $((X%2)) -eq 1 ] && X=$((3*X+1)) || X=$((X/2)); echo $X; done
Zadanie 18 Napisz pętlę która wyświetli wszystkie potęgi dwójki mniejsze niż podana liczba.
Zadanie 19
Napisz skrypt który utworzy nowy plik myProg_NUM.log
o najniższej
możliwej wartości NUM
, a następnie przypisze jego nazwę do zmiennej
LOG
i umieści w tym pliku bieżącą datę.
Komenda
read
[zmienna]...
wczytuje jedną linię ze standardowego wejścia, a następnie:
read
wpisuje linię do zmiennej REPLY,read
dzieli wczytaną linię na słowa,
user@host ~ $ read X Y Z jeden "dwa trzy" user@host ~ $ echo "X='$X' Y='$Y' Z='$Z'" X='jeden' Y='"dwa' Z='trzy"' user@host ~ $ read X Y Z jeden user@host ~ $ echo "X='$X' Y='$Y' Z='$Z'" X='jeden' Y='' Z='' user@host ~ $ read X Y Z jeden dwa trzy cztery pięć user@host ~ $ echo "X='$X' Y='$Y' Z='$Z'" X='jeden' Y='dwa' Z='trzy cztery pięć' user@host ~ $ read jeden dwa trzy cztery pięć user@host ~ $ echo "'$REPLY'" ' jeden dwa trzy cztery pięć'
Komenda read jest często wykorzystywana we wzorcach: |
|
polecenie | while read X do ... done
1. | while read X Y do ... done << EOF wartoscX1 wartoscY1 wartoscX2 wartoscY2 EOF ustawia się po parę/trójkę/… zmiennych naraz |
Do poprawnego działania należy zapewnić żeby nic we wnętrzu pętli nie czytało ze standardowego wejścia |
Zadanie 20
Napisz skrypt który dopisuje do pliku o nazwie log
linię tekstu wczytaną
ze standardowego wejścia poprzedzoną bieżącą datą.
Zadanie 21 Napisz skrypt który wczytuje liczby (znajdujące się w osobnych liniach) do momentu aż nie pojawi się pusta linia i następnie wyświetla najmniejszą z wczytanych liczb.
Pętla select
ma składnię identyczną jak pętla for:
select ZMIENNA in wartość1 wartość2 … do polecenia doneTa pętla:
REPLY
ZMIENNA
przypisuje odpowiadającą wartość…
polecenia
Zadanie 22
Przeanalizuj i przetestuj poniższy kod. Zmień go tak, by dopiero wpisanie q
przerywało pętlę.
select FILE in /etc/logrotate.d/* do [[ $FILE ]] || continue echo "===$FILE===" cat "$FILE" break done