sh
— (Bourne shell) szeroko dostępny interpreter
bash
— (Bourne-again shell) rozszerzona wersja sh
fish
— (Friendly Interactive shell) interpreter z ułatwieniami dla użytkowników i programistów
csh
and tcsh
— (C shell) interpreter luźno oparty na składni języka C
zsh
— (Z shell) rozszerzona wersja bash
-a
Lista komend zapisana w dowolnym pliku tekstowym może być wykonana za pomocą intepretera:
sh PLIK
bash PLIK
Pliki wewnętrznie oznacza się jako skrypt podając w pierwszej lini pliku
dyrektywę interpretera która informuje system operacyjny za pomocą jakiego
polecenia je otworzyć. Dyrektywa to pełna ścieżka programu do wykonania
poprzedzona znakiem komentarza i wykrzyknikiem #!
(tzw. shebang):
#!/bin/sh
#!/bin/bash
Można w ten sposób oznaczyć dowolny plik tekstowy do otwarcia za pomocą dowolnego programu:
#!/usr/bin/env python
#!/usr/bin/firefox
Plik tekstowy oznaczony dyrektywą można uruchomić:
./SKRYPT.sh – w katalogu bieżącym
/home/user/SKRYPT.sh – pełna ścieżka (z dowolnego katalogu)
Żeby uruchomić plik należy mu nadać odpowiednie prawa (chmod
).
Skrypt może zostać uruchomiony z argumentami, np.:
./SKRYPT.sh a b c d
Argumenty są dostępne przez parametry pozycyjne (positional parameters):
$1 $2 $3 $4 ... ${10} ${11} ...
Polecenie shift n
przesuwa zawartość zmiennych o n
w lewo (domyślnie
n
= 1).
Przykłady:
shift # 1=$2, 2=$3, 3=$4 ...
shift 2 # 1=$3, 2=$4, 3=$5 ...
$0
– ścieżka do skryptu (nazwa wykonywanego skryptu)
$#
– liczba argumentów
$*
– wszystkie parametry pozycyjne jako jeden napis ("$1 $2 ..."
$@
– wszystkie parametry pozycyjne jako osobne napisy ("$1" "$2" ...
)
$$
– PID procesu skryptu
Komenatrze zaczynają się od znaku #
i kończą się wraz z końcem bieżącej
lini.
# Prawo Ohma
I=`expr $V / $R`
#echo "I=$V/$R -> $I"
# TODO Dodać prawo Kirchhoffa
Jakie zastosowania mają komentarze?
Sposoby wykonania dwóch komend:
# wykonanie sekwencyjne
COMMAND1
COMMAND2
# wykonanie sekwencyjne
COMMAND1; COMMAND2
# wykonanie warunkowe
COMMAND1 && COMMAND2
# wykonanie warunkowe
COMMAND1 || COMMAND2
# wykonanie współbieżne
COMMAND1 & COMMAND2
# potok
COMMAND1 | COMMAND2
if COMMAND0
then
INSTRUCTIONS
elif COMMAND1 # Opcjonalne
INSTRUCTIONS
else # Opcjonalne
INSTRUCTIONS
fi
Wykonywane instrukcje w zależności od stanu wykonania wskazanej komendy
COMMAND0
.
Przydatne polecenia:
false
– nie robi niczego i kończy się stanem 1
true
– nie robi niczego i kończy się stanem 0
test
– sprawdza wskazany warunek
[ ]
– j/w tylko krócej
[[ ]]
– j/w tylko bezpieczniej zalecane, patrz niżej
(( ))
– sprawdza warunek arytmetyczny zalecane, patrz niżej
$(( ))
– wykonuje działanie arytmetyczne
Porównywanie wartości zmiennych
Uwaga: Bash operuje na ciągach znaków, więc nawet jak do zmiennej mamy przypisana liczbę, traktowana ona jest jako ciąg znaków. Dla bezpieczeństwa warto umieszczać zmienne w cudzysłowy (bo inaczej będzie problem jak ciąg znaków umieszczony w zmiennej będzie zawierać spacje).
x="abc"
y=42
if [ "$x" == "abc" ]; then
echo true # true
else
echo false
fi
if [ "$x" > "aaa" ]; then
echo true # true
else
echo false
fi
if [ ! "$y" == 100 ]; then
echo true # true
else
echo false
fi
if [ "$y" != 100 ]; then
echo true # true
else
echo false
fi
Porównywanie wartości liczbowych:
-lt
– lower-than
-le
– lower-equal
-eq
– equal
-ge
– greater-equal
-gt
– greater-than
y=42
if [ $y -lt 100 ]; then
echo true # true
else
echo false
fi
Łączenie warunków odbywa się przy pomocy operatora -o
(or) oraz operatora
-a
(and).
x="abc"
if [ $x == "abc" -o $x == "def" ]; then
echo true # true
else
echo false
fi
if [ $x == "abc" -a $x == "def" ]; then
echo true
else
echo false # false
fi
W warunkach można sprawdzać różne rzeczy związane z plikami, np.:
# [ -a FILE ]
– prawda jeśli FILE istnieje
# [ -d FILE ]
– prawda jeśli FILE istnieje i jest katalogiem
# [ -r FILE ]
– prawda jeśli FILE istnieje i można go czytać
# [ -w FILE ]
– prawda jeśli FILE istnieje i można do niego pisać
# [ -x FILE ]
– prawda jeśli FILE istnieje i jest wykonywalny
# [ FILE1 -nt FILE2 ]
– prawda jeśli FILE1 był zmieniony później niż FILE2, lub jeśli FILE1 istnieje a FILE2 nie
# [ FILE1 -ot FILE2 ]
– prawda jeśli FILE1 jest starszy niż FILE2, lub jeśli FILE2 istnieje a FILE1 nie
filename="file.txt" # 'file.txt' jest w bieżącym katalogu
if [ -a "$filename" ]; then
echo true # true
else
echo false
fi
W ogólności bezpieczniej używać następującej składni z podwójnymi [[ ]]
.
Wtedy np. nie trzeba umieszczać zmiennych w cudzysłowie (nawet jak wartość
zmiennej to ciąg znaków ze spacjami) oraz można używać operatorów
logicznych ||
i &&
jak w C.
x="abc"
if [[ $x == "abc" || $x == "def" ]]; then
echo true # true
else
echo false
fi
if [[ $x == "abc" && $x == "def" ]]; then
echo true
else
echo false # false
fi
filename="file.txt"
if [[ -a "$filename" ]]; then # if 'file.txt' exists
echo true # true
else
echo false
fi
Do wykonywania porównań arytmetycznych wygodniej użyć składni (( ))
(porównaj $(( ... ))
).
y=42
if (( $y < 100 + 13 )); then
echo true # true
else
echo false
fi
Szczegóły: link
case VALUE in
PATTERN0) INSTRUCTIONS;; # lub ;&
PATTERN1) INSTRUCTIONS;; # lub ;&
PATTERN2) INSTRUCTIONS;; # lub ;&
PATTERN3) INSTRUCTIONS;; # lub ;&
esac
Możliwa dowolna liczba warunków i dowolny blok instrukcji po każdym z warunków.
Przy zastosowaniu ;;
po przypasowaniu do warunku i wykonaniu
instrukcji wychodzi z konstrukcji case
. Przy zastosowaniu ;&
wykonuje instrukcje także kolejnego warunku.
Przykłady warunków:
napis
– traktowany dosłownie
*
– dowolny ciąg znaków
[A-Z]
– klasa znaków
?
– dowolny znak
image_199[0-9]\*.jp?g
– złożony warunek
while COMMAND
do
INSTRUCTIONS
done
Wykonywane instrukcje do czasu az stan wykonania komendy COMMAND
będzie
inny od 0
.
Przydatne instrukcje (dla każdego rodzaju pętli):
break
– przerywa działanie pętli
continue
– przerywa wykonywanie instrukcji w ciele pętli i przechodzi do następnej iteracji
for VARIABLE_NAME in ARGUMENT_LIST
do
INSTRUCTIONS
done
Wykonuje instrukcje dla każdego z argumentów z ARGUMENT_LIST
.
Każdy z argumentów jest przypisywany kolejno do zmiennej VARIABLE_NAME
.
Alternatywna uproszczona składnia:
for VARIABLE_NAME
do
INSTRUCTIONS
done
Wykounje instrukcje dla każdego z argumentów (parametrów pozycyjnych) skryptu.
exit
Zakończ wykonywanie skryptu. Jako argument przyjmuje stan zakończenia. Domyślnie status 0.
read
Wczytaj do zmiennej. (BASH shell bultin)
znak (napis) zachęty
czas wygaśnięcia w sekundach
nie wyświetlaj znaków wpisanych przez użytkownika
# Najprostszy przypadek
read i
echo $i
# Hasło wpisywane bez echa, max. przez 30 sekund
read -p "Password: " -s -t 30 password
echo $password
function NAME () { # Opcjonalne "function" lub "()"
INSTRUCTIONS
} REDIRECTION # Opcjonalne "REDIRECTION"
Przykłady:
# Przekierowuje echo na STDERR
function echo_err {
echo $@
} >&2
echo_err "Ala ma kota"
# Przeciąża polecenie echod
function echo {
if $DEBUG
then
echo $@
fi
}
# Sprawdza warunek i ustawia stan wyjścia
function fsm_spaghetti_day {
if [ `date +%A` == 'Friday' ]
then
exit 0
fi
exit 1
}
Wywołanie funkcji i przechwycenie jej outputu do zmiennej odbywa się tak jak wywołanie podprogramu. Do zmiennej trafi wszystko co funkcja wypisuje na stdout.
function foo() {
echo "Start FOO"
echo "Liczba wszystkich argumentów: $#"
for i in $@; do
echo "- $i"
done
echo "KONIEC foo"
}
foo_output=$(foo arg1 arg2 arg3)
echo "$foo_output"
printf "%s\n" "$foo_output"
# zarówno echo jak i printf wypiszą na ekran:
#
# Start FOO
# Liczba wszystkich argumentów: 3
# - arg1
# - arg2
# - arg3
# KONIEC foo
Tablice w bashu indeksowane są od 0.
Przypisanie wartości do elementu tablicy (tablica jest tworzona, gdy nie była zadeklarowana wcześniej):
myarray[3]="abc"
myarray[4]=42
Odwołanie do elementu tablicy:
${myarray[3]}
${myarray[$i]}
– gdzie do zmiennej i
jest przypisana wartość np. 4
Wszystkie elementy tablicy:
${myarray[@]}
– podobnie do $@
(wszystkie parametry pozycyjne jako osobne napisy ("$1" "$2" ...
))
Rozmiar tablicy:
${#myarray[@]}
– podobnie do $#
(liczba argumentów skryptu/funkcji)
Iterowanie po elemntach tablicy:
myarray[3]="abc"
myarray[4]=42
for i in ${myarray[@]}; do
echo $i
done
for i in $(seq 0 ((${#myarray[@]} - 1)) ); do
echo $i
done
Rozbijanie ciągów znaków na tablice:
str="Ala ma... kota"
echo $str # Ala ma... kota
list=($str)
echo ${list[@]} # Ala ma... kota
echo ${list[0]} # Ala
echo ${list[1]} # ma...
echo ${list[2]} # kota
Rozbijanie ciągów znaków na tablice przy pomocy innego separatora niż spacja:
str="Ala_ma..._____kota"
echo "$str" # Ala_ma..._____kota
OLD_IFS=$IFS ## zapisanie domyslnego separatora
IFS="_"
list=($str)
echo "${list[@]}" # Ala ma... kota
echo ${list[0]} # Ala
echo ${list[1]} # ma...
echo ${list[2]} #
echo ${list[3]} #
echo ${list[4]} #
echo ${list[5]} #
echo ${list[6]} # kota
IFS=$OLD_IFS ## przywrocenie domyslnego separatora
Szczegóły: link
Jako pierwszy parametr podajemy nazwe formatu docelowego,
./chcase joined my new project # Zwraca: mynewproject
./chcase underline my new var # Zwraca: my_new_project
./chcase uppercase my new const # Zwraca: MY_NEW_CONST
./chcase dashes my new resource # Zwraca: my-new-resources
Jeśli format nie zostanie podany wyświetl dostępne formaty i zakończ z błędem. Jeśli użytkownik nie poda argumentów, wyświetl krótką instrukcję i zakończ z błędem.
parametr należy podać miejsce utworzenia kopii. Jako kolejne podajemy nazwy kopiowanych plików i katalogów. Do nazw kopiowanych zasobów dodana powinna być też data i czas utworzenia kopii.
./bup /tmp/backup asia/ basia/ casia.txt
/tmp/backup/10-05-12_10:03_asia/
/tmp/backup/10-05-12_10:03_asia/cokolwiek_było_w_katalogu
/tmp/backup/10-05-12_10:03_basia/
/tmp/backup/10-05-12_10:03_basia/cokolwiek_było_w_katalogu
/tmp/backup/10-05-12_10:03_casia.txt
Jeśli wskazany katalog docelowy nie istnieje to należy go utworzyć. Jeśli wskazany katalog docelowy istnieje ale nie jest katlogiem, należy zakończyć działanie z błędem.
Jeśli plik do kopiowania nie istnieje, należy go pominąć i na samym końcu działania programu wypisać ostrzeżenie, że plik nie został odnaleziony.
Skrypt który rozmawia z użytkownikiem. Użytkownik zadaje pytanie,
skrypt losuje odpowiedź z pliku. Kiedy użytkownik napiszę kluczową frazę
(np. that's enough
) skrypt kończy rozmowę.
Skrypt który czeka, aż zawartość określonej strony się zmieni. Jeśli zawartość się zmieniała, to skrypt wykonuje wskazaną komendę. Użytkownik może (ale nie musi) określić jak często skrypt ma sprawdzać kopie lokalną.
Przykład użycia:
# Przykładowa strona (zapisana do zmiennej dla wygody)
WEBSITE=http://www.cs.put.poznan.pl/ksiek/SOP/resources/embrace_change.php
# Sprawdza czy strona się zmieniła z domyślną częstotliwością.
# Jeśli strona się zmieni to otwiera ją w przeglądarce internetowej.
./diditchange "$WEBSITE" firefox "$WEBSITE"
# Sprawdza czy strona się zmieniła raz na 5 sekund.
# Jeśli strona się zmieni to otwiera ją w przeglądarce internetowej.
./diditchange "$WEBSITE" -t 5 firefox "$WEBSITE"
Jeśli nie można połączyć się ze stroną skrypt powinien wyświetlić błąd i zakończyć działanie.
Przydatne polecenia: wget
, sleep
.
Skrypt-predykat który sprawdza porę dnia. Możliwe pory dnia: early, late, day, night, morning, lunchtime, evening
Przykłady użycia:
if ./its late
then
...
fi
while ./its lunchtime
do
...
done
Jeśli użytkownik poda porę dnia nieznaną skryptowi należy wyświetlić komunikat o błędzie.
Skrypt który dodaje dyrektywę interpretera dla skryptów znalezionych we wskazanych katalogach. Do skryptów o rozszerzeniu .sh dodawane jest #!/bin/bash, do skryptow o rozszerzeniu .py ‘#!/usr/bin/env python’. Inne rodzaje skryptów są pomijane.
Przykład użycia: ./addshebang repository/scripts "/tmp/work in progress"
Jeśli plik już posiada dyrektywę interpretera, wypisz wiadomość do użytkownika i zignoruj plik.