<!doctype linuxdoc system>

<!--
     An introduction to bash shell programming
 -->

<!-- Title information -->

<article>
<title>Uvod v programiranje v BASH - HOW-TO</title>
<author>Mike G <tt/mikkey at dynamo.com.ar/</author>
<date>
    Čet Jul 27 09:36:18 ART 2000
</date>
<abstract>
Ta spis vam bo v pomoč, ko se boste lotili pisanja osnovnih in nekoliko
zahtevnejših lupinskih skriptov. Ni mišljeno, da bi bil vsemogočen
vodnik (glej naslov). Sam NISEM nikakršen izvedenec za programiranje v
lupini in sem ga napisal zato, da bi se pri tem veliko naučil,
za povrh pa morda z njim koristil tudi drugim. Vsak odziv nanj je
dobrodošel, še posebno v obliki popravkov :)
</abstract>

<!-- Table of contents -->
<toc>

<!-- Requisites -->
<sect>
Uvod


<sect1>
Kje dobiti najnovejšo različico?
<p>
<htmlurl url="http://www.linuxdoc.org/HOWTO/Bash-Prog-Intro-HOWTO.html"
 name="http://www.linuxdoc.org/HOWTO/Bash-Prog-Intro-HOWTO.html">


</sect1>

<sect1>
Potrebno znanje
<P>
Prav pride dobro poznavanje ukazne vrstice GNU/Linux in osnovnih pojmov
programiranja. Čeprav tole ni uvod v samo programiranje,
pojasnjuje (oziroma si vsaj prizadeva pojasniti) številne osnovne
pojme.
<P>
</sect1>
<sect1>
Uporaba tega spisa
<P>
Ta spis vam bo prišel prav v naslednjih primerih:
<itemize>
<item> Nekaj veste o programiranju in bi radi začeli
pisati lupinske skripte.
<item> Nekaj malega se vam sanja o programiranju in bi vam prišel prav kak napotek.
<item> Želeli bi videti nekaj lupinskih skriptov in komentarjev,
da bi lahko začeli pisati lastne.
<item> Selite se iz DOS/Windows (oziroma ste se pravkar
preselili) in bi želeli narediti "paketne" procese.
<item> Tako ste navdušeni nad računalništvom, da preberete vsak
how-to, ki vam pride pod roke.
</itemize>
</sect1>
</sect>

<!-- Very simple Scripts -->
<sect>
Zelo preprosti skripti

<P> Ta HOW-TO bo postregel z nekaj namigi o lupinskih skriptih, ki bodo
močno oprti na zglede.
<P> V tem delu boste našli nekaj kratkih skriptov, ki vam bodo pomagali
razumeti različne tehnike.

<sect1>
Tradicionalni skript 'Hello World'

<!-- __TO-DO__
someone suggested using this instead of the line numbering
but it doesn't work on version 1.0.9
<pre></pre>
-->
<P>
<tscreen><verb>
  #!/bin/bash
  echo Hello World
</verb></tscreen>
<P>
<P> Ta skript ima samo dve vrstici.
Prva pove sistemu, kateri program naj uporabi pri zaganjanju
datoteke.
<P> Drugo vrstico sestavlja edini ukaz, ki ga skript izvede, in ta
izpiše 'Hello World' na terminal.
<P> Če dobite nekaj kot <it>./hello.sh: Command not found.</it>, je
verjetno napačna prva vrstica, '#!/bin/bash' - da bi ugotovili pravilno
pot do bash, izvedite 'whereis bash' ali poglejte v poglavje 'Iskanje
bash'.
</sect1>
<sect1>
Zelo preprost skript za varnostno kopijo
<P>
<tscreen><verb>
#!/bin/bash
tar -cZf /var/moja-varnostna-kopija.tgz /home/jaz/
</verb></tscreen>
<P> V tem skriptu namesto izpisovanja sporočila na terminal naredimo
arhiv uporabnikovega domačega imenika. Ne uporabljajte tega
skripta - na voljo je tudi veliko boljši, predstavljen bo pozneje.
</sect1>
</sect>

<!-- Redirection -->
<sect>
Vse o preusmerjanju

<sect1>
Teorija in hitri napotki
<p> Opisniki datoteke so trije - stdin (standardni vhod),
stdout (standardni izhod) ter stderr (standardni izhod za napake).

<p> V osnovi lahko:
<enum>
<item> preusmerite stdout v datoteko
<item> preusmerite stderr v datoteko
<item> preusmerite stdout v stderr
<item> preusmerite stderr v stdout
<item> preusmerite stderr in stdout v datoteko
<item> preusmerite stderr in stdout v stdout
<item> preusmerite stderr in stdout v stderr
</enum>
1 'predstavlja' stdout in 2 stderr.
<p> Drobno pojasnilo za boljšo predstavo o teh stvareh: z ukazom less si
lahko ogledate tako stdout (ki bo ostal v medpomnilniku) kakor stderr,
ki se bo izpisal na zaslon, vendar bo izginil, ko se boste poskušali
premikati po medpomnilniku.
</sect1>

<sect1>
Zgled: stdout v datoteko
<p> To bo preusmerilo izhodni tok programa v datoteko.
<tscreen><verb>
ls -l > ls-l.txt
</verb></tscreen>
V tem primeru bo ustvarjena datoteka z imenom  'ls-l.txt'. V njej bo
tisto, kar bi se sicer izpisalo na zaslon, ko bi pognali
ukaz 'ls -l'.
</sect1>

<sect1>
Zgled: stderr v datoteko
<p> Takole lahko standardni izhod za napake preusmerimo v datoteko.
<tscreen><verb>
grep da * 2> grep-napake.txt
</verb></tscreen>
Ustvarjena bo datoteka z imenom 'grep-napake.txt', v njej pa bo
vse, kar bo ukaz 'grep da *' izpisal na stderr.
</sect1>

<sect1>
Zgled stdout v stderr
<p> Tu bomo izhodni tok stdout preusmerili v isti opisnik datoteke kakor
stderr.
<tscreen><verb>
grep da * 1>&2
</verb></tscreen>
Del izpisa, ki bi sicer šel na stdout, bo v tem primeru preusmerjen
na stderr.
</sect1>

<sect1>
Zgled: stderr v stdout
<p> To bo povzročilo, da bo izhodni tok stderr preusmerjen v isti opisnik
datoteke kakor stdout.
<tscreen><verb>
grep * 2>&1
</verb></tscreen>
Stderr del izhodnega toka bo tako preusmerjen na standardni izhodni
tok - če boste ta ukaz prek cevovoda povezali s programom less,
boste opazili, da bodo vrstice, ki navadno 'izginejo' (ker so
izpisane na stderr), tokrat ostale vidne (ker smo jih preusmerili na
stdout).
</sect1>

<sect1>
Zgled: stderr in stdout v datoteko
<p> Celoten izhodni tok programa bomo preusmerili v datoteko. To včasih
pride prav pri izvajanju opravil v cronu, ko želite ukaz "utišati".
<tscreen><verb>
rm -f $(find / -name core) &> /dev/null
</verb></tscreen>
Ta ukaz (še vedno smo pri vnosu v cron) bo zbrisal vse datoteke z
imenom 'core' v kateremkoli imeniku. Naj vas
opozorim, da morate biti precej gotovi glede tega, kaj bo ukaz
storil, če boste njegov izhod zavrgli.
</sect1>
<!--
redirect stderr and stdout to stdout
redirect stderr and stdout to stderr
-->



<!--
<sect1>
Sample:
<p> This will
<tscreen><verb>
command
</verb></tscreen>
Explanation
</sect1>
-->

</sect>

<!-- pipes -->
<sect>
Cevovodi
<p> To poglavje preprosto in praktično razloži, kako uporabljati
cevovode in zakaj bi to sploh hoteli.

<sect1>
Kaj so cevovodi in zakaj bi jih hoteli uporabljati
        <p> Cevovodi vam omogočajo (zelo preprosto) povezati izhodni
        tok enega programa z vhodnim tokom drugega.
</sect1>


<sect1>
Zgled: preprost cevovod s programom sed
<p> To je zelo preprost način uporabe cevovodov.
<tscreen><verb>
ls -l | sed -e "s/[aeio]/u/g"
</verb></tscreen>
V tem primeru se zgodi naslednje: najprej se izvede ukaz 'ls -l',
njegov izhodni tok pa je - namesto da bi se izpisal na terminal -
posredovan programu sed, ki nato izpiše, kar mu je zaukazano.
</sect1>

<sect1>
Zgled: alternativa ukazu 'ls -l *.txt'
<p> To je verjetno bolj neroden način izvajanja 'ls -l *.txt', vendar je tu
zaradi prikaza delovanja cevovodov in ne zaradi odločanja o
primernosti rabe ukaza samega.

<tscreen><verb>
ls -l | grep "\.txt$"
</verb></tscreen>
Tu je izhodni tok ukaza 'ls -l' posredovan programu grep, ki nato
izpiše vrstice, ki ustrezajo regularnemu izrazu "\.txt$".
</sect1>



<!--
<sect1>
Sample:
<p> This will
<tscreen><verb>
command
</verb></tscreen>
Explanation
</sect1>
-->


</sect>




<!-- Variables -->
<sect>
Spremenljivke

<!-- Dry Theory -->
<P> Spremenljivke lahko uporabljate prav tako kakor v vseh drugih
programskih jezikih. Podatkovnih tipov tu ni - spremenljivko
lahko sestavlja število, znak ali niz znakov.
<P> Spremenljivke vam ni treba deklarirati, ustvari se, brž ko ji
pripišete vrednost.



<!-- Samples -->
<sect1>
Zgled: Hello World! z uporabo spremenljivk
<P>
<tscreen><verb>
#!/bin/bash
NIZ="Hello World!"
echo $NIZ
</verb></tscreen>

<P>
Vrstica 2 ustvari spremenljivko z imenom NIZ in ji priredi niz "Hello
World!". VREDNOST spremenljivke nato dobimo tako, da na začetek
postavimo znak '$'. Če tega znaka ne boste uporabili, bo izhod
programa drugačen - verjetno ne tak, kot bi želeli (kar
poskusite!).
</sect1>

<sect1>
Zgled: Zelo preprost skript za varnostno kopijo (nekoliko boljši)
<P>
<tscreen><verb>
#!/bin/bash
DATOTEKA=/var/moja-varnostna-kopija-$(date +%Y%m%d).tgz
tar -cZf $DATOTEKA /home/jaz/
</verb></tscreen>
<P>
Ta skript prinaša še eno novost. Za začetek morate razumeti
ustvarjanje spremenljivke in prirejanje vrednosti v vrstici 2. Gotovo
ste opazili izraz '$(date +%Y%m%d)'; če boste skript tudi pognali,
boste ugotovili, da izvede ukaz med oklepaji in zajame njegov izhod.
<P>
Ime izhodne datoteke tega skripta bo vsak dan drugačno, ker smo za
ustvarjanje imena uporabili ukaz date s predpisano obliko izhoda
(+%Y%m%d). To lahko še nadalje spremenite z drugačnim predpisom
oblike.
<P>
Še nekaj zgledov:
<P>
echo ls
<P>
echo $(ls)
</sect1>

<sect1>
Lokalne spremenljivke
<p>
Krajevne spremenljivke lahko ustvarimo s ključno besedo <it>local</it>.

<tscreen><verb>
#!/bin/bash
HELLO=Hello
function hello {
    local HELLO=World
    echo $HELLO
}
echo $HELLO
hello
echo $HELLO
</verb></tscreen>
<p> Ta zgled nazorno ponazarja uporabo krajevne spremenljivke.
</sect1>

</sect>

<!-- Conditionals -->
<sect>
Pogojni stavki

<P> Pogojni stavki vam omogočajo odločitev o izvajanju niza ukazov glede
na ovrednotenje določenega izraza.

<!-- Dry Theory -->
<sect1>
Teorija
<P> Pogojni stavki so številnih oblik. Najosnovnejša je:
<bf>if</bf> <it/izraz/ <bf>then</bf> <it/stavek/
pri čemer se 'stavek' izvede le, če je 'izraz' ovrednoten kot
resničen. '2<1' je na primer izraz, ki je ovrednoten kot neresničen,
'2>1' pa kot resničen.
<p> Pogojni stavki imajo tudi drugačne oblike, kot na primer:
<bf/if/ <it/izraz/
<bf/then/ <it/stavek1/ <bf/else/ <it/stavek2/.
'stavek1' je v tem primeru izveden le, če je 'izraz' resničen, drugače
pa se izvede 'stavek2'
<P> Še ena oblika pogojnih stavkov:
<bf/if/ <it/izraz1/
<bf/then/ <it/stavek1/
<bf/else if/ <it/izraz2/ <bf/then/ <it/stavek2/
<bf/else/ <it/stavek3/.
Tu je dodan le del "ELSE IF 'izraz2' THEN 'stavek2'", ki izvede
'stavek2', če je 'izraz2' ovrednoten kot resničen. Vse drugo
verjetno razumete (glej prejšnje oblike).
<P> Nekaj besed o sintaksi:
<P> Osnovna zgradba pogojnega stavka v bash je:
<P> if [izraz];
<P> then
<P>     ukazi, če je 'izraz' resničen
<P> fi
</sect1>
<!-- Samples -->
<sect1>
Zgled: Osnovni pogojni stavek if .. then
<P>
<tscreen><verb>
#!/bin/bash
if [ "foo" = "foo" ]; then
   echo izraz je ovrednoten kot resničen
fi
</verb></tscreen>
<P> Ukazi, ki se izvedejo, če je izraz v oglatih oklepajih
ovrednoten kot resničen, so navedeni med 'then' in 'fi' - 'fi'
označuje konec pogojenih ukazov.
</sect1>
<sect1>
Zgled: Osnovni pogojni stavek if .. then ... else
<P>
<tscreen><verb>
#!/bin/bash
if [ "foo" = "foo" ]; then
   echo izraz je ovrednoten kot resničen
else
   echo izraz je ovrednoten kot neresničen
fi
</verb></tscreen>
</sect1>
<sect1>
Zgled: pogojni stavki s spremenljivkami
<P>
<tscreen><verb>
#!/bin/bash
T1="foo"
T2="bar"
if [ "$T1" = "$T2" ]; then
    echo izraz je ovrednoten kot resničen
else
    echo izraz je ovrednoten kot neresničen
fi
</verb></tscreen>
</sect1>
<!--
<sect1>
Sample: testing if a file exists
<P> one more thank to mike
<tscreen><verb>
#!/bin/bash
FILE=~/.basrc
if [ -f $FILE ]; then
    echo file $FILE exits
else
    echo file not found
fi
if [ 'test -f $FILE']
</verb></tscreen>
</sect1>
-->

</sect>

<!-- Loops : for, while and until -->
<sect>
Zanke for, while in until
<!-- Dry Theory -->

<P> To poglavje razlaga zanke for, while in until.
<P> Zanka <bf/for/ se nekoliko razlikuje od take zanke v drugih
programskih jezikih. Omogoča vam zanko, ki se ponovi za vsako
'besedo' v določenem nizu.
<P> <bf/While/ ponavlja ukaze, dokler je nadzorni izraz ovrednoten kot
resničen; ustavi se, ko postane izraz neresničen, oziroma ko naleti na
ukaz za prekinitev zanke.
<P> Zanka <bf/until/ deluje skoraj enako kakor while, le da se
ukazi izvajajo, dokler je nadzorni izraz ovrednoten kot neresničen.
<!-- Samples -->
<sect1>
Zgled zanke for
<P>
<tscreen><verb>
#!/bin/bash
for i in $( ls ); do
    echo beseda: $i
done
</verb></tscreen>
<p>
<p> V drugi vrstici deklariramo spremenljivko i, kateri bomo
pripisali različne vrednosti iz $( ls ).
<p> Tretja vrstica bi bila po potrebi lahko tudi daljša, oziroma bi
se pred 'done' (4) lahko zvrstilo več ukazov.
<p> 'done' (4) pove, da je ukazov, ki so uporabljali $i, konec in da
lahko $i pripišemo novo vrednost.
<p> Ta skript sicer ne počne ničesar koristnega, lahko pa bi mu na
primer naročili, naj izpiše le določene datoteke (glej prejšnji
zgled).
<p>
</sect1>
<sect1>
Zanka for kot v programskem jeziku C
<P> Ta zanka je bolj podobna zanki for v C/perl.
<tscreen><verb>
#!/bin/bash
for i in `seq 1 10`;
do
    echo $i
done
</verb></tscreen>
</sect1>
<!-- while -->
<sect1>
Zgled zanke while
<P> <tscreen><verb>
#!/bin/bash
STEVEC=0
while [ $STEVEC -lt 10 ]; do
    echo Stevec kaze $STEVEC
    let STEVEC=STEVEC+1
done
</verb></tscreen>
<P>
<P> Ta skript 'posnema' dobro znano strukturo 'for' (C, pascal,
perl itd.)
</sect1>
<!-- until -->
<sect1>
Zgled zanke until
<P> <tscreen><verb>
#!/bin/bash
STEVEC=20
until [ $STEVEC -lt 10 ]; do
    echo STEVEC $STEVEC
    let STEVEC-=1
done
</verb></tscreen>
</sect1>
</sect>

<!-- Functions -->
<sect>
Funkcije
<P> Tako kakor v skoraj vsej programskih jezikih lahko tudi tu združite dele
programa v funkcije - tako bolj smiselno organizirate program ali pa
vadite umetnost rekurzije.
<P> Deklaracija funkcije je na moč preprosta: function moja_funkcija {
moji_ukazi }.
<P> Funkcijo kličete tako, kakor bi bila drug program; samo napišete
njeno ime.
<P>
<sect1>
Zgled funkcij
<P> <tscreen><verb>
#!/bin/bash
function izhod {
    exit
}
function hello {
    echo Hello!
}
hello
izhod
echo foo
</verb></tscreen>
<P> Vrstice 2-4 opisujejo funkcijo 'izhod', vrstice 5-7 pa
funkcijo 'hello'. Če niste popolnoma prepričani, kaj naredi ta
skript, kar poskusite!
<P> Kot vidite, funkcij ni treba deklarirati v kakem
posebnem vrstnem redu.
<P> Ko boste skript pognali, bo ta najprej klical funkcijo
'hello', nato funkcijo 'izhod'. Vrstice 10 skript ne bo dosegel
nikoli.
</sect1>

 <sect1>
Zgled funkcije s parametri
<P>
<tscreen><verb>
#!/bin/bash
function izhod {
    exit
}
function e {
    echo $1
}
e Hello
e World
izhod
echo foo
</verb></tscreen>
<P>
Ta skript je skoraj popolnoma enak prejšnjemu. Poglavitna razlika je
funkcija 'e', ki izpiše svoj prvi parameter. Parametri, ki jih podamo
funkciji, so obravnavani enako kakor parametri, podani skriptu.
</sect1>


</sect>


<!-- User interfaces -->
<sect>
Uporabniški vmesniki
<!-- Simple menus with select -->
<sect1>
Uporaba select za ustvarjanje preprostih menujev
<P>
<tscreen><verb>
#!/bin/bash
IZBIRE="Pozdrav Izhod"
select opt in $IZBIRE; do
   if [ "$opt" = "Izhod" ]; then
       echo koncano
       exit
   elif [ "$opt" = "Pozdrav" ]; then
       echo Hello World
   else
       clear
       echo nedovoljena izbira
   fi
done
</verb></tscreen>
<P>
Če boste pognali ta skript, boste ugotovili, da programerji sanjajo o tako
preprostem načinu ustvarjanja tekstnih menujev. Konstrukcija je na moč
podobna 'for', le da namesto izvedbe seznama ukazov povpraša uporabnika za
vsako 'besedo' v $IZBIRE.
<P>
</sect1>
<!-- Using the command line -->
<sect1>
Uporaba parametrov iz ukazne vrstice
<P>
<tscreen><verb>
#!/bin/bash
if [ -z "$1" ]; then
    echo uporaba: $0 imenik
    exit
fi
IZVIRNA_MAPA=$1
CILJNA_MAPA="/var/varnostne_kopije/"
DATOTEKA=home-$(date +%Y%m%d).tgz
tar -cZf $CILJNA_MAPA$DATOTEKA $IZVORNA_MAPA
</verb></tscreen>
<P>
Kaj naredi ta skript, bi vam moralo biti jasno. Izraz v prvem
pogojnem stavku preveri, ali je program dobil parameter ($1). Če ga ni,
se skript konča z izpisom navodila za uporabo.
Preostanek skripta verjetno razumete.
</sect1>

</sect>

<!-- Misc -->
<sect>
Razno
<!-- Reading user input  -->
<sect1>
Branje uporabnikovega vnosa z read
<P>
Gotovo boste kdaj želeli, da uporabnik kaj
vpiše. Tole je eden od možnih načinov:
<tscreen><verb>
#!/bin/bash
echo Vnesite svoje ime
read IME
echo "Zdravo, $IME!"
</verb></tscreen>
<P>
Z read lahko dobite tudi več vrednosti hkrati:
<tscreen><verb>
#!/bin/bash
echo Vnesite svoje ime in priimek
read IME PRIIMEK
echo "Zdravo, $PRIIMEK $IME!"
</verb></tscreen>

</sect1>

<!-- Arithmetic evaluation  -->
<sect1>
Računanje
<P> V ukazni vrstici poskusite tole:
<P> echo 1 + 1
<P> Če ste pričakovali, da boste dobili '2', boste nekoliko
razočarani. Kaj storiti, če želite, da vam BASH pomaga izračunati
nekaj računov? Rešitev je taka:
<P> echo $((1+1))
<P> Ta ukaz bo dal bolj 'smiseln' izpis. Isto lahko dosežete tudi
takole:
<P> echo $[1+1]
<P>
<P> Če so v vaših računih ulomki ali težja
matematika, lahko za računanje uporabite program bc.
<P> Če na primer v ukazni vrstici poženete "echo $[3/4]", boste
dobili rezultat 0, ker bash pri izračunih uporablja le cela
števila. Za pravilni rezultat bo treba pognati "echo 3/4|bc -l",
kar bo vrnilo pravilen rezultat - 0,75.
</sect1>


<sect1>
Iskanje bash
<P> Mike (glej Zahvale) v sporočilu piše:
<P> Zgledi vedno uporabljajo #!/bin/bash .. morda bi lahko navedel
navodilo, kako najti bash, če ga ni na tem mestu.
<P> Še najbolje je uporabiti 'locate bash', vendar vsi sistemi
nimajo programa locate.
<P> 'find ./ -name bash' v korenskem imeniku je navadno prav
tako učinkovit.
<P> Mesta, ki jih preverite:
<P>         ls -l /bin/bash
<P>         ls -l /sbin/bash
<P>         ls -l /usr/local/bin/bash
<P>         ls -l /usr/bin/bash
<P>         ls -l /usr/sbin/bash
<P>         ls -l /usr/local/sbin/bash
<P> (več mest se trenutno ne morem domisliti... sicer pa sem bash po
različnih sistemih našel na enem od navedenih mest.
<P> Lahko poskusite tudi 'which bash'.
</sect1>

<!-- Capturing a commands output  -->
<sect1>
Vrnjena vrednost programa
<P> Bash shrani vrnjeno vrednost programa v posebno spremenljivko z
imenom $?.
<P> Naslednji zgled prikazuje, kako ugotoviti vrnjeno vrednost
programa; predpostavljam, da imenika <it/dada/ ni. (Tudi
tole je predlagal mike.)
<tscreen><verb>
#!/bin/bash
cd /dada &> /dev/null
echo vv: $?
cd $(pwd) &> /dev/null
echo vv: $?
</verb> </tscreen>
</sect1>


<!-- Capturing a commands output  -->
<sect1>
Zajemanje izhoda ukaza
 <P> Ta kratki skript izpiše vse tabele iz vseh zbirk podatkov 
(če imate seveda nameščen MySQL). Popraviti morate ukaz mysql, da
bo v njem veljavno uporabniško ime in geslo.
<tscreen><verb>
#!/bin/bash
DBS=`mysql -uroot  -e"show databases"`
for b in $DBS ;
do
    mysql -uroot -e"show tables from $b"
done
</verb></tscreen>
</sect1>

<!-- Multiple source files -->
<sect1>
Več izvirnih datotek
<P> Več izvirnih datotek lahko uporabite z ukazom source.
<P> __TO-DO__
</sect1>
</sect>


<!-- Tables -->
<sect>
Tabele
<!-- String operators -->
<sect1>
Primerjalni operatorji za nize

<P> (1) niz1 = niz2
<P> (2) niz1 != niz2
<P> (3) niz1 < niz2
<P> (4) niz1 > niz2
<P> (5) -n niz1
<P> (6) -z niz1
<P>
<P> (1) niz1 je enak niz2
<P> (2) niz1 ni enak niz2
<P> (3) __TO-DO__
<P> (4) __TO-DO__
<P> (5) niz1 ni prazen (v njem je en ali več znakov)
<P> (6) niz1 je prazen
</sect1>

<sect1>
Primerjave nizov
<p> Primerjanje dveh nizov
<tscreen><verb>
#!/bin/bash
NIZ1='niz'
NIZ2='Niz'
if [ $NIZ1=$NIZ2 ];
then
    echo "NIZ1('$NIZ1') je enak NIZ2('$NIZ2')"
fi
if [ $NIZ1=$NIZ1 ];
then
    echo "NIZ1('$NIZ1') je enak NIZ1('$NIZ1')"
fi
</verb></tscreen>
<p> Andreas Beck je v svojem sporočilu predlagal uporabo <it/if [ $1 = $2 ]/.
<p> To ni pretirano dobra zamisel, ker boste, če je $NIZ1 ali
$NIZ2 prazen, deležni sporočila o napaki. Bolje je uporabiti x$1=x$2
ali "$1"="$2"

</sect1>

<!-- Arithmetic operators -->
<sect1>
Aritmetični operatorji
<P> +
<P> -
<P> *
<P> /
<P> % (ostanek)
</sect1>

<!-- Arithmetic relational operators -->
<sect1>
Primerjalni aritmetični operatorji
<P> -lt (&lt;)
<P> -gt (&gt;)
<P> -le (&lt;=)
<P> -ge (&gt;=)
<P> -eq (==)
<P> -ne (!=)
<P> Če znate programirati v C, preprosto izberite operator, ki
ustreza izbranemu operatorju v oklepajih.
</sect1>


<!-- Useful commands  -->
<sect1>
Priročni ukazi
<P>
To poglavje je spet napisal Kees (glej Zahvale).
<P>
Nekateri izmed navedenih ukazov so že sami po sebi skoraj pravi
programski jeziki, zato bo o njih povedano le najosnovnejše. Če
boste želeli bolj poglobljen opis, si lahko ogledate njihove
priročnike (man pages).

<bf/sed/ (stream editor - urejevalnik toka)

<P>
Sed je neinteraktivni urejevalnik. Datoteke ne urejate s premikanjem
kazalca po zaslonu, temveč sedu podaste skript navodil za urejanje
in ime datoteke. Sed bi lahko opisali tudi kot filter. Oglejmo si
nekaj zgledov:

<tscreen><verb>
$sed 's/za_zamenjavo/zamenjava/g' /tmp/nekaj
</verb></tscreen>
<P>
Sed zamenja niz 'za_zamenjavo' z nizom 'zamenjava', pri čemer bere iz
datoteke /tmp/nekaj. Rezultat bo poslan standardnemu izhodu (navadno
konzola), lahko pa seveda na konec ukaza dodaste '&gt;zajeto', kar bo
preusmerilo izhod v datoteko 'zajeto'.

<tscreen><verb>
$sed 12, 18d /tmp/nekaj
</verb></tscreen>

<P>
Sed bo izpisal vse vrstice razen 12. in 18. Izvirne datoteke ta
ukaz ne spremeni.

<bf/awk/ (spreminjanje podatkovnih datotek, iskanje ter obdelava
besedila)

<P>
na voljo so številne izvedbe programskega jezika AWK (najbolj znana
interpreterja sta GNU gawk in 'new awk' mawk). Načelo je
preprosto: AWK išče določen vzorec in izvede niz ukazov, ko ga najde.
<P>
Ustvaril sem preskusno datoteko 'nekaj', ki obsega naslednje vrstice:
<P> <it/"test123/
<P> <it/test/
<P> <it/tteesstt"/

<tscreen><verb>
$awk '/test/ {print}' /tmp/nekaj
</verb></tscreen>
<P>
test123
<P>
test
<P>
Vzorec, ki ga AWK išče, je 'test', ukaz, ki ga izvede, ko v datoteki
/tmp/nekaj najde vrstico s tem vzorcem, pa 'print'.

<tscreen><verb>
$awk '/test/ {i=i+1} END {print i}' /tmp/nekaj
</verb></tscreen>

<P> 3

<P> Če iščete številne vzorce, je pametno zamenjati besedilo
med narekovaji z '-f datoteka.awk' in napisati vzorce ter ukaze v
datoteko 'datoteka.awk'.

<bf/grep/ (izpiše vrstice z iskanim vzorcem)

<P> Na ukaz grep smo že nekajkrat naleteli v prejšnjih poglavjih, ko je bilo
treba izpisati vrstice z iskanim vzorcem. Vendar grep zmore še več.
<tscreen><verb>
$grep "iščemo tole" /var/log/messages -c
</verb></tscreen>
<P> 12
<P> Niz "iščemo tole" se je v datoteki /var/log/messages ponovil 12-krat.

<P> [priznam, ta zgled ni popolnoma resničen - malce sem priredil
/var/log/messages :-)]

<bf/wc/ (prešteje vrstice, besede in znake)

<P> Ta zgled ne izpiše točno tistega, kar bi pričakovali. V uporabljeni
preskusni datoteki je naslednje besedilo:
<it/"uvod v bash/
<it/ testna datoteka"/

<tscreen><verb>
$wc --words --lines --bytes /tmp/nekaj
</verb></tscreen>

<P>
1 5 28 /tmp/nekaj
<P>
Wc se za vrstni red podanih parametrov ne zmeni, temveč izpiše
statistike vedno enako: &lt;vrstice&gt; &lt;besede&gt; &lt;znaki&gt;
&lt;datoteka&gt;.

<bf/sort/ (razvrsti vrstice besedila)

<P> To pot je v preskusni datoteki naslednje besedilo:
<P> <it/"b/
<P> <it/c/
<P> <it/a"/
<tscreen><verb>
$sort /tmp/nekaj
</verb></tscreen>

<P> Izpis je videti takole:

<P> <it/a/
   <P> <it/b/
<P> <it/c/

<P> Ukazi ne bi smeli biti tako enostavni :-)

<bf/bc/ (programski jezik za računanje)

<P> Bc lahko prebere račune iz datoteke, podane v ukazni vrstici, ali pa
prek uporabniškega vmesnika. Ta zgled prikazuje nekaj ukazov.
<P> Navadno bc zaženem s parametrom -q, ki prepreči izpis pozdravnega
sporočila.

<tscreen><verb>
$bc -q
</verb></tscreen>

<P> <it/1 == 5/
<P> <it/0/
<P> <it/0.05 == 0.05/
<P> <it/1/
<P> <it/5 != 5/
<P> <it/0/
<P> <it/2 ^ 8/
<P> <it/256/
<P> <it/sqrt(9)/
<P> <it/3/
<P> <it/while (i != 9) {/
<P> <it/i = i + 1;/
<P> <it/print i/
<P> <it/}/
<P> <it/123456789/
<P> <it/quit/

<bf/tput/ (inicializacija terminala ali poizvedba zbirke podatkov
terminfo)

<P> Manjša demonstracija zmožnosti programa tput:
<tscreen><verb>
$tput cup 10 4
</verb></tscreen>
<P> Pozivnik se prikaže na (y10,x4)
<tscreen><verb>
$tput reset
</verb></tscreen>
<P> Počisti zaslon, pozivnik pa se prikaže na (y1,x1). (y0,x0) je zgornji
levi kot zaslona.
<tscreen><verb>
$tput cols
</verb></tscreen>
<it/80/
<P> Izpiše število znakov terminala v smeri osi x.
<P> Toplo vam priporočam, da se dobro seznanite vsaj s temi programi. Seveda
pa je še množica drugih majhnih programov, s katerimi v ukazni
vrstici lahko izvajate prave čarovnije.
<P> [nekaj zgledov v tem poglavju je vzetih iz priročnikov in pogostih vprašanj]
</sect1>
</sect>


<!-- More Scripts -->
<sect>
Še več skriptov
<sect1>
Izvajanje ukaza na vseh datotekah v imeniku.
<P>
</sect1>
<sect1>
Primer: Preprost skript za varnostno kopijo (še nekoliko boljši)
<P>
<tscreen><verb>
#!/bin/bash
IZVORNA_MAPA="/home/"
CILJNA_MAPA="/var/varnostne_kopije/"
DATOTEKA=home-$(date +%Y%m%d).tgz
tar -cZf $CILJNA_MAPA$DATOTEKA $IZVORNA_MAPA
</verb></tscreen>
<P>
</sect1>
<sect1>
Preimenovalnik datotek
<P>
<tscreen><verb>
#!/bin/sh
# renna: preimenuje več datotek po določenih pravilih
# napisal felix hudson Jan - 2000

# najprej preverimo za različne 'načine', ki jih program ima.
# če prvi argument ($1) ustreza pogoju, izvedemo določen del
# programa ter nato končamo izvajanje skripta

# preverimo, ali gre za primer predpone
if [ $1 = p ]; then

# zdaj se znebimo spremenljivke za način ($1) ter predpone ($2)
   predpona=$2 ; shift ; shift

# hiter preskus, ki preveri, ali so bila podana imena datotek
# če niso bila, je bolje, da ne storimo ničesar, kakor 
# da bi poskušali preimenovati datoteke, ki jih ni!!

   if [$1 = ]; then
      echo "datoteke niso bile podane"
      exit 0
   fi

# ta zanka se ponovi za vsako datoteko, ki je bila podana
# skriptu, ter preimenuje eno naenkrat
   for datoteka in $*
     do
     mv ${datoteka} $predpona$datoteka
   done

# tu končamo izvajanje
   exit 0
fi

# preverimo, ali gre za preimenovanje končnice
# ta del je skoraj enak prejšnjemu, zato nima zaznamkov
if [ $1 = k ]; then
   koncnica=$2 ; shift ; shift

   if [$1 = ]; then
     echo "datoteke niso bile podane"
     exit 0
   fi

   for datoteka in $*
   do
     mv ${datoteka} $datoteka$koncnica
   done

   exit 0
fi

# preverimo, ali gre za zamenjavo vzorca
if [ $1 = z ]; then

   shift

# tale del sem dodal zato, da takrat, ko uporabnik ne
# navede parametrov, ne poškodujemo datotek

   if [ $# -lt 3 ] ; then
     echo "uporaba: renna z [izraz] [zamenjava] datoteke... "
     exit 0
   fi

# odstranimo druge parametre
   STARO=$1 ; NOVO=$2 ; shift ; shift

# ta zanka se ponovi za vsako datoteko, ki je bila podana
# skriptu, in jo preimenuje z uporabo programa 'sed', ki v
# besedilu iz standardnega vhoda poišče izraz in ga zamenja z
# drugim. Tu mu na standardni vhod podamo ime datoteke.

   for datoteka in $*
   do
     NOVO_IME=`echo ${datoteka} | sed s/${STARO}/${NOVO}/g`
     mv ${datoteka} $NOVO_IME
   done
   exit 0
fi

# če smo prišli do sem, to pomeni, da programu ni bil podan
# noben parameter, zato uporabniku povemo, kako se ga uporablja

echo "uporaba;"
echo " renna p [predpona] datoteke.."
echo " renna k [končnica] datoteke.."
echo " renna z [izraz] [zamenjava] datoteke.."
exit 0

# končano!
</verb></tscreen>
</sect1>
<sect1>
Preimenovalnik datotek (preprost)
<p>
<tscreen><verb>
#!/bin/bash
# preimenuj.sh
# enostaven preimenovalnik datotek

kriterij=$1
izraz=$2
zamenjava=$3

for i in $( ls *$izraz* );
do
   datoteka=$i
   novo_ime=$(echo $i | sed -e "s/$izraz/$zamenjava/")
   mv $datoteka $novo_ime
done
</verb></tscreen>
</sect1>
</sect>

<sect>
Ko gre kaj narobe (razhroščevanje)
<sect1>
Načini klicanja BASH
<p>
V prvo vrstico skripta napišite
<tscreen><verb>
#!/bin/bash -x
</verb></tscreen>
<P> To bo pri izvajanju izpisalo nekaj koristnih informacij.
</sect1>
</sect>

<sect>
O tem spisu
<P> Sporočite mi vaše predloge/popravke, oziroma kaj bi radi videli
v tem spisu. Poskusil ga bom posodobiti, kakor hitro bo mogoče.
<sect1>
(brez) jamstva
<P> Ta spis ne jamči ničesar o ničemer. In tako naprej.
</sect1>
<sect1>
Prevodi
<p> Italijanski: William Ghelfi (wizzy at tiscalinet.it)
        <htmlurl name="je tukaj" url="http://web.tiscalinet.it/penguin_rules">
<p> Francoski: Laurent Martelli
        <htmlurl name="je neznano kje" url="http://">
<p> Korejski: Minseok Park
        <htmlurl url="http://kldp.org" name="http://kldp.org">
<p> Korejski: Chun Hye Jin
        <htmlurl url="" name="unknown">
<p> Slovenski: Andrej Lajovic (andrej.lajovic at guest.arnes.si)
        <htmlurl url="http://www.lugos.si/delo/slo/HOWTO-sl/Bash-Prog-Intro-HOWTO-sl.html" name="http://www.lugos.si/delo/slo/HOWTO-sl/Bash-Prog-Intro-HOWTO-sl.html">
<p> Španski: neznan
        <htmlurl url="http://www.insflug.org" name="http://www.insflug.org">
<p> Domnevam, da je na voljo še več prevodov, vendar o njih nimam
podatkov. Če veste za katerega, mi, prosim, sporočite.
</sect1>
<sect1>
Zahvale
<P>
<itemize>
<item> Vsem, ki so ta spis prevedli v druge jezike (prejšnje
     poglavje)
<item> Nathanu Hurstu za številne popravke,
<item> Jonu Abbottu za pripombe o aritmetičnih izrazih,
<item> Felixu Hudsonu za skript, <it/renna/
<item> Keesu van den Broeku za številne popravke ter vnovično pisanje
       poglavja o uporabnih ukazih,
<item> Mike (pink) je imel nekaj predlogov o iskanju bash in
       preverjanju datotek,
<item> Fieshu za predlog pri poglavju o zankah,
<item> Lion je predlagal, naj omenim pogosto napako (./hello.sh: Command not found.),
<item> Andreasu Becku za nekaj popravkov in komentarjev.
</itemize>
</sect1>
<sect1>
Zgodovina
<p> Dodani novi prevodi in nekaj manjših popravkov.
<p> Dodal poglavje o uporabnih ukazih, ki ga je znova napisal Kess.
<p> Upošteval še nekaj popravkov in predlogov.
<p> Dodani zgledi pri primerjavi nizov.
<p> v0.8 opustil različice, domnevam, da je datum dovolj.
<p> v0.7 Popravki ter napisanih nekaj starih TO-DO.
<p> v0.6 Manjši popravki.
<p> v0.5 Dodal poglavje o preusmeritvi.
<p> v0.4 je izginila s svojega mesta zaradi mojega nekdanjega šefa
    in ta spis je našel mesto tam, kjer mora biti:
    www.linuxdoc.org.
<p> prej: ne spominjam se, poleg tega nisem uporabljal ne rcs ne
    cvs :(
</sect1>

<sect1>
Še več virov
<P>
<P> Uvod v bash (pod BE)
    <htmlurl url="http://org.laol.net/lamug/beforever/bashtut.htm"
      name="http://org.laol.net/lamug/beforever/bashtut.htm">
<P> Bourne Shell Programming
    http://207.213.123.70/book/
</sect1>
</sect>

</article>
