Kurs BASH


Kurs BASH
Bash to powloka systemowa UNIX napisana dla projektu GNU. Nazwa jest akronimem od Bourne-Again
Shell (ang. znowu shell Bourne'a). Jest to jedna z najpopularniejszych powlok systemów uniksowych.
Wywodzi sie od powloki Bourne'a sh, która byla jedna z pierwszych i najwazniejszych powlok systemu
UNIX, i zawiera pomysly zawarte w powlokach Korna i csh. Pierwotna powloka Bourne'a byla tworzona
przez Stephena Bourne'a. Bash byl pisany glównie przez Briana Foxa i Cheta Rameya. Powloka Bournea sh
zostala stworzona okolo roku 1977. Powloka bash zostala napisana w roku 1987 przez Briana Foxa. W roku
1990 jej glównym opiekunem zostal Chet Ramey. Bash jest domyslna powloka w wiekszosci dystrybucji
systemu Linux oraz w systemie Mac OS X w wersji 10.4 Tiger. Istnieja takze wersje dla wiekszosci systemów
Unixowych. Bash jest takze domyslna powloka w srodowisku Cygwin dla systemów Win32. Program jest
rozprowadzany na licencji GPL. Bash jest bardzo dobrym jezykiem skryptowym, sluzacym do efektywnego
zarzadzania systemem.
Skrypt to nic innego jak zwykly jawny nie skompilowany plik tekstowy zawierajacy rózne instrukcje,
polecenia systemowe, do wykonania przez zdefiniowany w skrypcie interpreter powloki systemowej
(/bin/bash). Zadaniem powloki jest przetlumaczenie ich na polecenia systemu. Jesli ktos mial do czynienia z
plikami wsadowymi (Batch) w systemie DOS to wlasnie skrypty sa tego typu plikami.
Kurs BASH
Hello World i wykonanie skryptu
Tradycyjnie pierwszym krokiem do pisania wlasnego kodu jest wyswietlenie na standardowym wyjsciu
(stdout), tutaj ekran monitora, oslawionego tekstu "Hello World".
Zakladajac ze pracujemy np. w Linuksowej konsoli, w której mamy za powloke obrana wlasnie powloke bash
wystarczy wydac komende:
echo "Hello World"
Na wyjsciu dostaniemy w odpowiedzi napis Hello World. Dlaczego tak sie dzieje? Otóz pracujac w powloce
bash od razu jestesmy w srodowisku w którym wykonujemy kod - powloka interpretuje bezposrednio
polecenia dla niej zrozumiale i natychmiast podaje nam wynik dzialania instrukcji na nasz ekran. A teraz do
rzeczy. Napiszmy prosty skrypt który wlasnie wydrukuje nam na nasz ekran monitora nasze Hello World.
Kazdy skrypt powinien zawierac na poczatku zdefiniowany rodzaj shella w jakim skrypt ma byc wykonany:
#!/bin/bash lub krócej #!/bin/sh
tutaj skrypt zawsze bedzie wykonywany przez interpreter polecen bash, niezaleznie od tego jakiej powloki w
danej chwili uzywamy. Przykladowo pracujemy w powloce csh, skrypt na czas wykonania zawartego w nim
kodu wywola powloke bash:
tworzymy plik tekstowy o nazwie hello.sh
touch hello.sh
nastepnie edytujemy utworzony plik dowolnym edytorem np. pico, vi, vim, joe:
pico hello.sh
#wypelniamy nasz pusty plik ponizszym kodem:
#!/bin/bash
#Nastepnie piszemy kod:
echo "Hello World"
#koniec skryptu
Uwaga:
"#" oznacza komentarz w skrypcie. Interpreter kodu pomija wszystko zawarte za # do konca linii, mozemy w
ten sposób zamieszczac w skrypcie swoje komentarze.
Zapisujemy zmiany i oto mamy swój pierwszy skrypt, który nalezy uruchomic. Jest kilka sposobów na
uruchamianie skryptów. Wiele poradników nakazuje nadac plikowi prawo wykonania aby go uruchomic -
owszem jest to poprawne jednak nie niezbedne, skrypty mozemy uruchamiac na kilka sposobów, najprostszy
sposób uruchomienia skryptu to wydanie komendy sh (dla bash): sh skrypt.sh - taki sposób uruchamiania
skryptu pozbawia nas potrzeby umieszczania na poczatku kodu definicji powloki #!/bin/bash, poniewaz sh
oznacza: wykonaj skrypt w powloce bash. Jezeli nie podamy w skrypcie definicji powloki, ani tez nie
wykonamy skryptu poleceniem sh, skrypt nasz zostanie wykonany w domyslnej powloce w jakiej wlasnie jest
wywolywany, w linuksie zazwyczaj domyslna powloka jest bash.
W naszym przykladzie zakladajac ze jestesmy w tym samym katalogu co nasz skrypt wydajemy polecenie:
sh hello.sh
w przeciwnym wypadku musimy podac pelna sciezke dostepu do skryptu:
sh /home/user/skrypty/hello.sh
co nam wyswietli na wyjsciu, na monitorze tresc:
Hello World
Inny sposób wykonania skryptu, to nadanie mu prawa, atrybutu do wykonania czyli:
chmod + x hello.sh
nastepnie aby wykonac skrypt wystarczy wydac polecenie:
./hello.sh
#lub
. /home/user/skrypty/hello.sh
Kolejnym sposobem uruchamiania skryptów jest nadanie im praw do wykonania i umieszczenia sciezki do
skryptu w zmiennej PATCH, lub np. skopiowanie skrytu do katalogu /usr/sbin, wtedy wykonujemy nasz
skrypt tak jak inne programy zawarte w naszym systemie, wydajac po prostu polecenie:
hello.sh
Nazwa naszego pliku hello.sh posiada rozszerzenie .sh które nie jest konieczne, jednak dobrym nawykiem jest
nadawanie skryptom rozszerzen w celu latwiejszego ich identyfikowania przez nas samych jak i przez
róznego rodzaju edytory podswietlajace skladnie kodu, np. edytor zawarty w midnight commanderze
rozpoznaje rodzaj kodu po rozszerzeniu pliku i w sposób wygodny dla nas podswietla skladnie kodu podczas
edycji skryptu.
" Ciekawostka:
Ogólnie #!/bin/powloka wcale nie musi byc powloka, moze byc dowolnym poleceniem, np. nastepujacy
skrypt sam sie kasuje:
#!/bin/rm
#Zawartosc skryptu nie jest istotna
#jakies tam polecenia
#End script
Powloka rozwinie to nam w /bin/rm ./nazwa_skryptu, czyli skasuj hello.sh
Polecenie ECHO
Echo jest poleceniem wyswietlajacym na ekranie swoje argumenty.
Skladnia:
#!/bin/bash
echo "jakis tekst" #wydrukuje na ekranie: jakis tekst
#End script
Parametry ECHO:
-n : nie jest wysylany znak nowej linii (kursor pozostaje w tej samej linii)
-e : wlacza inetrpretacje znaków specjalnych takich jak:
\a : alert, brzeczyk systemowy
\b : backspace
\c : to samo co -n
\e : escape
\f : line feed
\n : form feed czyli wysuw strony, nowa linia
\r : znak nowej linii powrót karetki (\f\r daje to samo co \n)
\t : tabulacja pozioma
\v : tabulacja pionowa
\\ : backslash
\nnn : znak, którego kod ASCII ma wartosc ósemkowa
\xnn : znak, którego kod ASCII ma wartosc szesnastkowa
Przyklad:
echo -n "przykladowy tekst"
Jako ze w powloce znak backslash jest znakiem specjalnym, musimy go podwoić, badz wpisywac ciagi
znaków w cudzyslowach.
Przyklad:
#!/bin/sh echo -e \\a
sleep 1 #wstrzymanie skryptu na 1 sekunde
echo "peep"
echo -e "\a"
Here-Documents
'Here documents' - dokumenty miejscowe. Jest to rodzaj przekierowania, które pozwala czesc tresci skryptu
traktowac jako standardowe wejscie.
Skladnia:
<
here-document
ogranicznik
Przyklad:
#!/bin/sh
cat << KONIEC
Ten tekst zostanie
wyswietlony na ekranie
ogranicznikiem jest KONIEC
KONIEC
Po wykonaniu powyzszego skryptu otrzymamy na ekranie wszystko to co zawarlismy miedzy wyrazami
KONIEC. Ten sposób moze byc uzywany do wyswietlenia wiekszej ilosci informacji na ekran bez
koniecznosci wpisywania echo w kazdej linii. W dokumencie miejscowym dokonuje sie wszystkich
rozwiniec. Ogranicznik koncowy musi znalezc sie sam w jednej linii, dlatego ostatnie slowo KONIEC
pojawia sie na ekranie. Pierwszy ogranicznik moze byc dowolnym wyrazeniem, szczególy w podreczniku
systemowym.
Do spisu tresci
Slowa zastrzezone (ang. reserved words)
Tak jak w kazdym jezyku programowania tak samo i w bashu wystepuja slowa zastrzezone, zarezerwowane,
które maja dla powloki specjalne funkcjonalne znaczenie, wtedy gdy nie sa cytowane. Glówne slowa to:
! case do done elif else esac fi for function if in select then until while { } time [ ]
Polecenie sleep i exit
Polecenie sleep powoduje wstrzymanie wykonywania instrukcji skryptu na okreslony czas:
#!/bin/bash
echo "teraz wstrzymam skrypt na 10 sekund"
sleep 10
echo "koniec papa..."
#End script
Polecenie exit
Polecenie exit powoduje natychmiastowe zakonczenie skryptu bez wzgledu na miejsce wywolania.
Dodatkowy parametr pozwala udostepnic otoczeniu kod wyjscia, który moze nalezec do przedzialu 0 - 125,
przy czym zero jest traktowane jako sukces. Kody powyzej 125 sa zarezerwowane i maja nastepujace
znaczenie:
Kody wyjscia > 125
126 - plik nie mial atrybutu wykonywalnosci "x"
127 - nie znaleziono pliku o zadanej nazwie
128 i w góre powodem zakonczenia byl jakis sygnal (wartosc kodu wyjscia równa jest 128 + numer sygnalu)
Piszac skrypty niejednokrotnie zalezy nam aby skrypt po zakonczeniu pracy, w zaleznosci od tego jakie czesci
skryptu sie wykonaly zwrócil odpowiedni status. A wiec jesli skrypt zakonczyl dzialanie bez bledu zwróci
nam status 0, natomiast jak cos pójdzie nie tak zwróci 1. A co jezeli polowa skryptu wykona sie prawidlowo
natomiast druga nie wykona sie wcale a my chcemy o tym wiedziec - wtedy mozemy zastosowac exit z
odpowiednimi statusami.
Przyklad:
#!/bin/bash
#przenies zosia na mundek (zmien nazwe)
mv zosia mundek
if [ $? -eq 0 ]
then echo "Powodzenie"
else echo "blad"; exit 4
fi
#tutaj moze byc dalsza czesc skryptu do wykonania
echo "papa"
#End script
Jezeli np. plik zosia nie istnieje to mamy niepowodzenie w wykonaniu skryptu i chcemy zeby w takim
przypadku skrypt po zakonczeniu zglosil kod 4. Jezeli natomiast polecenie mv zadziala bez bledu to zwróci
kod 0, instrukcja if sprawdzi czy status dzialania polecenia mv jest równy 0 i wyswietli powodzenie, oraz
skrypt bedzie wykonywany dalej i zakonczy sie powodzeniem i statusem 0, w przeciwnym wypadku wykona
sie druga czesc instrukcji - skrypt wyswietli tekst "blad" i wykonujac polecenie exit ze statusem 4 przerwie
dzialanie a dalsza czesc skryptu juz se nie wykona.
Potoki i przekierowania
Potoki i przekierowania pozwalaja zmieniac standardowe wejscie, wyjscie oraz standardowe wyjscie bledów
lub laczyc je w potoki. Mozna przekierowywac dowolne z deskryptorów, jednak w wiekszosci przypadków
wykorzystywane sa jedynie na standardowych wejsciach i wyjsciach.
Skladnia potoku:
polecenie1 | polecenie2 | polecenie3 | polecenien...
Potok jest ciagiem polecen porozdzielanych znakiem "|", przy czym standardowe wyjscie polecenia
poprzedzajacego jest standardowym wejsciem polecenia nastepnego.
Przyklad:
$ /sbin/ifconfig | grep HWaddr > wynik.txt
Jak widac pierwsze polecenie wywoluje polecenie ifconfig nastepnie polecenie drugie - program grep
wyodrebnia z wyniku dzialania ifconfig jedynie linie zawierajaca adres MAC karty sieciowej i caly wynik
dzialania potoku jest zapisywany do pliku o nazwie wynik.txt - ta ostatnia operacja to wlasnie
przekierowanie...
Przekierowania
Deskryptor (ang. descriptor) - struktura definiujaca okreslony obiekt w pamieci.
Przed wykonaniem polecenia powloka sprawdza, czy nie powiazac okreslonych deskryptorów innymi, badz z
plikami. Przekierowanie wejscia [n] < plik powoduje otwarcie pliku do czytania i powiazanie deskryptora o
numerze n z zawartoscia pliku. Jesli pominiemy numer deskryptora powloka przekieruje standardowe wejscie
(deskryptor nr 0).
Przyklad:
$ mail jakis@adres.com This e-mail address is being protected from spambots, \
you need JavaScript enabled to view it < wiadomosc.txt
Przekierowanie wyjscia [n] > plik powoduje otwarcie pliku do pisania i powiazanie go z deskryptorem o
numerze n. Jesli plik nie istnieje zostanie stworzony, jesli istnieje zostanie skrócony do zera (patrz opcja
noclobber). Gdy pominiemy numer deskryptora powloka przekieruje standardowe wejscie (deskryptor nr 1).
Przekierowanie wyjscia z dopisywaniem [n] >> plik powoduje otwarcie pliku do dopisywania (jesli nie
istnieje zostanie stworzony) i powiazanie go z deskryptorem o numerze n. Gdy pominiemy numer deskryptora
powloka przekieruje standardowe wejscie. Przekierowanie wyjscia i standardowego wyjscia &> plik
powoduje otwarcie pliku do pisania i powiazanie go ze standardowym wyjsciem i standardowym wyjsciem
bledów. Powyzsze definicje nie zbyt jasno tlumacza róznice miedzy przekierowaniem wyjscia i wejscia. Na
przekierowanie wejscia mozna patrzec w ten sposób, ze czytajac z danego deskryptora bedziemy czytali z
pliku, a w przypadku przekierowania wyjscia piszac do danego deskryptora wynik znajdzie sie w pliku.
Przekierowan wejscia rzadziej sie uzywa, gdyz wiekszosc polecen pozwala podac jako parametr plik
wejsciowy zamiast standardowego wejscia.
Przyklad:
$ find / -size +500k -xdev >& duze_pliki
Wpisujac powyzsza linijke w pliku duze_pliki dostaniemy liste plików biezacego systemu plików
zajmujacych conajmniej 500 kilobajtów. W pliku tym zostana tez umieszczone komunikaty o bledach.
Duplikowanie deskryptorów. Wywolanie [n] && [m] piszac do deskryptora [n]
piszemy do deskryptora [m].
Przyklad:
$ find / -size +500k -xdev > duze_pliki 2>&1
Wywolanie to jest równowazne wczesniejszemu. Standardowe wyjscie jest powiazane z plikiem duze_pliki, a
standardowe wyjscie bledów ze standardowym wyjsciem - prosciej jesli chcemy wyeksportowac do tego
samego pliku wynik dzialania programu wraz z bledami stosujemy takie wlasnie przekierowanie 2>&1.
Opcja "noclobber"
Wlaczenie opcji:
set -o noclobber lub set -C
Po wlaczeniu tej opcji jesli w przekierowaniach &gr; plik i >& plik plik istnieje, to zostanie wyswietlony blad.
Zabezpiecza to przed przypadkowym nadpisaniem istniejacego pliku. Aby zapisac do istniejacego pliku
nalezy wylaczyc opcje 'noclobber' (set +o noclobber lub set+C) lub tez uzyc przekierowania >| plik.
Przyklad:
$ touch plik
$ set -o noclobber
$ ls > plik
bash: plik: cannot overwrite existing file
$ ls >| plik
$ set +C
$ echo koniec > plik
Zmienne
W przeciwienstwie do wiekszosci jezyków programowania w skryptach nie deklaruje sie zmiennych przed ich
zastosowaniem. Ich pierwsze uzycie jest jednoczesnie ich deklaracja. Nazwa zmiennej moze byc dowolnym
identyfikatorem, przy czym wazna jest wielkosc liter i zmienne VARIABLE i variable sa rózne. Aby odwolac
sie do zawartosci zmiennych trzeba przed nazwa zmiennej napisac znak $. Istotne jest takze to, ze zmienne sa
pamietane przez powloke jako ciagi znaków, dopiero odpowiednie polecenia interpretuja te ciagi w
odpowiedni sposób.
Przyklad:
$ ile=5
$ echo ile
ile
$ echo $ile
5
$ ile=5+5 # 5+5 jest ciagiem znaków
$ echo ile
5+5
$
Zmienne srodowiskowe
W skrypcie mozemy sie odwolac do zmiennych srodowiskowych w sposób identyczny do zwyklych
zmiennych, piszac znak dolara przed nazwa zmiennej. Aby zobaczyc liste zmiennych zdefiniowanych w
systemie wpisz w powloce polecenie env.
Przykladowe zmienne:
$HOME : katalog domowy uzytkownika
$PATH : lista katalogów przeszukiwana przez polecenia
$PS1 : znak zgloszenia powloki
$IFS : separator pola wejscioweg, wszystkie znaki z tej zmiennej uzywane sa do oddzielania slów na
wejsciu
$ IFS=":"
$ read x y z
1:2:3
$ echo x=$x y=$y z=$z
x=1 y=2 z=3
$ read x y z
1 2 3
$ echo x=$x y=$y z=$z
x=1 2 3 y= z=
$ IFS=" :"
$ read x y z
1:2 3
$ echo x=$x y=$y z=$z
x=1 y=2 z=3
$ IFS=":,"
$ read x y z
1,2:3
$ echo x=$x y=$y z=$z
x=1 y=2 z=3
$ read x,y,z *
1:2:3
bash: read: `x,y,z`: not a valid identifier
$ d=`x,y,z`
$ read $d **
1:2:3
$ echo x=$x y=$y z=$z
x=1 y=2 z=3
$ read "$d" ***
1:2:3
bash: read: `x,y,z`: not a valid identifier
Kilka ostatnich linijek z pewnoscia wprowadzilo troche zamieszania i wymaga wytlumaczenia. Powloka
przeszukuje rezultaty rozwiniec parametrycznych, rozwiniec polecen w apostrofach i rozwiniec
arytmetycznych w poszukiwaniu znaków ze zmiennej IFS { (*)-nie jest rozwinieciem}. Jesli takie znajdzie
zamienia je na spacje (**). Jesli jednak calosc ujmiemy w cudzyslowy nie dojdzie do zamiany (***).
#!/bin/sh
IFS=:
echo "$*"
echo $*
echo $@
echo $1 $3
Wykonujac skrypt z parametrami: raz dwa trzy otrzymamy wynik:
raz:dwa:trzy
raz dwa trzy
raz dwa trzy
raz trzy
Polecenie unset
Polecenie unset anuluje przypisania zmiennym wartosci usuwajac te zmienne ze srodowiska.
Mozna równiez je stosowac do funkcji.
Przyklad:
$ zmienna=5
$ echo $zmienna
5
$ unset zmienna
$ echo $zmienna
$
Polecenie EXPORT
To co przypiszemy zmiennym ginie po jego zakonczeniu, a to dlatego, ze uruchamiajac skrypt dostajemy
nowe srodowisko i wszelkie przypisania zmiennych dotycza tego srodowiska. Uruchamiajac jakis inny proces
z naszego skryptu dziedziczy on nasze srodowisko, ale po zakonczeniu skryptu wszystko znika. Istnieje
jednak polecenie export, któro pozwala zmienic srodowisko procesu rodzica. Czesto to polecenie jest
wykorzystywane w pliku konfiguracyjnym powloki (np. dla bash'a bedzie to /etc/bashrc i w katalogu
uzytkownika .bashrc).
Skladnia:
export zmienna
lub
export zmienna=wartosc
Przyklad:
export PATH=$PATH:$HOME/bin
export DISPLAY=localhost:10.0
export ORACLE_BASE=/u01/app/oracle
ORACLE_HOME=$ORACLE_BASE/product/11.0.2
export ORACLE_HOME
Zmienne parametryczne:
- Kod powrotu ostanio wykonywanego polecenia
$?
- PID procesu bieżącej powłoki
$$
- nazwa wykonywanego skryptu
$0
- liczba przekazanych parametrów
$#
- lista parametrów porozdzielanych pierwszym znakiem ze zmiennej $IFS,
$*
przykład ponieżej
$@
- lista parametrów porozdzielanych spacjami
$1,$2..
- poszczególne parametry w kolejności podanej przy uruchamianiu skryptu
Przykład:
#!/bin/sh
IFS=:
echo "$*"
echo $*
echo $@
echo $1 $3
Wykonując skrypt z parametrami: raz dwa trzy otrzymamy wynik:
raz:dwa:trzy
raz dwa trzy
raz dwa trzy
raz trzy
Polecenie unset
Polecenie unset anuluje przypisania zmiennym wartości usuwając te zmienne ze środowiska.
Można również je stosować do funkcji.
Przykład:
$ zmienna=5
$ echo $zmienna
5
$ unset zmienna
$ echo $zmienna
$
Cudzyslowy i apostrofy
Cudzyslowy
Istnieja dwa rodzaje cudzyslowów: " " (podwójne) i ' ' (pojedyncze). Istnieja jeszcze ` (apostrofy) omówione
w nastepnym rozdziale. Cudzyslowów umozliwiaja przypisanie zmiennej ciagu zawierajacego spacje.
Normalnie powloka traktuje spacje jako separatory parametrów. Jesli w poprzednim skrypcie chcielibysmy
przekazac jako parametr ciag zawierajacy spacje, musielibysmy wywolac skrypt nastepujaco:
./nazwa_skryptu raz 'dwa trzy' cztery
co po wykonaniu daloby nam:
raz:dwa trzy:cztery
raz dwa trzy cztery
raz cztery
Cudzyslowy " " i ' ' róznia sie tym, ze w przypadku tych pierwszych powloka rozwija wszystkie nazwy
zmiennych w wartosci tych zmiennych (rozwiniecia parametryczne), rozwija tez polecenia w apostrofach
(patrz Apostrofy dalej) oraz odpowiednio traktuje pewne znaki poprzedzone backslashem '\'. Nie jest tak w
przypadku drugich apostrofów. Dodatkowo jesli chcemy miedzy cudzyslowami " " uzyc tego samego
cudzyslowu, musimy poprzedzic go backslashem \. Nie dotyczy to cudzyslowów pojedynczych, gdyz znak
cudzyslowu po backslashu bedzie traktowany jako koniec lancucha. Nic nie stoi jednak na przeszkodzie, by
wewnatrz cudzyslowów " " uzywac ' ' i odwrotnie. Aby nie doszlo do rozwiniecia zmiennej w cudzyslowach
podwójnych mozemy znak dolara poprzedzic backslashem \. Aby otrzymac backslash musimy napisac go
podwójnie. Oczywiscie w cudzyslowach pojedynczych nie dojdzie do zadnego rozwiniecia.
Przyklad:
$ kat='$HOME'
$ echo $kat
$HOME
$ echo "\$HOME"
$HOME
$ kat="$HOME"
$ echo $kat
/home/radek
$ kat='Cudzyslów " podwójny'
$ echo $kat
Cudzyslow " podwójny
$ kat="Cudzyslów \" podwójny"
Cudzyslów " podwójny
Ponizej wymienione sa sekwencje poprzedzone backslashem zamieniane wewnatrz cudzyslowów
podwójnych.
Pozostale sekwencje poprzedzone backslashem (wymienione przy poleceniu echo) nie sa zamieniane.
Apostrofy
Apostrofy ( ` ` ) maja calkiem odmienne od cudzyslowów znaczenie.
Powloka wykonuje polecenie zawarte miedzy nimi, a wartoscia wyrazenia jest wynik dzialania tego
polecenia.
$ data=`date`
$ echo $data
Sun Sep 24 22:37:46 2000
#Zamiast dwóch apostrofów mozna zamiennie uzyc konstrukcji: $( ), np.:
data=$(date)
Podstawienia komend mozna zagniezdzac.
Obliczanie Wyrazen
Aby obliczyc wyrazenie mozemy skorzystac z zewnetrznego polecenia expr lub uzyc wewnetrznej konstrukcji
BASH'a, która jest szybsza gdyz nie potrzebuje oddzielnego procesu i jest wygodniejsza w uzyciu. Czasami
trzeba jednak uzyc 'expr', dlatego zostanie pózniej zaprezentowane.
Tych operatorów mozemy uzywac w nastepujacych konstrukcjach:
$((wyrazenie))
((wyrazenie))
let wyrazenie
Dwie ostatnie linijki sa sobie równowazne. Sa one poleceniami w przeciwienstwie do linijki pierwszej, która
jest rozwinieciem arytmetycznym i nie moze wystapic samodzielnie. Wystepuje przewaznie po prawej stronie
znaku przypisania, w poleceniu 'echo', itp.
Przyklad:
zmienna=0
$ echo $((zmienna=zmienna+2))
2
$ ((zmienna=$zmienna**3));echo $zmienna
8
Rozwiniecia parametryczne
Zalózmy, ze mamy 100 plików o nazwach: plik1.txt, plik2.txt, ... plik100.txt i chcemy te pliki polaczyc w
jeden o nazwie plik.txt
Sprawe zalatwi ponizszy skrypt:
#!/bin/sh
zmienna=0
cat /dev/null > plik.txt # tworzenie pustego pliku
while [ zmienna -ne 101 ]; do
zmienna=$(($zmienna+1))
cat plik$zmienna.txt >> plik.txt
done
Jesli jednak nasze pliki nazywaja sie np. plik1XX.txt, plik2XX.txt ... plik100XX.txt, to po modyfikacji
linijki cat plik$zmiennaXX.txt >> plik.txt dostaniemy komunikat o braku pliku.
Jak mozna sie domyslic powloka rozwinela zmienna $zmiennaXX, która jest niezdefiniowana. Aby to
zadzialalo wystarczy zapisac te linijke w nastepujacy sposób:
cat plik${zmienna}XX.txt >> plik.txt
W ponizszych przykladach slowo moze byc wyrazeniem arytmetycznym, ciagiem znaków, wyrazeniem
parametrycznym, poleceniem w odwrotnych apostrofach.
Inne rozwinięcia parametryczne
wartością jest wartość zmiennej parametr jeśli
jest ona zdefiniowana i nie jest pusta w
${parametr:-slowo}
przeciwnym wypadku wartością jest slowo
(zmienna parametr się nie zmienia)
wartością wyrażenia jest wartość zmiennej
parametr jeśli jest ona zdefiniowana i nie jest
${parametr:=slowo
pusta, w przeciwnym wypadku wartością
wyrażenia i zmiennej parametr staje się slowo
wartością wyrażenia jest wartość parametru, jeśli
jest on zdefiniowany i nie pusty, w przeciwnym
wypadku wyświetlana jest wiadomość powstała
${parametr:?slowo} po rozwinięciu slowo (jeśli pominiemy slowo
zostanie wyświetlony komunikat o tym, że
zmienna parametr jest niezdefiniowana bądz
równa null
jeśli parametr jest niezdefiniowany bądz null nic
${parametr:+slowo} nie jest podstawiane, w przeciwnym wypadku
wartością jest slowo
${#parametr} zwraca długość zmiennej parametr w znakach
zwraca wartość parametru po usunięciu z niego
${parametr#slowo} od początku najkrótszego ciągu pasującego do
słowa
zwraca wartość parametru po usunięciu z niego
${parametr##slowo} od początku najdłuższego ciągu pasującego do
słowa
zwraca wartość parametru po usunięciu z niego
${parametr%slowo}
od końca najkrótszego ciągu pasującego do słowa
zwraca wartość parametru po usunięciu z niego
${parametr%%slowo}
od końca najdłuższego ciągu pasującego do słowa
Przykład:
!/bin/sh
plik=/etc/lilo.conf
tmp1=${plik##*/}k
tmp2=${plik#*/}
tmp3=${plik%/*}
echo $plik
echo $tmp1
echo $tmp2
echo $tmp3
#Wykonanie skryptu spowoduje wypisanie następujących linii:
/etc/lilo.conf
lilo.conf
etc/lilo.conf
/etc
Przykład:
#!/bin/sh
line=`grep ^root /etc/passwd`
echo Shell roota: ${line##*:}
Polecenie "test"
Polecenie to ma wiele opcji i jest często wykorzystywane w skryptach do realizowania różnych warunków
logicznych, gdyż zawsze zwraca wartość logiczną.
Posiada dwa rodzaje składni, np.:
$ if test -f /etc/passwd; then
> echo "plik passwd istnieje w /etc"
> fi
jest równoważne wykonaniu:
$ if [ -f /etc/passwd ]; then
> echo "plik passwd istnieje w /etc"
> fi
Należy pamiętać o istotnym szczególe, mianowicie o pozostawieniu przynajmniej po jednej spacji po
nawiasie otwierającym i przed zamykającym.
" Opcje które mogą pojawić się po słowie test lub w nawiasach:
" (Po prawej stronie wypisane są sytuacje, kiedy polecenie zwraca prawdę)
Porównanie ciągów
lancuch ciąg nie jest pusty
lancuch1 = lancuch2, lancuch == ciągi są jednakowe
lancuch2
lancuch1 != lancuch2 ciągi są różne
-n lancuch ciąg nie jest pusty
z lancuch ciąg jest pusty
Porównania arytmetyczne
wyr1 -eq wyr2 wyrażenia są równe
wyr1 -ne wyr2 wyrażenia są różne
wyr1 -gt wyr2 wyr1 jest większe od wyr2
wyr1 -ge wyr2 wyr1 jest większe równe od wyr2
wyr1 -lt wyr2 wyr1 jest mniejsze od wyr2
wyr1 -le wyr2 wyr1 jest mniejsze równe od wyr2
wyr1 -a wyr2 wyrażenia 1 i 2 są prawdziwe (AND)
wyr1 -o wyr2 jedno z wyrażeń jest prawdziwe
(OR)
! wyr wyrażenie jest zerowe
Sprawdzenia plików
-a plik plik istnieje
-b plik plik jest urządzeniem blokowym
-c plik plik jest urządzeniem znakowym
-d plik plik jest katalogiem
-e plik plik istnieje
-f plik plik jest plikiem regularnym
-g plik plik z ustawionym bitem SGID
-h plik plik jest linkiem symbolicznym
-G plik plik, którego GID właściciela = EGID
-k plik plik z ustawionym bitem sticky
-L plik plik jest linkiem symbolicznym
-O plik plik, którego UID właściciela = EUID
-p plik plik jest łączem nazwanym (PIPE)
-r plik plik jest odczytywalny
-s plik plik ma niezerową wielkość
-S plik plik jest gniazdem
-u plik plik z ustawionym bitem SUID
-w plik plik jest zapisywalny
-x plik plik jest wykonywalny
plik1 -ef plik pliki mają ten sam numer urządzenia
oraz i-węzła
plik1 -nt plik2 plik1 jest młodszy niż plik2
plik2 -ot plik2 plik1 jest starszy niż plik2
Inne
-o nazwa opcji opcja powłokinazwa opcjijest
włączona
Jest jeden szczegół o którym warto pamiętać używająć opcji -G i -O. Można nadać skryptowi bit
SUID i SGID, ale nie jest on honorowany. Dlatego skrypcie EUID (efektywny ID) jest równy UID
(rzeczywisty ID).
Nie zapominajmy o spacjach przed i po znakach równości i różności w warunkach. Może być kilka
niejasności odnośnie niektórych warunków dotyczących sprawdzania plików, ale nie to jest
przedmiotem tego kursu i nie zostaną tu wyjaśnione. W większości wypadków
wystarczy znać opcje z literami: d, f, r, s, w, x. Rolę spójników logicznych AND i OR pełnią
odpowiednio operatory: -a i -o, np. [ wyr1 -a wyr2 -o wyr3 ].
Polecenie 'test' jest poleceniem zewnętrznym dla powłoki. '[' jest linkiem symbolicznym do polecenia
test. Pisząc więc [ wyrażenie ] zostaje wywołane polecenie test z dodatkowym argumentem ']'.
Istnieje jednak wewnętrzna konstrukcja w BASH'u, która pozwala obliczać wyrażenia logiczne.
" Składnia:
[[ wyrażenie ]]
Przyjmuje ona taki sam zestaw opcji oprócz spójników logicznych AND i OR, którymi są tu
odpowiednio && i ||
Polecenie 'expr'
Jak mówi podręcznik systemowy, polecenie expr wykonuje obliczenie wyrażenia i zapisuje je na
standardowe wyjście. Jako, że jest to polecenie zewnętrzne dla powłoki wykorzystuje się je
wpisując w odwrotne apostrofy, bądz w równoważną im konstrukcję $(polecenie).
" Przykład:
$ ile=5
$ ile=`expr $ile + 5`
$ ile=$(expr $ile + 5)
$ echo $ile
15
Polecenie przyjmuje w parametrze ciąg operandów i operatorów, które muszą być porozdzielane
spacjami.
Operandy mogą być liczbami bądz ciągami znaków.
Operatory
operatory logiczne OR (gdy wynik nie jest zerem zwracany jest
| , & pierwszy niezerowy argument) i AND(gdy wynik nie jest zerem
zwracany jest pierwszy z argumentów)
+ , - , * , / , operatory arytmetyczne (odpowiednio: dodawanie, odejmowanie,
% mnożenie, dzielenie całkowite, dzielenie modulo)
operatory porównania (operatory: '=' i '==' są równoważne)
dokonuje porównania wzorców, oba argumenty rzutowane są na napisy
przy czym drugi z argumentów może być wyrażeniem regularnym w
postaci akceptowalnej przez GREP'a. Jeśli wzorce pasują do sieie
zwracana jest długość napisu, jeśli nie, zwracane jest zero. Dodatkowo
:
jeśli w drugim argumencie umieszczona zostanie jednapara nawiasów
'\(' i '\)', to w przypadku pasowania wzorców zostanie zwrócony napis
zawarty między tymi nawiasami lub napis pusty w przypadku
przeciwnym.
rozpoznawane słowa kluczowe
match
napis wyr_ to samo co 'napis : wyr regularne'
regularne
substr
napis zwraca podnapis z podanego napisu o podanej długości i zaczynający
pozycja się od podanej pozycji
długość
index zwraca pozycję wystąpienia tego znaku ze zbioru znaków będącego
napis znaki drugim argumentem, którego zajmuje najniższą pozycję w napisie
length
zwraca długość napisu
napis
Przykłady:
$ napis='Ala ma kota'
$ expr index "$napis" k
8
$ expr index "$napis" A
1
$ expr index "$napis" kA
1
$ expr substr "$napis" 8 3
kot
$ expr match "$napis" 'Ala ma kota'
11
$ expr "$napis" : 'Ala ma psa'
0
$ expr "$napis" : 'Ala .*' # wyrażenie regularne
11
$ expr "$napis" : 'Ala ma \(kot\)a'
kot
Konstrukcja 'if'
Jest to instrukcja warunkowa, z którą każdy, kto programował w jakimś języku programowania
musiał się zetknąć.
Składnia polecenia:
if warunek
then
instrukcje
else
instrukcje
fi
Jako że jesteśmy przyzwyczajeni do pisania else w tej samej linii co if, możemy tak dalej robić,
tylko musimy oddzielić instrukcję if warunek od then średnikiem, gdyż inaczej powłoka traktuje then
jako dokończenie warunku.
" Przykład:
$ if [ -f /etc/passwd ]; then echo plik_istnieje ; else echo Ups; fi
" W przypadku złożonych warunków słowa else if stojące obok siebie można złączyć w słowo
elif.
Poniższy skrypt drukuje największą z trzech liczb przekazanych jako parametry:
#!/bin/sh
if [ $1 -ge $2 ]; then
if [ $1 -ge $3 ]; then echo $1
else echo $3
fi
elif [ $2 -ge $3 ]; then echo $2
else echo $3
fi
" Wykonajmi poniższe polecenia, niech imie będzie zmienną niezainicjowaną.
$ if [ $imie = "franek" ]
> then echo Cze_franek
> fi
[: =: unary operator expected
Ostatni komunikat błędu spowodowany jest tym, że zmienna imie jest niezainicjowana, wobec
czego warunek w nawiasach staje się warunkiem [ = "franek" ]. Czyli brakuje jednego parametru.
Problem rozwiąże następujący zapis: [ "$imie" = "franek" ], a warunek staje się warunkiem [ "" =
"franek" ].
Listy AND i OR
Lista AND
Składnia
instrukcja1 && instrukcja2 && instrukcja3 && instrukcja4 ...
Wykonywane są instrukcje po kolei aż do momentu określenia wyniku całości wyrażenia, czyli jeśli
np. instrukcja1 zwróci FALSE, to instrukcja2 się już nie wykona, a całość wyrażenia przyjmie
wartość FALSE. Aby otrzymać TRUE, wszystkie spośród instrukcji muszą zwrócić TRUE, w tym
wypadku wszystkie one zostaną wykonane. Zamiast instrukcji może wystąpić dowolne wyrażenie,
wtedy zamiast wykonywania instrukcji zostanie obliczona wartość tego wyrażenia. Całość można
traktować jako ciąg iloczynów logicznych przetwarzany od lewej do prawej, w którym instrukcje są
wykonywane dopóki wartość wyrażenia nie jest jeszcze określona.
Lista OR
" Składnia:
instrukcja1 || instrukcja2 || instrukcja3 || instrukcja4 ...
Analogicznie jak w przypadku listy AND instrukcje są wykonywane do momentu określenia całości
wyrażenia, tylko w tym przypadku jest odwrotnie, mianowicie jeśli dowolna z instrukcji przyjmie
wartość TRUE, to wartość wyrażenia będzie określona jako TRUE i dalsze instrukcje się nie
wykonają. Zwrócenie przez instrukcję FALSE powoduje dalsze przetwarzanie wyrażenia. Aby
otrzymać FALSE, wszystkie instrukcje muszą zwrócić FALSE.
Wyrażenie można traktować jako ciąg sum logicznych. Można łączyć ze sobą listy AND i OR, by
uzyskać bardziej skomplikowane warunki. Żaden z operatorów && i || nie ma wyższego priorytetu
od drugiego. Całość wyrażenia jest wykonywana sekwencyjnie od lewej do prawej, jeśli chcemy to
zmienić można użyć nawiasów zwykłych.
" Przykład
$ [ -f /etc/passwd ] && echo Ufff || echo Ups
Ups Laughing
Przykład:
#!/bin/sh
$1 -ge $2 ] && ( [ $1 -ge $3 ] && echo $1 || echo $3) || \
([ $2 -ge $3 ] && echo $2) || echo $3
Powyższy skrypt wyświetla na ekran największy z 3 podanych mu parametrów. Nowy element
pojawił się w 2 wierszu w przykładzie 2 na końcu linii - backslash. Postawienie znaku backslash \ na
końcu wiersza informuje powłokę, że dana linijka jest kontynuowana w następnym wierszu
(normalnie następny wiersz to następna instrukcja). Dzięki temu nie musimy pisać długich wierszy,
które przeszkadzają w wygodnym edytowaniu i oglądaniu pliku (nie wszystkie edytory obsługują
automatyczne zawijanie).
Blok instrukcji
Wszędzie tam, gdzie musimy użyć pojedynczej instrukcji, możemy zastosować blok instrukcji, czyli
ciąg instrukcji ujęty w nawiasy klamrowe lub zwykłe.
" Składnia:
{ ciąg poleceń; }
lub
(ciąg poleceń)
W przypadku nawiasów klamrowych polecenia są wykonywane w bieżącej powłoce i środowisku.
Wartością takiego bloku jest wartość ostatniej instrukcji. Dla poleceń w nawiasach zwykłych
tworzona jest nowa powłoka. Wartością zwracaną jest kod ostatniej instrukcji bądz wartość
zwrócona przez komendę 'exit'.
" Przykład:
$ if true && { false;false;true }
> then echo TRUE
> fi
TRUE
$ if true && (false;false;exit 0)
> then echo TRUE
> fi
TRUE
true i false są poleceniami zwracającymi odpowiednio prawdę i fałsz. Dodatkowo false jest często
wpisywane w /etc/passwd w ostatniej kolumnie, by uniemożliwić użytkownikowi wejście na shella.
Zamiast polecenia true może też wystąpić : (pojedynczy dwukropek), któro jest poleceniem pustym
zwracającym prawdę, np.
$ while :
> do echo -e \\a
> done
Konstrukcja 'case'
" Składnia polecenia:
case zmienna in
ciag_wzorca [ | ciag_wzorca ] ... ) ciag_instrukcji ;;
ciag_wzorca [ | ciag_wzorca ] ... ) ciag_instrukcji ;;
...
esac
Polecenie to działa w ten sposob, że dopasowuje zmienną po kolei do wzorcow i po udanym
dopasowaniu wykonuje ciąg instrukcji przyporządkowany temu wzorcowi. Jeśli zmienna pasuje do
kilku wzorcow, wykona się tylko pierwszy. W przypadku, gdy nie pasuje do żadnego wzorca nic się
nie wykona. Aby wykonała się jakaś czynność domyślna stosuje się znak * jako wzorzec. Wzorzec
ten pasuje do każdej wartości zmiennej, należy więc pamietać, by wpisać go na końcu. Przy
stosowaniu polecenia należy się kierować zasadą, by bardziej ogólne wzorce umieszczać dalej.
Najlepiej jednak zobaczyć to na przykładzie,
" Przykład:
#!/bin/sh
echo Czy chcesz kontynuować?
read x
case $x in
"tak" | "TAK" | "T" | "t" ) echo Wpisałeś tak;;
"nie" | "NIE" | "N" | "n" ) echo Wpisałeś nie;;
* ) echo Nie wiem co wpisałeś;;
esac
Skracając i uogólniając powyższe warunki otrzymamy poniższy skrypt. Wtedy do pierwszego
wzorca pasuje litera T lub t lub napis "tak" w którym dowolna z jego liter może być mała lub duża.
#!/bin/sh
echo Czy chcesz kontynuować?
read x
case $x in
[Tt][Aa][Kk] | [Tt] ) echo Wpisałeś tak;;
[Nn][Ii][Ee] | [Nn] ) echo Wpisałeś nie;;
* ) echo Nie wiem co wpisałeś;;
esac
Pętla 'for'
" Składnia polecenia
for zmienna in zbiór_wartości do
instrukcje
done
Pętla FOR działa w ten sposób, że dla każdego elementu ze zbioru wartości przypisuje go do
zmiennej i wykonuje instrukcje zawarte wewnątrz pętli.
Zbiór wartości nie musi być podany jawnie, może być rozwinięty przez powłokę, np.:
" Przykład:
#!/bin/sh
for zm in *
do
if [ -u $zm ]
then echo $zm
fi
done
W tym wypadku powłoka rozwija znak * w listę plików z bieżącego katalogu. Powyższy skrypt
drukuje na ekranie pliki, które mają ustawiony bit SUID. Poniższy przykład pokazuje jak wysłać do
wszystkich użytkowników systemu posiadających konta shellowe z powłoką /bin/bash. Pamiętajmy
o odwróconych cudzysłowach.
#!/bin/sh
for user in `cat /etc/passwd | grep /bin/bash | cut -d : -f -1`
do
echo "Cześć mam do sprzedania fortepian" | mail -s "Spam" $user
done
Pętla 'while'
" Składnia polecenia
while warunek
do
instrukcje
done
Warunek sprawdzany jest przed wywołaniem instrukcji. Instrukcje będą wykonywane dopóki
warunek jest prawdziwy.
" Przykład:
#!/bin/sh
i=1
while [ $i -le 5 ]; do
echo "$i) Opcja $i"
i=$(($i+1))
done
Pętla 'until'
" Składnia polecenia
until warunek
do
instrukcje
done
Pętla ta jest podobna do pętli WHILE z tą różnicą, że instrukcje są wykonywane dopóty warunek
jest fałszywy. Jeśli warunek staje się prawdziwy pętla jest przerywana.
" Przykład:
#!/bin/sh
until who | grep root > /dev/null
do
sleep 2
done
echo -e \\a
echo "**** root sie zalogowal ****"
Ten skrypt co 2 sekundy sprawdza, czy do systemu zalogował się root. Jeśli tak, to powiadamia nas
i kończy działanie. Jeśli w momencie uruchomienia skryptu root jest już w systemie, instrukcje w
pętli UNTIL nie wykonają się ani razu.
Pętla 'select'
" Składnia polecenia
select zmienna [ in zbiór_wartości ]
do
instrukcje
done
Zbiór wartości jest wyświetlany na standardowym wyjściu, każdą pozycję poprzedza numer. Jeśli
pominiemy in zbiór_wartości wyświetlone zostaną parametry pozycyjne (parametry przekazane do
skryptu bądz parametry przekazane funkcji). Wyświetlany jest następnie znak zachęty systemu (ze
zmiennej PS3) i powłoka oczekuje na wprowadzenie numeru. Jeśli wprowadzimy poprawny numer
zmienna z polecenia otrzyma wartość odpowiadającą temu numerowi, jeśli nie jest to numer, bądz
numer jest błędny zmienna ta otrzyma wartość NULL. Następnie wykonywane są instrukcje
wewnątrz bloku. Potem wszystko zaczyna się od początku. Aby wyjść z pętli należy wśród instrukcji
wstawić polecenie break lub return lub też wprowadzić znak EOF. Dodatkowo wprowadzona przez
nas linia jest pamiętana w zmiennej REPLY.
" Przykład:
$ select plik in *
> do
> echo Wpisana linia: $REPLY
> Wybrałeś $plik
> break
> done
Polecenia 'break' i 'continue'
Oba polecenia służą do wcześniejszego wyjścia z aktualnego przebiegu pętli, z tą różnicą, że po
wykonaniu BREAK wykonywanie pętli kończy się całkowicie podczas, gdy po wykonaniu
CONTINUE pomijane są polecenia za CONTINUE i wykonywany jest następny przebieg pętli.
Najlepiej zobaczyć to na przykładzie,
" Przykład:
#!/bin/sh
tymczasowy='names.$$$'
cat /dev/null > $tymczasowy
for zm in *
do
if [ -d $zm ] || [ -x $zm ] || [ $zm = *.tar ] || [ $zm = *.gz ] || [ $zm =
$tymczasowy ]
then continue
fi
echo Dodaję $zm do archiwum.
echo $zm >> $tymczasowy
done
tar -T $tymczasowy -cf $1
rm -f $tymczasowy
exit 0
Powyższy skrypt pakuje do archiwum o nazwie przekazanej w parametrze wszystkie pliki z
bieżącego katalogu, które nie są katalogami, nie mają atrybutu wykonywalności i nie mają
rozszerzeń *.tar lub *.gz. Skrypt najpierw zapisuje do pliku tymczasowego nazwy plików, które mają
być przetworzone,
a następnie wykonuje polecenie tar z odpowiednim przełącznikiem, by czytał nazwy plików do
spakowania z pliku tymczasowego. Ostatnie porównanie w liście OR $zm=$tymczasowy służy do
tego, by w pliku tymczasowym nie znalazła się nazwa pliku tymczasowego.
Polecenie 'shift'
Podając parametry do skryptu możemy się do nich odwoływać w skrypcie za pomocą nazw $1,
$2, ... , $9. Aby uzyskać dostęp do argumentu 10 i powyżej należy wykonać odpowiednią ilość razy
polecenie SHIFT. Pojedyncze wywolanie polecenia przesuwa wszystkie argumenty w ten sposób,
że $1
zawiera teraz $2, a $9 zawiera 10 argument. Argument, który uprzednio był w $1 jest tracony. Gdy
już wyczerpie się lista argumentów przypisywane są ciągi puste.
" Przykład:
#!/bin/sh
while [ -n "$1" ]; do
ile=$(($ile+1))
shift
done
echo -n Wpisales $ile parametr
case $ile in
1 ) echo . ;;
2 | 3 | 4) echo y. ;;
* ) echo ow. ;;
esac
Polecenie 'set'
Polecenie to ustawia zmienne parametryczne powłoki przez co stają się dostępne w taki sam
sposób, jak gdyby były podane jako parametry skryptu.
#!/bin/sh
set raz dwa trzy
echo $1 $3
Wykorzystując dodatkowo zmienną IFS możemy napisac poniższy skrypt, który przetwarza podany
mu na wejście plik /etc/passwd wypisując linie zawierające użytkownika i odpowiadający mu katalog
domowy. Aącząc go np. z poleceniem grep, możemy wyświetlić tylko tych użytkowników, którzy
mają
powłokę bash, np.
" grep /bin/bash /etc/passwd | nazwa_skryptu
" Przykład:
#!/bin/sh
IFS=":"
read x
while [ -n "$x" ]; do
set $x
echo $1 $6
read x
done
Polecenie 'trap'
Polecenie służy do przechwytywania sygnałów wysłanych do procesu skryptu. Listę sygnałów
możemy zobaczyć wpisując w powłoce polecenie trap -lSkładnia polecenia: trap Pisząc zamiast
polecenia myślnik przypisujemy sygnałowi domyślną akcję.
" Przykład: (nie zapominajmy o odwrotnych cudzysłowach)
#!/bin/sh
trap `echo Nie zamkne sie, hehehe` SIGINT
trap `echo No dobra bede juz grzeczny; trap - SIGINT`
SIGHUP
while :
do
echo Naciśnij ^C aby wyjść
sleep 2
done
Pierwsze polecenie trap powoduje, że program na wysłanie mu sygnału SIGINT będzie reagował
pierwszym komunikatem. Następna linijka mówi, że wysyłając sygnał SIGHUP skrypt również
wypisze komunikat, ale też przypisze standardowe działanie dla sygnału SIGINT. Tak więc
początkowo naciskając klawisze ^C nie uda nam się zamknąć programu, ale wystarczy wysłać do
procesu sygnał SIGHUP, by było to możliwe. Aby wysłać do dowolnego procesu w systemie jakiś
sygnał używamy polecenia kill [patrz: man kill]. W naszym przypadku będzie to polecenie kill
-SIGHUP PID, gdzie PID to numer identyfikacyjny naszego procesu, sprawdzimy to za pomocą
polecenia ps x [patrz: man ps]. PID procesu jest podawany w pierwszej kolumnie, a który to proces
znajdziemy po nazwie z ostatniej kolumny.
Funkcje, instrukcja 'return'
" Składnia:
nazwa_funkcji( ) {
instrukcje
}
lub
function nazwa_funkcji( ) {
instrukcje
}
Parametry do funkcji przekazujemy pisząc w miejscu wywołania nazwę funkcji i listę parametrów.
Wewnątrz funkcji do parametrów możemy się dostać tak, jak w bloku głównym do parametrów
przekazanych skryptowi, czyli przez zmienne $1, $2, itd. Odpowiednio się zmieniają także zmienne
$#, $*, $@. Po zakończeniu działania funkcji parametrom pozycyjne ($#, $*, $@,$1, ..) przywracane
są ich pierwotne wartości. Instrukcja 'return' powoduje przerwanie wykonania funkcji i przetwarzanie
następnej linijki od miejsca wywołania. Instrukcję 'return' można wykorzystać do tego, by funkcja
mogła zwrócić jakąś wartość, jednak nie można umieścić nazwy funkcji po prawej stronie znaku
przypisania, tak więc nie można pobrać tej wartości. W praktyce zwraca się jedną z dwu wartości:
jeden lub zero, a wynik działania funkcji można sprawdzić w instrukcji 'if'. Aby napisać funkcję, która
zwraca jakąś wartość trzeba w tej funkcji użyć nazwy zmiennej, która będzie dostępna w miejscu
wywołania funkcji. Poniższy przykład pokazuje jak napisać funkcję zwracającą kwadrat liczby
przekazanej w parametrze. Wykorzystujemy tu zmienną pomocniczą kw, której zostanie przypisany
kwadrat wyrażenia.
" Przykład:
$!/bin/sh
kwadrat() {
kw=$(($1*$1))
}
kwadrat 2
echo $kw
W kolejnym przykładzie wykorzystujemy instrukcję 'return' do zwrócenia powodzenia danej funkcji,
przy czym wartość równa zeru będzie interpretowana jako powodzenie, a różna od zera jako
porażka. Zatem jeśli będziemy chcieli napisać funkcję, która ma zostać użyta w instrukcji IF lub w
listach AND i OR, to aby wyrażenie przyjęło wartość prawdy musimy zwrócić zero. Poniższa funkcja
oczekuje dwóch parametrów: pliku i znaku mówiącego o prawie dostępu (jeden ze znaków rwx).
Gdy użytkownik ma dla danego pliku prawo do operacji przekazanej w parametrze funkcja zwróci
zero.
#!/bin/sh
czy_masz_prawa() {
case $2 in
"r") if [ -r $1 ]; then return 0; fi ;;
"w") if [ -w $1 ]; then return 0; fi ;;
"x") if [ -x $1 ]; then return 0; fi ;;
esac
return 1
}
if czy_masz_prawa /etc/passwd w; then
echo ziutek::500:500::/home/ziutek:/bin/bash >> /etc/passwd
else echo Brak praw
fi
Domyślnie każda zmienna jest globalna. Umieszczając w ciele funkcji przed pierwszym użyciem
zmiennej dyrektywę 'local' powodujemy, że jest ona traktowana jako lokalna.
" Przykład:
$ func1 () { local zmienna=1; }
$ func2 () { zmienna=2; }
$ zmienna=0
$ echo $zmienna
0
$ func1 $ echo $zmienna
0
$ func2 $ echo $zmienna
2
Podając jako parametr nazwę katalogu możemy za pomocą poniższego skryptu zdjąć atrybut
wykonywalności dla wszystkich plików danego katalogu i jego podkatalogów. Warto zauważyć, że
BASH dopuszcza rekurencję. To samo można zrobić krócej wykorzystując polecenie 'find':
find -type f -perm +111 -exec chmod -x {} \;
#!/bin/sh
Zdejmij_x() {
for zm in *; do
if [ -f $zm ]; then
chmod  x $zm
fi
if [ -d $zm ]; then
cd $zm;
zdejmij_x;
cd ..
fi
done
}
katalog=$PWD
cd $1
zdejmij_x
cd $katalog
Polecenie 'source'
Gdy wywołujemy skrypt w powłoce dla skryptu tworzone jest nowe środowisko, podobnie jeśli
wywołamy w skrypcie jakiś inny skrypt lub program dzieje się podobnie. Polecenie SOURCE
pozwala wykonać skrypt w bieżącym kontekście (bez tworzenia nowego środowiska). Ma to między
innymi takie zastosowanie jak polecenie include z języka C włączające do pliku programu inny plik.
Tak więc możemy oddzielić plik z funkcjami od bloku głównego.
" Przykład:
#Plik funkcja:
czy_masz_prawa() {
case $2 in
"r") if [ -r $1 ]; then return 0; fi ;;
"w") if [ -w $1 ]; then return 0; fi ;;
"x") if [ -x $1 ]; then return 0; fi ;;
esac
return 1
}
#Plik główny:
#!/bin/sh
source funkcja
if czy_masz_prawa /etc/passwd w; then
echo ziutek::500:500::/home/ziutek :/bin/bash >> /etc/passwd
else echo Brak praw
fi
Zamiast pisać SOURCE możemy użyć jego krótkiego odpowiednika, czyli pojedynczej kropki, np.:
. funkcja
Polecenie 'exec'
Polecenie pozwala wywołać program podany w parametrze, o ile jednak zwykłe wywołanie
programu tworzy nowe środowisko, to polecenie exec powoduje, że bieżący kontekst procesu
zostaje zamazywany przez kontekst programu wywoływanego. Nie można powrócić do skryptu
powłoki w
przeciwieństwie do polecenia source, któro nie zmienia kontekstu, lecz podaje polecenia z pliku
bieżącej powłoce bez tworzenia nowego środowiska. O ile w przypadku polecenia source
parametrem był dowolny plik tekstowy zawierający polecenia do wykonania, to argumentem
polecenia exec może być również skompilowany program. Możemy sprawdzić działanie
uruchamiając
np. jeden ze skryptów pisząc exec nazwa_skryptu. Po takim wywołaniu i zakończeniu skryptu nie
będzie już powłoki, w której go wykonalśmy, gdyż jej kontekst został zastąpiony przez kontekst
skryptu.
Polecenie 'wc'
Polecenie to wypisuje na standardowe wyjście ilość linii, słów i bajtów pliku podanego w
parametrze. Podając odpowiednie opcje możemy wybrać interesujące nas informacje, które zostaną
podane na wyjście.
Opcje polecenia 'wc'
-c, --bytes, --chars wydrukowanie ilości bajtów
-w, --words wydrukowanie ilości słów
-l, --lines wydrukowanie ilości linii
" Przykład:
$ dzis=$(date | awk '{print $3}')
$ last | grep root | awk '$5=='$dzis | wc -l
Wywołując powyższe polecenia dowiemy ile razy dzisiejszego dnia logował się root.
Polecenie 'cut'
Polecenie dla każdej linii z wejścia wycina określone fragmenty, przy czym dla każdej linii jest to taki
sam fragment.
Wejściem mogą być pliki podane w parametrze bądz standardowe wejście.
Opcje polecenia 'cut'
-b, --bytes lista_bajtów wypisz wyłącznie bajty wyliczone w
lista_bajtów
-c, --characters lista_znaków wypisz wyłącznie znaki wyliczone w
lista_znaków (opcja równoważna -b,
szczegóły w manualu)
-f, --fields lista_pól wypisz wyłącznie pola wyliczone w lista_pól
-d, --delimiter delim separator pól (standardowo tabulator)
-s, --only-delimited nie drukuj linii nie zawierających separatora
pól(ma zastosowanie w przypadku opcji -f)
lista_bajtów, lista_znaków i lista_pól to ciąg liczb bądz zakresów oddzielonych przecinkami,
najlepiej zrozumieć to na przykładzie,
" Przykład:
$ cat /etc/passwd | cut -f 1,6 -d :
$ cat /etc/passwd | cut -f 1,3-5 -d :
$ cat /etc/passwd | cut -f 3- -d :
$ cat /etc/passwd | cut -b -10
Domyślnym separatorem pól jest tabulator, więc jeśli na wejście podamy ciąg spacji nie zostanie on
uznany za separator. Podobnie jeśli obierzemy za separator spację, tabulator nie będzie
separatorem. Problem może rozwiązać polecenie 'col', któro między innymi zamienia tabulatory na
spacje. Taki potok wyglądałby następująco:
" cat plik | col -x | cut -f 1 -d ' '
Polecenie 'tr'
Polecenie tłumaczy lub usuwa znaki ze standardowego wejścia, wynik zapisuje na standardowe
wyjście. Musimy do polecenia przekazać jeden lub dwa zbiory znaków. Jeśli będą to dwa zbiory
będzie dokonywane tłumaczenie ze zbioru pierwszego na drugi. W przypadku przekazania jednego
zbioru dokonuje się w zależności od przekazanych opcji kasowania lub ściskania znaków. Ściskania
znaków można także dokonywać w przypadku dwóch zbiorów. Nie zostały tu wymienione wszystkie
kombinacje opcji. Nie zostały też opisane przypadki, kiedy zbiory pierwszy i drugi nie są równej
długości lub znaki się powtarzają. Aby sprawdzić jak polecenie reaguje w takim wypadku
zajrzyj do podręcznika systemowego.
Opcje polecenia 'tr'
-s, --squeeze-repeats zastępuje sekwencję powtórzonych znaków
zbioru pierwszego pojedynczym wystąpieniem
tego znaku
d, --delete usuwa znaki zawarte w zbiorze pierwszym_
-c, -- complement jeśli podany przed zbiorem pierwszym pod
uwagę jest brane dopełnienie zbioru
pierwszego (znaki nie będące w zbiorze)
Postać zbioru znaków
Zbiór znaków może być:
" listą znaków (można używać znaków poprzedzonych backslashem - należy pamiętać o
cudzysłowach, by uniknąć rozwijania tych znaków przez powłokę)
" zakresem (np. a-z)
" klasą znaków podawaną w postaci [:nazwa_klasy:], patrz tabelka poniżej
Znaki poprzedzone backslashem
\a Control-G (bell)
\b Control-H (backspace)
\f Control-H (wysuw strony)
\n Control-J (nowa strona)
\t Control-I (tabulator)
\v Control-K (tabulator poziomy)
\ooo znak o kodzie ósemkowym ooo
\\ backslash
Klasy znaków
alnum Litery i cyfry
alpha Litery
blank Pozioma biała spacja (tabulator,
spacja)
cntrl Znaki kontrolne
digit Cyfry
graph Znaki drukowalne (bez spacji)
lower Małe litery
print Znaki drukowalne (ze spacją)
punct Znaki interpunkcyjne
space Dowolny biały znak
upper Duże litery
xdigit Cyfry szestnastkowe
" Przykład:
$ tr a-z A-Z
$ tr [:lower:] [:upper:] # obie linijki zamieniają małe znaki na duże.
$ tr -cd [:alnum:] # kasuje wszystkie znaki niealfanumeryczne
Przykład:
$ for plik in *.HTM; do
> mv $plik `echo ${zm%.HTM} | tr A-Z a-z`.html
> done
Powyższa pętla zamienia wszystkie nazwy plików o rozszerzeniach *.HTM na nazwy o
rozszerzeniach *.html zamieniając przy tym wszystkie litery duże na małe.
Grep
grep - jeden z podstawowych programów wchodzących w skład systemu Unix. Służy do
znajdowania w strumieniu wejścia (plik, lub po prostu wpisywany tekst) ciągów znaków pasujących
do danego wyrażenia regularnego. Został napisany przez Kena Thompsona.
"grep" jest angielskim akronimem od słów global regular expression print, czyli drukowanie
globalnych wyrażeń regularnych.
" Przykłady zastosowań:
" wypisanie na standardowym wyjściu linijek pasujących do danego wyrażenia
określony tekst: grep "szukany_tekst" plik
" wypisanie na standardowym wyjściu linijek, nie pasujących do wzorca: grep -v
"szukany_tekst" plik
Polecenie "grep" służy do wyszukiwania wzorcu z pliku i przekierowywania. Komenda ta wyświetla
linie pasujące lub nie do określonego wzorca.
Uproszczona składnia:
grep [-v] wzorzec [Plik]
-v oznacza negację wzorca (czyli wzorzec nie może wystąpić);
wzorzec - to łańcuch znaków do wyszukiwania, może zawierać wyrażenie regularne;
Plik - plik/lista plików do przeszukania;
" Przykłady:
grep 'Ala' plik -znajduje wyraz 'Ala' w pliku;
grep 'A[lg]a' plik -znajduje wyraz 'Ala' lub 'Aga';
grep 'A.a' plik -znajduje wyrazy takie jak 'Ala' 'Aga' itp;
grep '^Ala' plik - znajduje wyraz 'Ala' na początku wersu;
grep 'Go*gle' plik - znajduje wyraz 'Gogle', 'Google' itd;
grep '[0-9]' - znajduje dowolny ciąg znaków z zakresu od 0 do 9;
yródło: http://pl.wikipedia.org/wiki/Grep
Zadania/sytuacje:
" Zestawienie tylko nazw pasujących plików:
grep -l 'main' *.txt
podaje nazwy wszystkich plików txt z bieżącego katalogu, których zawartość zawiera `main'.
" Rekurencyjnie przeszukiwanie katalogów:
grep -r 'hello world' /home/user
szuka 'hello world' we wszystkich plikach pod katalogiem /home/user.
" Przeszukiwanie plików jeśli wzorzec zaczyna się od znaków nie standardowych:
grep -e '--hello world--' *
szuka wszystkich linii zawierających '--hello world--'. Bez -e , grep wziąłby '--hello world--' jako listę
opcji.
" Szukanie całego ciągu, a nie jego części:
grep -w 'hello' *
szuka tylko tych wystąpień ciągu hello, które są całymi wyrazami.
Można zastosować '\' aby dopasować początek i koniec ciągu:
grep 'hello\>' *
wyszukuje tylko ciągi kończące się na hello.
" Wyświetlenie linii sąsiadujących z pasującymi:
grep -C 2 'hello' *
wyświetli 2 linie zawartości przy każdej z dopasowanych linii zawierającej hello.
"
" Wyświetlenie dodatkowo na początku linii nazwy pliku zawierającego dany ciąg:
grep 'username' /etc/passwd /dev/null
" Filtrowanie samego grep:
ps -ef | grep '[c]ron'
Pominięcie nawiasów kwadratowych, dopasowywuje nie tylko linię wyjściową z polecenia ps
dotyczącą cron ale i linię wyjściową ps dla samego procesu grep.
ps -ef | grep 'cron'
wyświetli:
user 3457 2281 0 13:53 pts/1 00:00:00 grep cron
root 5505 1 0 Feb29 ? 00:00:00 /usr/sbin/cron
Można to także ograniczyć poprzez zastosowanie opcji -v, która oznacza negację wzorca:
ps -ef |grep 'cron' |grep -v grep
" Szukanie ciągów zawierających zadany ciąg1 i ciąg2 czyli logiczne AND:
grep 'hello world' /home/user/*.txt | grep 'zuza,pralka'
grep wyświetli wszystkie linie zawierające `hello world', oraz/i `zuza,pralka'.
" Równoczesne przeszukiwanie standardowego wejścia i pliku na przykładzie polecenia
cat:
cat /etc/passwd | grep 'user' - /etc/group
" Kobinacja polecenia grep z find i xargs.
find /home/user -name '*.txt' -print | xargs grep 'hello world' /dev/null
polecenie przeszuka tylko pliki txt pod względem występowania w nich, ciągu 'hello world'
" Dlaczego grep zgłasza "Binary file matches" - Gdyby grep podał wszystkie pasujące
"linie" pliku binarnego, utworzony wynik nie byłby prawdopodobnie do niczego przydatny, a
nawet mógłby nabałaganić na ekranie. Zatem GNU grep wyłącza wypisywanie dla plików,
które wyglądają na binarne. Możemy zmusić go do wypisywania dopasowanych linii również
dla takich plików stosując opcję '-a' lub '--binary-files=text'. Chcąc się pozbyć komunikatów
"Binary file matches" ("Plik binarny pasuje do wzorca") należy skorzystać z opcji '-I' lub `--
binary-files=without-match'.
" Dlaczego `grep -lv' nie wypisuje nazw plików niepasujących? `grep -lv' wypisuje nazwy
wszystkich plików zawierających co najmniej jedną niepasującą linię. Do uzyskania listy nazw
wszystkich plików nie zawierających żadnych pasujących linii, należy użyć opcji `-L' lub `--files-
without-match'.
" Przydatne linki:
Więcej na temat grep oraz tłumaczenie dokumentacji man:
http://ultra.ap.krakow.pl/~bar/GREP/grep_toc.html
Sed
sed (ang. Stream EDitor  edytor strumieniowy)  program służący do przetwarzania plików
tekstowych.
sed jest filtrem  pobiera dane w postaci wierszy tekstu ze standardowego wejścia, przetwarza je
zgodnie z poleceniami podanymi w wierszu poleceń lub zapisanymi w pliku i przesyła wyniki
operacji na standardowe wyjście. Cechą charakterystyczną seda jest to, że wykonuje on na danym
wierszu wszystkie polecenia, jakie są do wykonania. Ponieważ przetwarzanie odbywa się wiersz po
wierszu, sed nie ma praktycznie żadnych ograniczeń na wielkość przetwarzanego pliku.
Sed nadaje się szczególnie do przetwarzania plików o pewnej strukturze. Napisany został
pierwotnie przez Lee E. McMahona dla systemu UNIX w roku 1973, w chwili obecnej dostępny jest
praktycznie dla każdego systemu operacyjnego, który dysponuje wierszem poleceń.
Zestaw poleceń programu sed wzorowany jest na poleceniach programu ed i większość z nich
działa podobnie, jeśli uwzględni się specyfikę seda. Na przykład, polecenie 25d działa w sedzie
następująco: sprawdza, czy bieżący wiersz jest 25 wierszem przetwarzanego pliku i jeśli tak, to nie
kieruje go na wyjście ("usuwa" go), a jeśli nie, wiersz pojawi się na wyjściu. Jednak nie wszystkie
polecenia eda mogły zostać zaimplementowane, w szczególności te, które odpowiedzialne są za
kopiowanie lub przenoszenie tekstu. Zamiast tego, sed dysponuje buforem, który pozwala
przechować wybrane wiersze i zestawem poleceń do manipulacji nimi. Na przykład, polecenie eda
25t76 (skopiuj wiersz 25 do 76) można zrealizować w sedzie jako dwa polecenia: 25h; 76g 
przechowaj wiersz 25 w buforze i wstaw po napotkaniu wiersza 76.
" Oto typowy przykład wywołania seda:
sed -e 's/dobry/zły/g' plik_wejściowy > plik_wyjściowy
s oznacza zastępowanie, g globalne, w całym wierszu. Po pierwszym ukośniku pojawia się
wyrażenie regularne, opisujące tekst, który ma być wyszukany, po drugim wyrażenie, które ma
zastąpić wyszukany tekst. Polecenie zastępowania jest najczęściej wykonywanym poleceniem
seda.
" Przykład wywołania seda z dołączonym zewnętrznym plikiem poleceń polecenia.sed:
sed -f polecenia.sed plik_wejściowy > plik_wyjściowy
Następujące wywołanie usuwa z pliku wszystkie puste wiersze, lub te, które zawierają wyłącznie
spacje:
sed -e '/^\ *$/d' inputFileName
" Przykład ten wykorzystuje kilka metaznaków używanych w wyrażeniach regularnych seda:
" ^  pasuje do początku wiersza
" $  pasuje do końca wiersza
" .  pasuje do dowolnego znaku
" *  pasuje do dowolnej (również zerowej) liczby wystąpień poprzedzającego znaku
" [ ]  pasuje do dowolnego znaku wewnątrz nawiasów [ ]
De facto sed jest prostym językiem programowania i mimo że nie pozwala na korzystanie z żadnych
zmiennych, dopuszcza jedynie proste instrukcje skoku, to jest językiem zupełnym w sensie Turinga.
sed wyewoluował z grepa, programu wyszukującego frazy tekstowe w plikach i był jednym z
pierwszych programów unixowych pozwalających na edycję plików z wiersza poleceń, a jego
pojawienie się stanowiło impuls do powszechnego używania wyrażeń regularnych. Podobny w idei
działania awk może być traktowany jako następca seda. Wraz z awkiem, sed uważany jest za
prekursora języka Perl. W szczególności, s/// jest poleceniem Perla.
Mimo sędziwego wieku sed nadal jest używany i rozwijany ze względu na szybkość działania. W
specyficznych operacjach bije na głowę zarówno AWKa jak i Perla. Ciekawym rozwinięciem seda
jest GNU sed pozwalający na bezpośrednią zmianę przetwarzanego pliku i Super-sed, którego
składnia jest zgodna ze składnią Perla.
zródło: http://pl.wikipedia.org/wiki/Sed_(program)
" Przydatne linki:
przykłady użycia: http://www.gentoo.org/doc/pl/articles/l-sed1.xml
skrypty sed: http://sed.sourceforge.net/
"jednolinijkowce": http://sed.sourceforge.net/sed1line.txt
Awk
AWK jest interpretowanym językiem programowania, którego główną funkcją jest wyszukiwanie i
przetwarzanie wzorców. Jest także nazwą programu początkowo dostępnego dla systemów operacyjnych
będących pochodnymi UNIX-a, obecnie także na inne platformy. Nazwa języka pochodzi od pierwszych liter
nazwisk jego autorów Alfreda V. Aho, Petera Weinbergera i Briana Kernighana i czasami jest zapisywana
małymi literami oraz odczytywana jako jedno słowo awk.
Definicja języka AWK jest zawarta w POSIX 1003.2 Command Language And Utilities Standard. Wersja ta
jest z kolei oparta na opisie z The AWK Programming Language, napisanym przez Aho, Weinbergera i
Kernighana, z dodatkowymi właściwościami, zdefiniowanymi w wersji awk z SysVR4.
W wierszu poleceń podaje się opcje dla awk, tekst programu (jeśli nie podano go poprzez opcję -f lub --file) i
wartości, które mają być udostępnione w predefiniowanych zmiennych ARGC i ARGV.
yródło: http://pl.wikipedia.org/wiki/AWK
" Przykład wywołania:
awk -f ...
" najpopularniejszym użyciem awk jest polecenie:
awk '{ print $1 }' ...
powoduje to wypisanie pierwszego pola z każdego rekordu: np. pierwszego wyrazu.
" Przydatne linki:
Tutorial awk: http://sokrates.mimuw.edu.pl/~sebek/awk.html
Magia polecenia find
Find - program służący do przeszukiwania systemu.
Składnia:
find katalog_startowy cecha akcja
np:
find /home/user/ -name nazwa_pliku.txt
find /home/user -name "*.txt" -print
- co wyszuka nam w katalogu /home/user plik nazwa_pliku.txt lub drugi przykład wszystkie pliki *.txt
" cechy do określenia plików:
-name pattern - nazwa pliku okrełona przy pomocy danego wzorca;
-atime $n$ - do pliku zaglądano $n$ dni wcześniej;
-ctime $n$ - status pliku zmieniono $n$ dni wcześniej;
-mtime $n$ - zawartość pliku zmieniono $n$ dni wcześniej;
-user username - plik należy do danego użytkownika;
-group gname - dana grupa pliku;
-perm przywileje - pliki o danych przywilejach;
-local - plik na lokalnym systemie plików;
-size n[c] - dany rozmiar pliku;
-type c - dany typ pliku ( b, c, d, D, f, l, p, lub s).
b  blokowy (buforowany) plik specjalny
c  znakowy (niebuforowany) plik specjalny
d  katalog
p  łącze nazwane (FIFO)
f  zwykły plik
l  dowiązanie symboliczne
s  gniazdo
W okresleniu cechy można stosować oznaczenia "+n'' lub "-n'': pierwsze znaczy "więcej, niż... '', drugie
"mniej, niż... '' np. -atime +15 oznacza pliki otwierane przed więcej, niż 15-oma dniami.
" Typowe akcje:
-print - po prostu drukuje nazwę pliku;
-exec polecenie - wykonanie dla każdego ze znalezionych plików danego polecenia;
-ok polecenie - jak wyżej ale trzeba potwierdzić przed każdym wykonaniem.
" Czasem lepiej jest korzystać z przekierowania wyników poszukiwań do polecenia xargs niż z akcji
exec:
zamiast:
find / -name core -exec rm -f {} \;
wersja z xargs
find / -name core -print | xargs rm -f
Przykłady zastosowań:
" przeszukanie katalogu /home/user/muza pod kątem wystąpienia w nazwie pliku ciągu shazza (-iname
pomija rozróżnianie wielkości znaków).
find /home/user/muza -iname shazza
" przeszuka katalog i wyświetli nazwy plików, które nie mają w sobie nazwy 'shazza'
find /home/user/muza ! -name 'shazza'
" szukanie w aktualnym katalogu i wyświetlenie wszystkich plików których data ustawiona jest na Dec
21
find `pwd` -exec ls -l '{}' \; | grep "Dec 21"
" szukanie podkatalogów w katalogu /katalog należących do użytkownika osoba
find /katalog/ -user osoba -type d
" wyszukaj i skasuj wszystkie pliki z katalogu /tmp należące do usera nazwa_usera
find /tmp -user nazwa_usera -exec rm -rf {} \;
" Szukanie w bieżącym katalogu plików o rozmiarach przekraczających 10 MB z wyświetleniem
szczegułów (ls -l):
find . -size +10485760c -exec ls -l {} \;
" szukanie ciągu, gdy nie wiadomo w jakim pliku jest ciąg i w jakim katalogu znajduje się plik:
find /home/user/ -name "*.txt" -print | xargs grep "szukany_ciag"
" szukanie plików, w różnych katalogach - najpierwsz pszeszuka usr, home i na koncu tmp:
find /usr /home /tmp -name "*.jar
" pominiecie komunikatów o błędach jeśli nie mamy prawa dostepu do któregoś z katalogów:
find /usr /home /tmp -name "*.jar" 2>/dev/null
" Możemy także szukać plików które były modifikowane albo otwierane pod względem opcji -newer,
-anewer, and  cnewer. Jest to podobne do -mtime, -atime, and  ctime.
" -newer wskazuje na pliki, których zawartość nie była modyfikowana ostatnio
" -anewer wskazuje na pliki, które były odczytywane ostatnio
" -cnewer wskazuje na pliki, których status zmienił się ostatnio
- Aby odnalezć pliki w katalogu domowym /home które były jakkolwiek edytowane od ostatniego pliku tar:
find ~ -newer backup.tar.gz
" szukanie plików po prawach dostępu do plików:
find . -type f -perm a=rwx -exec ls -l {} \;
#albo
find . -type f -perm 777 -exec ls -l {} \;
find . -type f -perm -ug=rw -exec ls -l {} \; 2>/dev/null
#albo
find . -type f -perm -220 -exec ls -l {} \; 2>/dev/null
find . -type f -perm /ug=rw -exec ls -l {} \; 2>/dev/null
find . -type f -perm /220 -exec ls -l {} \; 2>/dev/null
" Aby wyszukać wszystkie pliki w systemie które mogą być edytowane przez wszystkich
użytkowników:
find / -wholename '/proc' -prune -o -type f -perm -0002 -exec ls -l {} \;
" Inne przydatne komendy find:
#Szukanie plików po użytkowniku, grupie:
[root] $ find / -type f -user username -exec ls -ls {} \;
[root] $ find / -type f -group users
find / -type d -gid 100
wylistuje pliki które nie mają odniesienia do /etc/passwd i /etc/group w uzytkowniku i grupie
find / -nouser -o -nogroup
pokaz pliki z prawami SID oraz GID
find / \( -perm -2000 -o -perm -4000 \) -ls
167901 12 -rwsr-xr-x 1 root root 9340 Jun 16 2006 /usr/bin/rsh
167334 12 -rwxr-sr-x 1 root tty 10532 May 4 2007 /usr/bin/wall
Welcome to the real world
" Przykłady:
" Wyświetl wszystkie pliki jpg w głąb do dwóch poziomów głównej struktury katalogu domowego:
find $HOME -maxdepth 2 -name \*jpg -print -exec xv {} \;
find $HOME -maxdepth 2 -name '*jpg' -print -exec xv {} +
find $HOME -maxdepth 2 -name '*jpg' -print0 | xargs -0 xv
" zadanie crontab, nadające uprawnienia wszystkim plikom i katalogom do czytania i pisania dla
wszystkich w zadanych katalogach:
find /home/user -type f -exec chmod a+wr {} \;
find /home/user -type d -exec chmod 777 {} \;
" zadanie crontab wymuszające ustawienie poprawnych uprawnień dla właściciela i grupy dla
poszczególnych plików:
find /home/user \( -name '[p,u]*' -a -type f -a ! -perm 664 \) -exec chmod 664 {} \;
find /home/user \( -name 'd*' -a -type f -a ! -perm 666 \) -exec chmod 666 {} \;
find /home/user \( -type f -a ! -user username \) -exec chown username {} \;
find /home/user \( -type f -a ! -group groupname \) -exec chgrp programs {} \;
" zadanie: kasowanie starszych niż 30 dni logów i rejestrację informacji o skasowanych plikach:
find /directory/log -mtime +30 -print -exec rm -f {} \; >> del_files.log 2> /dev/null
" funkcja: kasuje stare pliki tymczasowe o danych definicjach nazw z rejestracją informacji o
skasowanych plikach
find / -name core -type f -fstype xfs -print -exec rm -f {} \; >> del_files 2> /dev/null
find /var/tmp -mtime +1 -name '*ala*' -print -exec rm -f {} \; >> del_files 2 >
/dev/null
find /var/tmp -mtime +1 -name 'string*' -print -exec rm -f {} \; >> del_files 2 >
/dev/null
find /var/tmp -mtime +7 -print -exec rm -f {} \; >> del_files 2 > /dev/null
" Przeszukanie katalogu /var tylko raz, a następnie wylistuje wszystkie pliki z uprawnieniami SUID do
pliku suid.txt oraz wszystkie pliki duże pliki (większe niż 100 MB) z listingiem do pliku big.txt
(przykład zaciągnięty z manuala programu find):
find /var \( -perm +4000 -fprintf /root/suid.txt '%#m %u %p\n' \) , \
\( -size +100M -fprintf /root/big.txt '%-10s %p\n' \)
Magia find + xargs:
" xargs znacznie poprawia szybkość i wydajność
- druga linia wykonuje się szybciej niż pierwsza dla dużej ilości plików
find / -name core -exec rm -f {} \;
rm -f $(find / -name core -print)
- Innymi słowy wywołując jednokrotnie polecenie "rm" włączając wszystkie nazwy plików, jest szybsze niż
wielokrotne wywoływanie polecenia "rm" osobno dla każdego pliku.
- Jednakże drugie polecenie może się nie powieść, kiedy liczba plików jest bardzo duża i przekracza
maksymalną liczbę znaków dozwolonych w jednym poleceniu (komendzie)
- "xargs" połączy wyjście komendy z pierwszej linii polecenia find i wykona się wielokrotnie z wielokrotnymi
argumentami jeżeli będzie to konieczne aby zapobiec przekroczeniu maksymalnej ilości dostępnych znaków
w jednej komendzie.
find / -name core -print | xargs rm -f
- wykonując proste polecenia można łatwo zobaczyć siłę programu xargs:
find $HOME -maxdepth 2 -name \*.jpg -exec echo {} \;
find $HOME -maxdepth 2 -name \*.jpg | xargs echo
Potęga opcji zero!
- Druga komenda się nie powiedzie jeżeli którekolwiek z plików będzie zawierało spację lub inne specjalne
znaki:
find $HOME -maxdepth 2 -name \*.jpg -exec ls {} \;
find $HOME -maxdepth 2 -name \*.jpg | xargs ls
- wyeliminowanie pustych pól z nazw plików rozwiązuje problem:
find $HOME -maxdepth 2 -name \*.jpg -print0 | xargs -0 ls
" Uwaga: dobrym sposobem na testowanie komend jest używanie "-exec echo", zanim wydamy
prawdziwą komendę a w szczególności taką która ma kasować jakiekolwiek pliki w systemie ( -exec
rm -rf {} ).


Wyszukiwarka

Podobne podstrony:
Kurs pisania skryptów w powłoce BASH
Alt klawiatura numeryczna Kurs dla opornych
Szybki kurs Adobe Photoshop
kurs latexa (4)
Kurs Psychografologii
kurs
Instrukcja Kurs 09

więcej podobnych podstron