Beveztetés a shell-scriptek csodálatos világába

©1998 Balázs-Csíki László
  1. Bevezetés
  2. Scriptek futtatása
  3. Shell változók
  4. Parancssor-argumentumok
  5. Interaktív scriptek
  6. Vezérlési szerkezetek
    1. if-else
    2. for ciklus
    3. while és until
    4. case
  7. Tippek és trükkök


Bevezetés.

Egy shell script nem más mint shell-parancsok sorozata, amelyeket az újrafelhasználás jegyében egy file-ba írunk. Mivel a shell programozható, shell scriptjeinkkel nagyon hasznos programokat írhatunk megdöbbentôen könnyen, ráadásul nem kell lefordítani ôket és könnyedén debuggolhatók!

Unixon van egy csomó shell, és ennek megfelelôen sokféle shell-ben írhatjuk meg scriptjeinket. Ennek ellenére nem kell zavarba jönnünk a bôségtôl, ugyanis sh-ban érdemes írni scriptjeinket, hogy olyan Unixokon is fussanak ahol esetleg csak ez van. A továbbiakban alapvetôen a Bourne shell programozását fogjuk taglalni, és minden script file elején a

#!/bin/sh
sorral jelezzük ezt. Megjegyzés: hallani olyasmiket, hogy a sh-család (sh, ksh, bash, zsh) alkalmasabb a scriptek írására mint a csh-család (csh, tcsh). Ez balga elôítélet. KIZÁRÓLAG azért érdemes sh-t használni, mert az mindenhol van. (Legtöbbször a ksh/bash/zsh nem nyújt lényeges elônyöket a Bourne-shellhez képest a script-programozáshoz, miként a tcsh sem a csh-hoz képest. Interaktív shellnek persze hülyeség volna sh/csh-t használni - erre valók a többiek. Az is elôfordulhat, hogy zsh-ban egy interaktív sorban meg tudod csinálni azt, amire sh-ban nemtriviális scriptet kellene írnod) Mellesleg Linuxon sh többnyire csak egy link a bash-ra, csh pedig a tcsh-ra. A bash egy kicsit másképp viselkedik, ha sh-ként hívod, de így is elfogad olyasmiket, amik az igazi sh-ban nincsenek meg. Linuxon ash-val lehet tesztelni, ha a kompatibilitás probléma (ash a legprimitívebb Bourne-kompatibilis shell). Ja, és a ksh-t pdksh néven lehet beszerezni.

A shell scriptek filozófiája általában az adatfolyam különbözô hasznos szûrôkön való átfolyatásan alapszik. Nézzük például, hogyan irathatjuk ki a nevünket (nem az júzert, az igazit):

#!/bin/sh
juzername=`whoami`
igaziname=`grep "^${juzername}:" /etc/passwd | cut -d: -f5 | cut -d, -f1`
echo A neved: $igaziname
A fenti példában megfigyelhettük a command substitution nevû pompás mechanizmust, ami azt csinálja, hogy a ` jelek közé (nem egyszeres idézôjel, nézz csak körül a billentyûzeteden!) tett parancs által kiírt string lesz a megfelelô változó értéke. Nem kell feltétlenül scripteket írni, ha a shell programozhatóságát használni akarjuk. Tegyük fel például, hogy van egy rakás PostScript fájlunk egy directoryban es sorra meg szeretnénk nézni ôket. Fárasztó es unalmas lenne minden alkalommal File/Open-re kattintgatni es utána kiválasztani a következôt vagy minden fájlra manuálisan újraindítani a gv-t. Okos ember ilyenkor a következôt írhatja a zsh parancssorba:
$ for i in *.ps
for > gv $i
vagy:
for i (*.ps) gv $i
Amikor lelövi az egyik gv-t (az egyszerû, csak egy q-t kell nyomni), automatikusan jön a következô. Sh/ksh/bash -ban kissé bonyolultabb:
$ for i in *.ps
> do
> gv $i
> done
A fenti persze zsh-ban is mûködik. Csh/tcsh-ban:
$ foreach i (*.ps)
foreach? gv $i
foreach? end


Futtatás

Egy scriptet többféleképp lehet futtatni: (1) ha egy pontot (vagy a source parancsot) teszel a filenév elé, akkor az aktuális shell soronként olvassa be a file-t, es a hatás ugyanaz, mintha te gépelted volna be ezeket a sorokat, (2) ha futtátasi jogot adsz a file-ra (chmod +x filenev), akkor egy új shell indul a script futtatására, akárcsak akkor ha (3) expliciten egy új shellnek adod at paraméterként.

A második módszer a legjobb általában: a script parancsként viselkedik és mivel külön process lesz, mindenféle dolgokat csinálhasz vele: a háttérben indíthatod (ha utána teszel egy & jelet), felfüggesztheted (Ctrl-Z), újraindíthatod a háttérben (bg) illetve az elôtérben (fg).

A futtattható scriptjeid legjobb helye a home directoryd bin/ konyvtárában van. Ellenôrizd, hogy a bin/ konyvtárad benne van-e a keresési útvonalban.(echo $PATH)


Shell változók

Egy változónak a következôképpen adhatsz érteket (sh, ksh, bash, zsh):

valtozo=ertek
illetve (csh, tcsh):
set valtozo=ertek
A változó értekét mindegyik shellben ugyanúgy nyerheted ki, mégpedig a dollárjellel:
echo $valtozo


Parancssor-argumentumok

A scripted parancssor-argumentumokat is kaphat, amelyeket ô elôredefiniált változóként érzékel; minden további magyarázat helyett álljon itt egy példa:
#!/bin/sh
echo a parancs neve: $0
echo az elsô argumentum: $1
echo a masodik argumentum: $2
echo a harmadik argumentum: $3
echo az argumentumok szama: $#
echo az osszes argumentum: $*


Interaktív scriptek

A következô példa mutatja, hogy hogyan lehet inputot kérni a script felhasználójától a read parancs segítségével:
#!/bin/sh

echo -n 'filename: '
read filename

echo filename will be $filename
if test -e $filename
then
	echo File $filename already exists. Exiting...
	exit 1
fi


Vezérlési szerkezetek

  1. if-else
  2. for ciklus
  3. while és until
  4. case

If-else

Feltételes elágazást a következô módon csinálhatunk:

 
if  feltétel 
then
	  parancsok (statements) 
fi
Az if az ôt követô parancsok közül az utolsónak az exit statusát nézi, ha az nulla (vagyis a parancs sikeresen futott), akkor "igaz"-nak veszi, különben "hamis"-nak. (Megjegyzés: a C programozási nyelvben ez pont fordítva van: a nulla felel meg az "hamis"-nak, minden más meg az "igaz"-nak).

Például:

#!/bin/sh
if who | grep haver > /dev/null
then
	echo A haver be van jelentkezve
fi
A grep eredményét átirányítottuk a /dev/null -ba (vagyis átadtuk az enyészetnek), mivel csak a grep exit statusa érdekel bennünket: szerepel a "haver" string a who parancs által kiírt sorokban vagy nem.

Az if-et értelemszerûen és a más programozási nyelvekben megszokott módon ki lehet egészíteni else és elif szerkezetekkel ( az utóbbi az else if-nek felel meg). Az elôzô példa egy általánosított változata:

#!/bin/sh

if test $# -eq 0 
then
	echo Használat: arguentumként egy juzernevet kérek.
	exit 1
fi

haver=$1

if who | grep $haver > /dev/null
then
	echo $haver be van jelentkezve
else
	echo $haver nincs bejelentkezve 
fi
A fenti példában láttunk egy igen hasznos paracsot, amelynek segítségével különféle feltételek meglétét tesztelhetjük (ezert is hívják testnek...). Lásd még: man test.

Végül megjegyzem, hogy if-szerû viselkedést if nélkül is elérhetünk: ha két parancs közé a && jelet tesszük, akkor a második csak akkor hajtódik végre ha az elsô a 0 visszatérési értékkel jelzi sikeres futását:

 
latex szoveg.tex && xdvi szoveg.dvi
vagy:
gcc -o proba proba.c && proba
A || jel esetében pont fordítva, akkor hajtódik végre a második, ha az elsô nem sikerül, például:
szukszavu_parancs || echo "Valami nem stimmel" >&2

For ciklus.

A for ciklus némiképp különbozik a Pascal es C nyelvek for ciklusától, mégpedig abban, hogy csak egy rögzített lista elemein lehet végigmenni vele. Tipikus felhasználása, hogy bizonyos kiterjesztésû file-okon vagy a parancssor argumentumain megyünk végig.

Például a következô script azokat a txt kiterjesztésû file-okat, amikben a "font" string szerepel egy directoryval feljebb mozgatja:

#!/bin/sh
for i in *.txt 
do
	if grep font $i > /dev/null
	then 
		mv $i ..
	fi
done

While és until.

A while ciklus addig fut, amíg a hasában levô feltétel igaz, az until pedig pont fordítva: amíg a feltétele igaz nem lesz. Mindkét esetben do és done közé kell zárni a ciklus parancsait.
A következô script addig irogatja ki kétmásodpercenként üzenetét, amíg le nem lövöd:

#!/bin/sh

while true
do
	echo itt vagyok, ragyogok
	sleep 2
done
Mellesleg a C nyelvhez hasonlóan, minden ciklusból break-al törhetünk ki, és continue-val ugorhatunk a ciklus elejére.

Case.

A case elágazást a következô példán tanulmányozd!
#!/bin/sh

case $1 in
"-a")
	echo "-a opcio eseten ezt irom ki"
	;;
"-o")
	echo "-o opcio eseten ezt irom ki"
	;;
*)
	echo Egyebkent meg ez a default.
esac


Tippek és trükkök.

0. tipp.


Legtöbbet a profik altal gyártott scriptekbôl tanulhatunk. Sok Unix parancs tulajdonképpen shell-script: a /bin vagy /usr/bin vagy /sbin/ vagy a /usr/sbin directorykban a
 
file * | grep script 
parancsal szelektálhatjuk ki a scripteket.

1. Usage

Ha scripted parancssor-argumentumokat igényel, akkor írhatsz egy "usage" függvényt (Ja, nem is mondtam, hogy függvényeket is definiálhatsz, most figyeld meg a módjat!), például:

#!/bin/sh

usage() {
	cat << EOM
	Ejnye-bejnye, ilyen meg ilyen paramétereket kellett volna adjál!
	Használat: $0
		-k kezdôknek
		-h haladóknak
	Olvasd el a man-paget is!
EOM
}

if test $# -eq 0
then
    usage
fi

2. Shell-scriptek debuggolása

Azon a kézenfekvô módszeren kívül, hogy megszórod egy rakás echo-val a forrást, más módszerek is vannak a debuggolásra:
sh -v filename
az egyes parancsok futtatása elött kiírja ôket (automatikus echo...)
sh -x filename
még több információt közöl az egyes parancsok eredményérôl és a változókról,
sh -n filename
esetén nem hajtódnak végre a parancsok, csak szintaktikusan ellenôrzödik a dolog


Visszajelzéseket
ezen a címen várok türelmetlenül...