Shell Scripting
previous.png
Cursus Linux       Advanced       Shell Scripting   
gnu.png


up.png Shell Scripts down.png

In dit hoofdstuk leeft men hoe een shell-script is opgebouwd. Het voornaamste doel van dit hoofdstuk is dat men shell-scripts leert lezen om te achterhalen wat er gebeurt tijdens de uitvoering ervan. Men heeft deze kennis nodig om problemen op te lossen die zich kunnen voordoen tijdens het opstarten van de computer, of bij installatie van programma's op het systeem. In beide gevallen wordt namelijk veelvuldig gebruik gemaakt van shell scripts. Een tweede doel van dit hoofdstuk is de student zover te brengen dat hij in staat is een eenvoudig shell script te schrijven. Na het doorworstelen van dit hoofdstuk is men geen voleerd programmeur van shell-scripts, daarvoor is veel programmeer ervaring nodig.

Wat leren we in dit hoofdstuk:

up.png 1 Inleiding down.png

Een shell script is een bestand met instructies die door de shell (in het geval van Linux bijna altijd bash) gelezen en begrepen moeten worden. Naast het eenvoudig na elkaar uitvoeren van opdrachten is het ook mogelijk om met shell scripts opdrachten uit te voeren als aan bepaalde voorwaarden is voldaan.

De script taal die in -bash- is ingebouwd is echter zo krachtig dat het mogelijk is om met bash een volledig functionerend programma te schrijven, zeker als dat gebeurt in combinatie met standaard UNIX programma's zoals grep, cut, sort en tr. Niet altijd wordt -bash- als de interpreterende shell gekozen, omwille van compatibiliteit met UNIX wordt zeer dikwijls de -sh- shell gekozen.

up.png Zelf shell-scripts ontwikkelen down.png

Als men de Linux opdrachten goed beheerst kan men toveren. Stelt U zich voor dat men meerdere van de linux opdrachten naar keuze kan combineren, wat kan men dan wel allemaal met het systeem doen. Daarom in dit hoofdstuk ruim baan voor de mogelijkheden die men met shell-scripts kan bereiken. Een waarschuwing is echter wel op zijn plaats, na het doornemen van dit hoofdstuk is men geen volleerd shell-script programmeur.

up.png Bestaande shell-scripts lezen down.png

Een tweede doel van dit hoofdstuk is het leren lezen van shell schripts, immers de meeste opstart procedures, zeker de services, zitten vervat in een shell script. Teneinde opstart problemen te kunnen begrijpen en daarom opstart problemen te kunnen verhelpen moet men shell-scripts kunnen lezen.

up.png 1.1 Scripten of niet scripten ? down.png

Voordat men begint met scripten, is het zaak zich af te vragen of een shell-script de beste oplossing voor het probleem dat men wil oplossen. Vaak zijn er andere oplossingen mogelijk, zoals een perl-script, of een volwaardig c-programma.
De voordelen van een shell-script:

Het nadeel van een shell-script is dat het relatief traag is. Het moet geinterpreteerd worden door de shell waarvoor het is gemaakt. De shell bevindt zich dus altijd tussen het script en de hardware die de uiteindelijke taken moet volbrengen. Dit is niet het geval met bijvoorbeeld een c-programma.
Daar staat tegenover dat een c-programma veel complexer is om te maken en ook veel meer voorkennis verondersteld. Voor veel voorkomende taken is een c-programma toch minder geschikt dan een shell-script, ondanks dat het sneller is, vooral omdat shell scripting wordt toegepast voor relatief simpele taken. De winst die men zou behalen door deze simpele taken te laten uitvoeren door een c-programma is te verwaarlozen.

up.png 1.2 Welke shell ? down.png

Er zijn op Linux zeer verschillende shells die gebruikt kunnen worden. Bij het schrijven van een shell-script moet men hiermee rekening houden. Een script dat ontwikkeld is voor de ene shell werkt daarom niet altijd op een andere shell. Gelukkig is de keuze relatief eenvoudig door het feit dat de bash shell (/bin/bash) de meest gebruikte shell is op Linux. Bash is een afgeleide van de aloude Bourne-shell (/bin/sh) die al in de jaren zeventig op UNIX systemen gebruikt werd. Het leuke daarvan is dat elk script dat voor deze shell is geschreven ook werkzaam zal zijn op de bash shell. Omgekeerd is dat niet het geval omdat Bash features heeft die in de Bourne-shell nog niet voorkwamen. Om deze reden worden shell-scripts dikwijls ontwikkeld voor de Bourne shell. Ze zijn dan compatible met Linux en met UNIX.

Naast Bash zijn er nog veel andere shells die gebruikt kunnen worden. De twee meest bekende daarvan zijn de Korne-shell (/bin/ksh) die onder Linux bekend staat als /bin/pdksh en de C-shell (/bin/csh) die onder Linux bekend staat als /bin/tcsh. De Korne shell zal men op Linux niet vaak tegenkomen, op UNIX is dit echter een veelgebruikte shell. De C-shell is vooral populair bij programmeurs die ook gebruik maken van de programmeer taal C. Beide shells kenmerken zich doordat ze niet compatible zijn met de Bash-shell. Een script dat dus voor de Korne of de C shell is ontwikkeld zal onder Bash niet werken en omgekeerd zal een Bash script niet werken onder Korne of C.

remark.png Om alle beschikbare shells op je systeem te kennen gebruik cat /etc/shells

remark.png Om de huidige gebruikte shell te zien , gebruik echo $SHELL

Dit probleem wordt opgelost door de zogenaamde shebang regel die men meestal als eerste regel in een shell script ziet staan. Wanneer men zelf een script schrijft is het altijd een goed idee om zelf een dergelijke shebang regel te gebruiken. Een shebang regel begint met een commentaar teken, gevolgd door een uitroepteken en de naam van de shell die men wil gebruiken in het script. Wanneer nu het script wordt gestart onder een shell waarvoor het script niet bedoeld is, dan zal de shebang regel ervoor zorgen dat de juiste shell alsnog geladen wordt, de voorwaarde is natuurlijk dat de shell dan ook geinstalleerd moet zijn. Omgekeerd zal een shell-script geschreven onder een ander operating systeem , wel kunnen werken onder Linux, onder voorwaarde dat de shell waarvoor het script bedoeld is op het Linux systeem geinstalleerd is.

shebang.png
Een voorbeeld van een script met een shebang regel (zie regel 1)

up.png 2 Shell scripts editeren down.png

Zoals we vroeger reeds gezien hebben zijn er onder linux een aantal mogelijkheden om ASCII bestanden te editeren. Wanneer nano of pico aanwezig zijn kunnen deze editors gebruikt worden. Indien niet dan zal men moeten vertrouwen op 'vi' of op 'vim' als ASCII editor. Zowel de GNOME als KDE omgeving leveren hun eigen grafische ASCII editor, allebei met een heleboel features die het leven een stuk gemakkelijker maken. Natuurlijk moet de grafische omgeving beschikbaar zijn om dit te kunnen gebruiken, hetgeen op een server niet altijd het geval is.

up.png 2.1 GNOME gedit down.png

De GNOME omgeving bevat 'gedit' als grafische ASCII editor. Wanneer 'gedit' dit bestand herkend als een shell-script dan zal gedit de syntax highlighting voor shell-scripts inschakelen.
Het herkennen van een shell-script gebeurt op twee manieren:

gedit.png

up.png 2.2 KDE kate down.png

De KDE omgeving bevat 'kate' als grafische ASCII editor. Wanneer 'kate' een bestand herkend als een shell-script dan zal kate de syntax highlighting voor shell-scripts inschakelen. Tenminste als deze feature ingesteld staat. Het herkennen van een shell script gebeurt door de extensie, wanneer een script de extensie .sh heeft wordt het herkend als shell script.

kate.png

up.png 3 De basis down.png

In de volgende paragrafen worden de basis elementen besproken zoals die in veel shell scripts voorkomen. Op basis van het gegeven schema en elementen zal men hoogwaardige scripts kunnen schrijven. Men komt de aangegeven onderdelen in vrijwel alle scripts tegen.

up.png 3.1 De structuur van een script down.png

Men is natuurlijk vrij in het neerschrijven van een script om op een willekeurige wijze het script allerlei opdrachten te laten uitvoeren, teneinde het doel van het script te bereiken. In de loop der jaren is er echter een bepaalde ordening en werkwijze ontstaan waardoor men van de structuur van een script kan spreken.
In de meeste gevallen is de structuur van het script als volgt:

Om te voorkomen dat het script te moeilijk (onmogelijk) leesbaar wordt is het een goed idee om ieder afzonderlijk te onderscheiden functioneel blok te voorzien van een commentaar blok.

Hieronder een voorbeeld van een script dat voldoet aan het bovenstaande.
hallo.png
Bovenstaand script voert de eerst opdracht uit , namelijk zet de tekst "Hallo wereld!" op het scherm. Naast de opdracht echo, die de meegegeven tekst op het scherm zet, wordt het script afgesloten met de opdrach exit 0, die de exit-status 0 aan de shell meedeelt. In een eenvoudig script zoals het deze is dat natuurlijk flauwe kul, maar het is een goede gewoonte dit op deze manier te doen. Zodra het script iets ingewikkelder wordt zal de exit 0 code wel zin hebben. De laatste exit-status in de shell kan men raadplegen met echo $?.
Het consequent toepassen van structuur elementen voor een script heeft de volgende voordelen
  • het script wordt gemakkelijk leesbaar
  • het script wordt begrijpelijk voor vreemden
    indien een eigen script bv 4 jaar oud is, is men een vreemde t.o.v. het eigen script
  • het script is gemakkelijk te onderhouden
  • Wanneer men een script leest/onderhoud kan men zich beperken tot dat gedeelte waarmee men bezig is

up.png 3.2 Activeren van een script down.png

Om een zopas geschreven script uit te voeren zijn er verschillende mogelijkheden:

up.png 3.2.1 Aanroepen als een argument van een shell down.png

Als men zonder al te veel handelingen wilt kijken of het script bruikbaar is, kan men het aanroepen als argument van de shell. Concreet betekend dit dat men vanuit de huidige shell en nieuwe shell start en ervoor zorgt dat deze het script uitvoert. Als het script de naam 'hallo' heeft ziet deze opdracht er als volgt uit : bash hallo

up.png 3.2.2 Het script sourcen down.png

Men kan een script ook opstarten met behulp van de source opdracht. Dit is een punt gevolgd door een spatie, gevolgd door de naam van het te sourcen script. Dus voor het script hallo wordt dat dan . hallo. Als alternatief voor de punt kan men ook de interne opdracht 'source' gebruiken, als in source hallo

up.png 3.2.3 Een script uitvoerbaar maken down.png

Om een shell-script als een zelfstandige opdracht te activeren moet het script eerst voorzien worden van de execute-permissies. Men kan dit doen door de opdracht chmod +x naam-van-het-script. (in deze vorm worden de execute permissies voor iedereen geplaats, in een meer beveiligde omgeving zou men de execute permissies bv alleen voor de eigenaar van het script kunnen zetten. Men kan het script nu activeren door de volledige naam van het script op de commando regel te typen en af te sluiten met enter. bv. ~/bin/hallo en enter

up.png 3.2.4 Een script beschikbaar maken in het zoekpad down.png

Als men wil vermijden dat men iedere keer men een script wil activeren men het volledige path van het script moet intikken, ofwel het script activeren door alleen de naam ervan in te tikken, moet het script in het zoekpad geplaats worden. Men kan dit doen door het script te verplaatsen naar een directory die in het zoekpad vermeld staat (om de directories te weten gebruik echo $PATH), of men kan de eigen scripting directory opnemen in de PATH omgevings variabele. Voor een gewone gebruiker zal men meestal een bin directory in de home directory aanmaken als plaats waar de executables van deze gebruiker worden opgeslagen. Deze ~/bin directory wordt dan achteraan in het zoekpad toegevoegd, met bv PATH=$PATH:~/bin

warning.png
Veiligheids risico

  1. Als men de variabele PATH aanpast is het het beste om de bin directory van een gebruiker steeds achteraan in het zoekpad toe te voegen. Immers wanneer de directory vooraan staat kan de gebruiker allerlei gewone opdrachten vervangen door zijn eigen versies ervan.
  2. De opdracht om de bin directory van de gebruiker toe te voegen aan het zoekpad kan het beste gebeuren in ~/.bashrc (of het equivalent ervan). Zodra de gebruiker aanlogt wordt in zijn geval dit zoekpad gelegd en voor niemand anders.
  3. Denk altijd even na over de naam van het script. Zorg ervoor dat het nooit de naam van een bestaande opdracht heeft. Met behulp van whereis gevolgd door de naam die men wil gebruiken kan men dit controleren.
  4. Gebruik bij voorkeur niet de huidige directory (./) om het zoekpad aan te vullen. Dit zou betekenen dat de gebruiker's huidige directory zou doorzocht worden op zoek naar de opdracht. Dit kan tot onverwachte resultaten lijden.
  5. Wil men een systeem onstabiel maken (of meschien kraken) zorg er dan voor dat je eigen huidige directory als eerste in het zoekpad staat, in dat geval heb je altijd voorrang wanneer er opdrachten worden uitgevoerd.

remark.png De subtiliteiten van de subshell
Wanneer men een shell-script start als argument van de shell, dan wordt de nieuwe shell gestart als een subshell. Dit will zeggen dat variabelen gedefinieerd binnen het script niet meer beschikbaar zijn zodra het script wordt afgesloten. Wanneer het script gesourced wordt, dan wordt het geinterpreteerd door de shell zelf, en niet door een subshell. In dit geval zijn de variabelen die in het script gedefinieerd worden wel beschikbaar nadat het script is afgelopen. BEHALVE wanneer het script begint met een shebang regel, in dat geval wordt altijd een subshell gestart (als gevolg van de shebang regel).

Wil je je eigen bin in het zoekpath, ook na een herstart van het scherm, neem dan de definitie van de PATH variabele op in je individueel .bashrc script.

up.png 3.3 Richtlijnen down.png

Als men zelf scripts gaat schrijven, respecteer dan de volgende basis regels in acht:

up.png 4 Werken met variabelen en parameters down.png

In voorgaande delen van deze cursus hebben we reeds kennis gemaakt met omgevings variabelen zoals PATH en PS1 waardoor de werkomgeving van de computer kan worden geoptimaliseerd. In deze paragraaf leert men dat het ook mogelijk is in een script eigen variabelen te definiëren. Daarnaast wordt ingegaan op het werken met de argumenten die men aan een script kan meegeven, de zogeheten positionele parameters. Variabelen zijn erg belangrijk voor het werken met scripts, ze zorgen er namelijk voor dat men de scripts op een flexibele wijze kan gebruiken.

up.png 4.1 Positionele parameters down.png

Alle argumenten die aan een script of een opdracht meegegeven kunnen worden, worden ook wel positionele parameters genoemd. Het eerste argument is positionele parameter nummer 1, het tweede argument is positionele parameter nummer 2, enzo... In de opdracht ls -l /etc is '-l' dus de eerste en '/etc' de tweede positionele parameter. In shell-scripts worden deze positionele parameters aangeduid als $1 en $2. De naam van het script zelf is $0.
Het volgende is een voorbeeld genaamd 'param01'.

param01.png

Als men dit script uitvoert door bijvoorbeeld de opdracht 'param dit is een rijtje parameters', wordt het volgende resultaat gegeven :

param01-1.png

In de uitvoer van het bovenstaande testscript zijn er een aantal dingen die niet juist zijn. Het eerste probleem is dat er geen eenvoudige manier is om alle positionele parameters te laten zien.

Op dezelfde wijze verdergaand kan men natuurlijk het script verbeteren.
Zoals in het volgende voorbeeld:

param02.png

De output is nu verbeterd:

param02-1.png

Het is natuurlijk begrijpelijk dat als men het aantal parameters op de command line wijzigt het script alweer zou moeten aangepast worden om het juiste aantal parameters af te drukken. In de volgende paragraaf word duidelijk hoe dit moet worden opgelost. Bovendien is het raadplegen van positionele parameters met de $X, waarbij X het volgnummer van de parameter is, beperkt tot maximaal 9 parameters.

up.png 4.1.1 Shift down.png

De interne opdracht 'shift' maakt het mogelijk om meer dan negen parameters te gebruiken. Elke keer de opdracht 'shift' wordt uitgevoerd schuiven de positionele parameters één positie op in de richting van de opdracht ($0). Dus de waarde van $2 wordt opgeslagen in $1, de waarde van $3 in $2, enzo... De waarde van de 10'de positionele parameter die niet rechtstreeks kan worden geraadpleegd wordt opgeslagen in $9. De waarde van $1 gaat verloren.
Beschouw het volgende script:

param03.png
Start het voorgaande script met de opdracht param2 a b c d e f g h i j k l m.

param03-1.png

We hebben nu geleerd hoe we meer dan 9 positionele parameters kunnen opvragen met behulp van de opdracht 'shift'. Natuurlijk is het zo dat het voorgaande script niet kan uitgebreid worden tot in het oneindige. Het zou veel gemakkelijker zijn om bv. alleen de $2 te gebruiken en na iedere 'shift' te testen of de variabele $2 nog een waarde heeft. Hoe we dit moeten doen zien we later.

up.png 4.1.2 Speciale parameters down.png

Naast de positionele parameters, die verwijzen naar parameters op specifieke posities op de commmand line, bestaan er nog enkele speciale parameters. Dit zijn tekens met een speciale betekenis, die gebruikt worden om te praten over de positionele parameters van een opdracht. Men kan ze gebruiken om iets te doen met het totaal van alle positionele parameters die u het script gebruikt worden.

speciale parameter betekenis
$# Verwijst naar het aantal positionele parameters
$* Geeft als resultaat een tekenreeks met de waarde van alle parameters. Elke parameter wordt van de vorige gescheiden door het scheidingsteken dat is gedefinieerd als de systeem variabele IFS.
$@ Geeft als output alle parameters waarbij elke parameter als individuele tekenreeks kan worden gebruikt.
$? Bevat de exit waarde van de laatst uitgevoerde opdracht
Ieder programma, script, of opdracht dat door linux uitgevoerd wordt geeft een exit code terug aan de shell die het heeft opgeroepen.
Deze exit code is 0 voor gelukte operaties, en niet 0 voor situaties waarin fouten zijn opgetreden.

Deze vier speciale parameters zijn erg handig om te werken met meerdere parameters. Het volgende script is een demonstratie van de werking van de speciale parameters. De functie 'aantal' demonstreerd het verschil tussen '$*' en '$@'.

param04.png

param04-1.png
Start het script met de opdracht param3 1 2 3 4

up.png 4.1.3 Opdracht substitutie down.png

Positionele parameters kunnen op een speciale manier worden gebruikt in combinatie met de interne opdracht set en opdracht substitutie. Bij opdracht substitutie wordt een waarde aan de positionele parameter toegekend doordat een opdracht vanuit een script wordt aangeroepen. Hierdoor wordt de tot dan bekende waarde van de positionele parameter overschreven. Via opdracht substitutie kunnen positionele parameters dus opnieuw worden gedefinieerd.

Als demonstratie kan men opdracht substitutie ook rechtstreeks toepassen. Beschouw de opdracht cd $(uname -r). De werking van cd is bekend. De opdracht 'uname -r' geeft het versie nummer van de huidige kernel. Hieronder ziet men het resultaat.

param05-1.png

Men kan dit mechanisme van opdracht substitutie ook gebruiken binnen een shell script. Dit blijkt uit het voorbeeld script waarin de opdracht date wordt gebruikt. De opdracht 'date' geeft een tekenreeks als resultaat.

param05.png

In het volgende voorbeeldscript wordt het resultaat van de opdracht 'date' als het ware geëxporteerd.
De tekenreeks 'ma aug 20 18:00:20 CEST 2007' die door de opdracht 'date' is gemaakt, wordt door het script als een reeks positionele parameters gezien.

param05-2.png

Wanneer men het script aanroept met een aantal parameters kan men zien dat de opdracht substitutie de waarden van de originele parameters overschrijft.

param05-3.png

up.png 4.2 Variabelen down.png

In veel shell scripts worden naast positionele parameters ook variabelen gebruikt. Met een variabele wordt een reeks tekens (waarde) op een benoemde plaats (naam) in het geheugen bewaard. In een later stadium kan men in hetzelfde script een bewerking met of op de tekenreeks uitvoeren. Een variabele wordt gedefiniëerd door er simpelweg een waarde aan vast te koppelen. Zoals in:

In de eerste twee voorbeelden wordt een waarde aan de variabele gekoppeld. De waarde kan vervolgens opgeroepen worden door een dollar teken voor de naam ervan te plaatsen. In de derde regel wordt de waarde van een variabele verwijderd.

Zoals reeds gezegd kan de waarde van een variabele worden opgeroepen door een dollar teken voor de naam van de variabele te plaasten. Zoals bv echo $SHELL , toont de waarde van de variabele SHELL. (dat SHELL in hoofdletters is geschreven is een toevallige conventie, zeker geen must). Een lijst van alle variabelen kan opgevraagd worden met set of met env.

Hieronder volgt een screen shot van script 'var01' dat de variabelen VAR1, VAR2, en VAR3 instelt. Vervolgens volgt een afbeelding van de variabelen. VAR3 wordt opnieuw ingesteld met niets en nogmaals een afbeelding van de variabelen.

var01.png

var01-1.png

up.png 4.2.1 Read-only variabelen down.png

Om er zeker van de zijn dat de waarde van essentiële variabelen niet wordt overschreven, kan een variabele read-only worden gezet. Hiervoor wordt de shell opdracht readonly gebruikt. Als een variabele eenmaal als read-only is gemarkeerd, kan de waarde die eraan gekoppeld is niet meer worden gewijzigd. De read-only variabele dient eerst te worden gedefinieerd alvorens hij kan worden gemaakt. Daarna kan men de variabele tegen overschrijven beschermen met de opdracht readonly naam. Readonly is een interne opdracht , meer informatie erover verkrijgt men met help readonly.

De volgende screen shots tonen respectievelijk een test script en het resultaat ervan.

var02.png

var02-1.pngvar02-1.png
Instellen van een readonly variabele geeft alleen maar een error melding.

up.png 4.2.2 Lokale - omgevings variabelen down.png

Alle variabelen die we tot hiertoe hebben gezien zijn alleen beschikbaar voor de shell waarin ze zijn gedefiniëerd. Dit betekent dat wanneer vanuit de shell een subshell wordt gestart, deze variabelen daarin niet beschikbaar zijn. Dit is het normale gedrag; een variabele is beschikbaar in de shell waarin hij is gedefiniëerd, elke variabele is dus in principe een lokale variabele.

De volgende screen-shot toont de gevolgen hiervan:

var03-1.png

Om een variabele ook in subshells beschikbaar te maken moet men er een omgevings variabele van maken. Dit doet men met behulp van de interne opdracht 'export'. Dus om omgevings variabelen te definieren gebruikt men:

De betekenis van de 3 syntax vormen is hetzelfde als voor gewone variabelen, alleen gaat het nu over omgevings variabelen. De volgende screen shot demonstreerd het gebruik hiervan.

var03-2.png

up.png 4.2.3 Shell variabelen down.png

Naast de door gebruikers ingestelde variabelen bestaan er ook variabelen die nodig zijn om de shell op de juiste wijze te laten functioneren. Dit zijn de zogenaamde shell-variabelen, ook wel de systeem-variabelen genoemd. Enkele voorbeelden zijn : PWD, UID, IFS, PATH. Kenmerkend is dat ze meestal in hoofdletters gedefinieerd zijn en dat ze altijd aanwezig zijn. Het zijn altijd omgevings variabelen, ze zijn namelijk in alle subshells beschikbaar.

var04.pngvar04.png

var04-1.pngvar04-1.png

up.png 4.3 Werken met invoer down.png

Een nuttig shell script is niet alleen in staat gegevens te verwerken die als positionele parameters zijn meegegeven, maar moet ook in staat zijn tijdens de uitvoering van het shell-script gegevens te kunnen opvragen en verwerken. Vooral installatie scripts maken gebruik van invoer, omdat sommige zaken nu eenmaal niet altijd op voorhand kunnen worden bepaald. Hiervoor wordt de interne shell opdracht read gebruikt. Om meer informatie over read te weten te komen gebruikt men help read. De syntaxis van read is read variabele.

De opdracht read leest tekst in van het toetsenbord (of van standaard input) en slaat deze tekst op in een variabele waarvan de naam als argument wordt gegeven. (of in de standaard variabele REPLY indien er geen argument gegeven is). Via deze variabele kan men verderop in het script de ingegeven waarde bewerken of gebruiken.

De opdracht read leest één regel van de standaard invoer. Het eerste woord in deze regel wordt in de eerste variabele geplaatst, het tweede woord in de tweede variabele, enzo... Als er meer woorden dan variabelen zijn worden alle woorden die te veel zijn in de laatste variabele geplaatst.

invoer01.png

invoer01-1.png

Het bovenstaande script wordt na de shebang en het gebruikelijke commentaar geopend met een zogenaamd 'here-document'. Dit is een alternatieve methode om tekst weer te geven op het scherm van de gebruiker. Zoals men kan zien bestaat de tekst gewoon uit tekst zoals men die in een document zou typen, en niet uit een serie echo regels. Het voordeel hiervan is dat er geen formaat verlies is op het moment dat men de tekst moet wijzigen. (hetgeen wel het geval is als men gebruik maakt van meerdere echo opdrachten). Een here-document wordt geopend met de opdracht cat, gevolgd door de constructie << EOF. De tekenreeks achter de << geeft aan waar het here-document eindigd. Men kan hier elke willekeurige tekenreeks gebruiken die men wil (niet noodzakelijk EOF). Op het moment dat de shell op een aparte regel dezelfde tekenreeks tegenkomt zal hij dat interpreteren als het einde van het here-document. Alle tekst tussen het begin en het einde van het here-document wordt op het scherm van de gebruiker getoond.

up.png 5 Variabelen bewerken down.png

In deze paragraaf bespreken we een aantal methoden om variabelen te bewerken.

up.png 5.1 Command substitution down.png

Een andere, zeer krachtige manier om de waarde van een variabele te vullen is door er het resultaat van een opdracht in te plaatsen. De syntaxis hiervoor is variabele=$(opdracht). Zo zou men een variabele wiebenik kunnen opvullen met de waarde van whoami, zoals in wiebenik=$(whoami).
Men kan dit zien als een variant op de algemene wijze waarop een opdracht zijn resultaat doorstuurt naar de standaard output of de standaard error. In plaats daarvan wordt nu de output doorgestuurd naar een specifieke geheugenplaats, zodat er verderop in het script nog gebruik van kan worden gemaakt.

comsub.png

In het voorgaande script wordt de naam van een bestand gevraagd dat vervolgens gecopieerd wordt naar de /lib/modules directory.

up.png 5.2 Waarde van argumenten down.png

Positionele parameters kunnen niet bewerkt worden in een script. Wil men de waarde van argumenten bewerken dan dient men eerst deze waarde op te slaan in een nieuwe variabele. Het volgende voorbeeld script laat zien hoe men dit principe gebruikt.

args.png

up.png 5.3 Substitution operators down.png

Op het moment dat het voor een script van cruciaal belang wordt dat een bepaalde variabele ook een waarde heeft, is het de moeite waard daar in het script een controle voor in te bouwen. In bash kan men daarvoor substitution operatoren gebruiken. Als de waarde van een variabele wordt aangeroepen via het dollar-teken, kan daar gelijktijdig een bewerking op worden uitgevoerd met een substitution operator. Hiervoor dient zowel de naam van de variabele als de substitution operator tussen accolades geplaatst te worden. Dit ziet eruit als : ${variabele:bewerking}
Het volgende geeft een overzicht van een aantal substitutie operatoren:

string01.png

string01-1.png

up.png 5.4 Pattern matching operatoren down.png

Met pattern-matching operatoren kan gezocht worden naar een patroon in de tekenreeks van de variabele, en vervolgens kan de variabele bewerkt worden naargelang het gevonden patroon. Met pattern matching operatoren kan onder andere een bestands naam worden bewerkt, men kan er een deel van een bestandsnaam mee verwijderen. Pattern matching operatoren zijn handig om uit een volledige bestands naam de directory naam te verwijderen zodat deze kan vervangen worden door de naam van een andere directory. De werking van de pattern matching operator berust erop dat in de waarde van de variabele gezocht wordt naar een tekenreeks, die vervolgens kan worden verwijderd. Als de gezochte tekenreeks zich vooraan in de variabele bevindt dan wordt het teken '#' gebruikt, bevindt de tekenreeks zich achteraan in de variabele dan wordt het teken '%' gebruikt.
Als er vervolgens in de waarde van de variabele de aangegeven tekenreeks voorkomt, kan het korste of het langste deel dat overeenkomt met de tekenreeks worden verwijderd. Eén enkele '#' of '%' betekent: verwijder het korste overeenkomende deel in de waarde van de variabele, twee hekjes '##' of procent tekens '%%' hebben de betekenis: verwijder het langste overeenkomende deel in de waarde van de variabele.
De werking hiervan kan best worden duidelijk gemaakt aan de hand van een voorbeeld: Stel dat men de variabele P bewerkt met als inhoud: /usr/doc/howto/en/Hardware-HOWTO.tar.gz. Men zoekt naar het patroon /*/, of met andere woorden naar een tekenreeks tussen twee slashes. Vervolgens gebruikt men de pattern matching patroon tekens om tekenreeksen te verwijderen.
De volgende tabel maakt dit principe duidelijk:

Substitution Resultaat
${P##/*/} Hardware-HOWTO.tar.gz
${P#/*/} doc/howto/en/Hardware-HOWTO.tar.gz
$P /usr/doc/howto/en/Hardware-HOWTO.tar.gz
${P%.*} /usr/doc/howto/en/Hardware-HOWTO.tar
${P%%.*} /usr/doc/howto/en/Hardware-HOWTO

Het bovenstaande resultaat wordt verkregen volgens de volgende regels:

  1. er wordt gezocht naar /*/, en alle voorkomens worden verwijderd
  2. er wordt gezocht naar /*/, en het eerste voorkomen wordt verwijderd
  3. het origineel
  4. er wordt gezocht naar .*, en het laatste voorkomen wordt verwijderd
  5. er wordt gezocht naar .*, en alle voorkomens worden verwijderd

Het volgende script en output toont een voorbeeld.

string02.png

string02-1.png

up.png 5.5 Rekenen in scripts down.png

Bash biedt ook een aantal mogelijkheden om berekeningen uit te voeren in scripts. In onderstaand voorbeeld wordt via een rekenkundige bewerking een teller bijgehouden.

calc01.png

Behalve bovenstaand voorbeeld zijn er nog mogelijkheden om berekeningen in een script uit te voeren.
Men kan de opdracht 'expr' (expression) gebruiken). Zie man expr om de details hiervan te zien.
Met VAR=$(expr 1 +2) wordt de variabele VAR ingesteld op de waarde 3.
Het nadeel van alle vorige voorbeelden is: zodra er aan de berekening iets wijzigd moet men het script wijzigen.
In het volgende voorbeeld wordt dit nadeel ondervangen.

calc02.png

Dit werkt goed met de meeste operatoren (zie man expr) behalve met de operator '*', die syntax error geeft. De reden hiervoor is dat '*' het joker teken van de shell is, en dus niet wordt geinterpreteerd als vermenigvuldigen. Gebruik een escape karakter voor de '*' om dit te verhelpen. Het volgende voorbeeld gebruikt de interne opdracht 'let', inplaats van de externe opdracht 'expr'. Dit werkt een stuk sneller.

calc03.png

Ook het volgende voorbeeld werkt sneller dan de externe 'expr' opdracht.

calc04.png

up.png 5.5.1 factor down.png

Met de opdracht factor kan men direct zien door welke getallen een getal deelbaar is.

up.png 5.5.2 bc, rekenen in decimalen down.png

Het probleem met rekenen binnen shell-scripts is dat met welke methode dan ook, alleen maar gehele getallen gebruikt kunnen worden. Dat is leuk als men een bepaalde bewerking meerdere keren na elkaar wil laten uitvoeren. Het is natuurlijk een stuk minder handig als men zeer nauwkeurige berekeningen zou willen maken. De oplossing is de opdracht 'bc'.
Met de opdracht bc kan men ook rekenen in getallen met decimalen.

Om te werken met bc is het wel handig om eerst eens in de interactieve mode van bc te werken.
De volgende screen-shot toont een interactive 'bc' sessie.

bc.png

Nu is het natuurlijk wel prettig om op de command line interactief te kunnen rekenen.
Maar in een shell-script heeft men daar niets aan. Om toch in een shell script 'bc' te kunnen gebruiken, gebruikt men echo.
Zoals in
       echo "scale=9; 10/3" | bc.
Het resultaat wordt direct op de prompt getoond.
Via command substitution kan men het resultaat in een variabele steken zoals in
       VAR=$(echo "scale=9; 10/3" | bc)
Bekijk de man page van bc om te ontdekken hoe krachtig deze reken machine is.

up.png 6 Flow control in shell scripts down.png

Het is mogelijk in een shell script bepaalde gedeelten alleen uit te laten voeren als aan één of meer voorwaarden is voldaan. Hiertoe dient flow-control te worden gebruikt.
Bash ondersteund hiervoor de volgende mogelijkheden:

up.png 6.1 Hulp middelen bij flow control down.png

Voordat we in de volgende paragrafen bespreken hoe flow-control moet toegepast worden, besteden we eerst aandacht aan de exit-status van een opdracht. Bij flow-control wordt vaak gebruik gemaakt van de exit-status van een opdracht. Het doel is om te kijken of al dan niet aan een bepaalde voorwaarde is voldaan waarbij de exit-status op uitermate handige wijze kan worden gebruikt. Ook besteden we aandacht aan de opdrachten exit, return en test, die vaak voorkomen in combinatie met flow-control.

up.png 6.1.1 exit status down.png

Wat men ook doet met flow-control, het komt er altijd op neer dat gekeken wordt of er wel of niet aan een bepaalde voorwaarde wordt voldaan. Hiervoor wordt gebruik gemaakt van de exit status van een opdracht. Elke opdracht geeft een code, de exit-status, terug aan het proces waardoor de opdracht is aangeroepen. Normaal betekend een exit-status met de waarde 0 dat alles goed verlopen is, alle andere waarden geven aan dat er iets is misgelopen, Default wordt de waarde 1 gebruikt om aan te geven dat er iets is misgelopen , de betekenis van andere waarden zijn puur afhankelijk van de opdracht of de toepassing in questie, en worden volledig door de (script) programmeur bepaald.

Een probleem met de exit-status is dat deze verdwijnt op het moment dat de volgende opdracht wordt aangeroepen. Dit kan uiteraard opgelost worden door de exit-status te bewaren. Hiervoor dient de speciale shell-variabele ?. Zie ook paragraaf 4.1.2 waar dit wordt gedemonstreerd in een script (zie $?).

up.png 6.1.2 De opdracht exit down.png

Nauw verwant aan de exit-status van een shell opdracht zijn de opdrachten exit en return, beiden interne opdrachten. De opdracht exit kan worden gebruikt om onder bepaalde voorwaarden een shell-script te beëindigen. Daarbij kan de exit-status die aan het besturings systeem wordt doorgegeven als argument worden meegegeven. In het volgende script vindt men een voorbeeld:

flow01.png

In dit script wordt gekeken of de positionele variabele $1 niet bestaat (zie [ -z $1 ], bekijk man test voor de preciese betekenis van de -z parameter). Als dat zo is wordt er een foutmelding gegenereerd en wordt de verdere uitvoering van het script afgebroken. Meer over de werking van zowel 'if-then' als 'test' vindt je verderop in dit hoofdstuk.

up.png 6.1.3 De opdracht 'return' down.png

De opdracht return doet hetzelfde als de opdracht exit, maar kan alleen worden gebruikt binnen een functie of een script dat is aangeroepen met de shell-opdracht source. Wanneer men return gebruikt buiten deze condities dan is de return waarde altijd false. (?). Wanneer men return gebruikt zonder een return waarde dan wordt de exit-status van de laatst gebruikte opdracht als return code meegegeven.

up.png 6.2 De opdracht 'test' down.png

Als men wil nagaan of een bepaalde voorwaarde vervult is, kan men de 'test'-functionaliteit, de interne opdracht test gebruiken.
De volgende twee syntaxis zijn gelijk:

Met de opdracht test wordt een expressie geëvalueerd. Afhankelijk van de uitkomst van de evaluatie wordt de exit-status op 0 (true) of 1 (false) gezet. Raadpleeg ook man test zowel als help test.

up.png 6.2.1 Categorien down.png

Er zijn 5 soorten test categorien:

up.png 6.2.2 File testers down.png

Met file testers worden bestand eigenschappen geévalueerd.

Test Betekenis
-s bestand bestand is niet leeg
-f bestand bestand bestaat en is een bestand (geen directory)
-d bestand bestand bestaat en is een directory
-w bestand bestand bestaat en is beschrijfbaar
-r bestand bestand bestaat en is leesbaar
-x bestand bestand bestaat en is uitvoerbaar
-u bestand bestand bestaat en set-user-id bit staat aan
-S bestand bestand bestaat en is een socket
-p bestand bestand bestaat en is een named pipe
-O bestand bestand bestaat en huidige gebruiker is de eigenaar
-L bestand bestand bestaat en is een symbolic link

up.png 6.2.3 file comparisons down.png

waarmee bestanden met elkaar vergeleken kunnen worden

Operator Betekenis
file1 -ef file2 file1 en file2 hebben dezelfde bestand en inode nummers
file1 -nt file2 file1 is jonger dan file2 (modificatie datum en tijd)
file1 -ot file2 file1 is ouder dan file2 (modificatie datum en tijd)

up.png 6.2.4 String tests down.png

waarmee teken reeksen bekeken worden

Operator Betekenis
string1 = string2 string1 is gelijk aan string2
string1 != string2 string1 is niet gelijk aan string2
string string1 is NOT NULL of niet gedefinieerd
-n string1 string1 is niet NULL en bestaat
-z string1 string1 is NULL en bestaat

up.png 6.2.5 Expression tests down.png

waarmee gekeken kan worden naar de uitkomst van een expressie en waarmee expressies met elkaar vergeleken kunnen worden

Operator Betekenis
! expression logical not
expression1 -a expression2 logical and
expression1 -o expression2 logical or

up.png 6.2.6 integer tests down.png

waarmee tekens met een numerieke waarde met elkaar vergeleken kunnen worden

Operator Betekenis Normaal if test if [ ]
-eq is gelijk aan 5 == 6 if test 5 -eq 6 if [ 5 -eq 6 ]
-ne is niet gelijk aan 5 != 6 if test 5 -ne 6 if [ 5 -ne 6 ]
-lt is kleiner dan 5 < 6 if test 5 -lt 6 if [ 5 -lt 6 ]
-le is kleiner dan of gelijk aan 5 <= 6 if test 5 -le 6 if [ 5 -le 6 ]
-gt if groter dan 5 > 6 if test 5 -gt 6 if [ 5 -gt 6 ]
-ge if groter dan of gelijk aan 5 >= 6 if test 5 -ge 6 if [ 5 -ge 6 ]

up.png 6.2.7 Single line test down.png

Door gebruik te maken van een 'or' conditie, aangeduid door twee opeenvolgende rechte strepen '||' kan men een test gebruiken om op een enkele lijn een test en actie te coderen. Vergelijk de volgende code voorbeelden. Het eerste voorbeeld is een normale if statement, het tweede voorbeeld een single line test.

if [ -z $1 ]; then
   echo "De eerste parameter is leeg"
   exit 1
fi;

-z $1 || exit 1

up.png 6.3 if then else down.png

Met 'if then else' kan worden bepaald dat opdrachten alleen worden uitgevoerd als aan een bepaalde voorwaarde is voldaan. Aan de voorwaarde is uitsluitend voldaan als deze een exit-status 0 heeft. Met 'if then' wordt de exit status van een opdracht gecontroleerd. Als aan de eerste voorwaarde niet is voldaan, kan men met 'else' de tegenovergestelde voorwaarde gebruiken.
Met de optionele 'elif' kan men meerdere voorwaarden in één if statement opnemen.
De algemene syntaxis van het if statement is te zien in de volgende afbeelding.
Zowel het 'elif' gedeelte als het 'else' gedeelte zijn optioneel. 'else' kan slechts 1 keer voorkomen, terwijl 'elif' meerdere keren kan gebruikt worden.

flow02.png

flow03.png

up.png 6.4.1 Combineren van exit-status down.png

Met één if statement kunnen ook meerdere voorwaarden tegelijk gecontroleerd worden. Daarvoor wordt gewerkt met de logische operatoren && (logische en) en || (logische of). Hiermee wordt gekeken of aan beide voorwaarden, of aan één van de twee voorwaarden voldaan is.
Men kan hetzelfde bereiken met de -a en -o opties van de test opdracht.

flow04.png

In dit voorbeeld script wordt gekeken of het bestand waarvan de naam als argument is gegeven, de GID- of de UID bit heeft ingesteld. De shell kijkt eerst of de eerste voorwaarde [ -g $1 ] een exit-status 0 oplevert. Als dat het zo is, is de exit-status van de tweede conditie niet relevant. Aan de logische of wordt immers voldaan als één van beide voorwaarden voldaan is. Als het de eerste conditie geen exit-status 0 oplevert wordt de tweede conditie getest. Als deze wel een exit-status oplevert wordt alsnog de 'echo' uitgevoerd omdat ook in dat geval voldaan is aan de logische of. Als geen van beide condities een exit-status 0 oplevert gebeurt er niets.

Met de operatoren && en || kan men een alternatieve, kortere schrijfwijze worden gebruikt dan bij het if-statement.
Zo is de opdracht test -x $1 || echo $1 is niet uitvoerbaar even geldig als een gehele if constructie die dezelfde test uitvoert. Ondanks dat de verkorte vorm de leesbaarheid van een script niet ten goede komt wordt dit toch veel toegepast. Dus hou de leesbaarheid van Uw script hoog met het plaatsen van commentaar.

Naast ||, waarmee gekeken wordt of de eerste opdracht een exit-status 0 oplevert en als dat niet het geval is de tweede opdracht, kan men ook gebruikt maken van de operator &&, waarmee gekeken wordt of beide opdrachten een exit-status 0 opleveren. In dit geval moeten beide condities dus waar zijn. Het volgende script is hier een voorbeeld van.

flow06.png

up.png 6.4 for down.png

Een van de grootste nadelen van constructies met 'if' is de afwezigheid van een eenvoudige mogelijkheid om tests uit te voeren op een reeks parameters na elkaar. (een reeks bestands namen is een typisch voorbeeld). Voor dit doel moet gebruik gemaakt worden van een lusconstructie zoals dat gedaan kan worden met de opdracht 'for'. De algemene vorm van dit statement is:

flow07.png

Constructies met for gaan ervan uit dat er een lijst met argumenten, bestanden, of welke lijst dan ook die afgewerkt moet worden. Die lijst wordt aangeduid door het statement 'in list'. Als geen lijst wordt gegeven, wordt gekeken naar x in de positionele parameters die bij het script zijn gegeven. Alle items die voorkomen in 'list' worden stuk voor stuk bekeken door for, en op elk van deze items wordt een opdracht reeks uitgevoerd. For leent zich daarom uitstekend om bewerkingen uit te voeren op alle bestanden in een directory.
Zo zou men een script kunnen aanmaken dat alle kleine letters in alle bestanden in de huidige directory omzet naar hoofdletters.
Een voorbeeld:

flow08.png

Meschien komt het functioneren van de file variabele in voorgaand script enigzins merkwaardig over. Dit is een variabele die binnen het for statement gedefinieerd wordt. Men dient er natuurlijk voor te zorgen dat deze variabele niet conflicteerd met een andere variabele. De waarde van de variabele wijzigd iedere keer de opdrachtenreeks afgewerkt is. De eerste keer dat de opdrachten reeks uitgevoerd wordt is de waarde van file de naam van het eerste bestand in de directory, de tweede keer, de naam van het tweede bestand, enzo.... net zolang tot alle bestanden in de directory afgewerkt zijn. In plaats van $file komt men binnen een for lus vaak de variabele $i tegen. De naam van de variabele in het for statement is vrij te kiezen.

flow09.png

Het volgende script gebruikt de variabele 'dir' in het for statement. Het script verdeeld de PATH variabele in zijn velden, door gebruik te maken van IFS (Internal Field Seperator), en doet een directory listing van de betreffende directory.

flow10.png

up.png 6.5 case down.png

Met case kan men de waarde van een variabele vergelijken met een aantal op voorhand vastgestelde waarden. Wanneer een treffer is gevonden kan men een reeks opdrachten uitvoeren.
De algemene syntax is als volgt:

flow11.png

Het volgende script is een eenvoudig voorbeeld van een 'case' toepassing.

flow12.png

In het bovenstaande script wordt door 'case' gekeken naar de waarde van de eerste positionele parameter '$1'. Als $1 een waarde van -a of -b heeft wordt een opdracht uitgevoerd. Als $1 geen van beide waarden bevat volgt er een fout melding. Merk op dat het joker teken gebruikt wordt om elke willekeurige waarde aan te duiden.
Let er bij het gebruik van 'case' ook op een paar eigenaardigheden in de syntaxis.

up.png 6.6 while down.png

Het statement 'while' wordt gebruikt om een reeks opdrachten uit te voeren zolang een voorwaarde voldaan is. Kortom een lus met while is afgelopen wanneer de exit-status 1 is. Een eenvoudig voorbeeld zal hier duidelijkheid brengen.

flow13.png

In regel 6 wordt de variabele count op 1 ingesteld. Vervolgens wordt er in regel 7 gekeken of er nog positionele parameters beschikbaar zijn. Zolang dit het geval is, is de while conditie true (ofwel exit-status is 0). Op regel 9 wordt de count en de huidige parameter getoond. Vervolgens wordt in regel 10 de positionele parameters geshift. In regel 11 wordt de parameter geteld. Met man expr kan men de betekenis van 'expr'

up.png 6.7 until down.png

Het statement 'until' wordt gebruikt om een reeks opdrachten uit te voeren zolang een voorwaarde niet voldaan is. Kortom een lus met until is afgelopen wanneer de exit-status 0 is. Een eenvoudig voorbeeld zal ook hier duidelijkheid brengen.

flow14.png

De werking van bovenstaand script is eigenlijk exact hetzelfde als het script van het while voorbeeld. In regels 6 en 7 worden de initiele waarden ingesteld, count wordt op 1 gezet, var op de eerste positionele parameter. In regel 8 wordt er getest met until op de inhoud var, ofwel, zolang var een waarde heeft is de conditie voldaan en zal de until clause (de opdrachten in het until gedeelte) uitgevoerd worden. Op regel 11 worden de positionele parameters opgeschoven en in regel 13 wordt var opnieuw ingesteld.

up.png 07 Functies down.png

In shell-scripts kan gebruik worden gemaakt van functies. Een functie is een subroutine, een soort script binnen een script, die gebruikt wordt om een naam te geven aan een reeks bij elkaar horende opdrachten. De functie kan vervolgens met zijn eigen naam aangeroepen worden. Als men ze goed toepast, kunnen functies gebruikt worden als de bouwstenen waaruit een script is opgebouwd.

Men kan ze op twee verschillende manieren definieren : (er is geen wezenlijk verschil tussen beide manieren)
De eerste het gebruik van het sleutelwoord 'function' gevolgd door de naam van de functie, en vervolgens de opdrachten tussen accolades.

function01

De tweede manier is de functienaam gevolgd door '()' en vervolgens de opdrachten tussen accolades.

function02.png

Het grote voordeel van functies is dat het mogelijk is code die vaak in een script gebruikt moet worden op één centrale plaats te onderhouden. Dit komt de overzichtelijkheid van scripts ten goede. Een ander voordeel is dat de snelheid van het script toeneemt. Dit komt omdat tijdens de werking van het script de code van de functie wordt opgeslagen in het werkgeheugen van de computer. Hierdoor kan met name bij grotere scripts de code sneller worden benaderd; deze hoeft immers niet meer van schijf te worden gelezen.
Hierna volgt een voorbeeld van een script waarin een functie wordt gebruikt:

function03.png

In het bovenstaande script is te zien dat functie 'schrijf' twee keer wordt aangeroepen.
De voordelen zijn legio:

up.png 08 opties down.png

Er is een verschil tussen opties en argumenten, alhoewel beiden als positionele parameters worden meegeven op de command line. Een optie is een waarde die op voorhand vast ligt en die gebruikt wordt om het gedrag van het script (programma) te wijzigen. Een argument is een waarde die als een variabele beschouwd wordt. Opties worden meestal voorafgegaan door een '-'. Er zijn 2 manieren om in een script opties van argumenten te onderscheiden. Ten eerste de opties kunnen opgezocht worden via grep of men kan ook gebruik maken van getopts.

up.png 8.1 Zoeken naar opties met grep down.png

De meeste opdrachten kunnen werken met opties en argumenten. Een optie is iets dat gebruikt wordt om het gedrag van een opdracht te beïnvloeden, zoals ls -l. Opties zijn altijd meegeprogrammeerd in de opdracht. Argumenten daarentegen worden gebruikt op een variabele waarde. In ls -l /usr is -l dus een optie en /usr een argument. De eerste manier om opties te onderscheiden van argumenten is te zoeken naar opties met de opdracht grep. Hierbij kan men zoeken naar alle positionele parameters die voorafgegaan worden door een liggend streepje. Vervolgens kan men de 'shift' opdracht gebruiken om één voor één alle opties af te handelen. Als alle opties afgehandeld zijn kan men verder gaan met het afhandelen van de argumenten.
De benodigde script code heeft de volgende theoretische vorm:

opties01.png

Het belangrijkste verschil met vorige scripts is de eerste regel: while de parameter een optie is. De vraag die hierbij rijst is natuurlijk , hoe wordt bepaald wanneer een parameter een optie is. Hiervoor dient de opdracht grep, men gebruikt grep om te zoeken in de waarde van de parameter naar een liggend streepje. Dit kan op de volgende manier gebeuren : while [ -n "$(echo $1 | grep '-')" ]
De betekenis van deze uitdrukking is : terwijl de eerste parameter minimaal 1 teken groot is en bovendien begint met een '-' is de uitdrukking waar. Vervolgens kan men met een 'case' iedere optie individueel behandelen. Nadat de optie behandeld is kan men met behulp van een shift operatie overgaan naar de volgende optie. Deze cyclus wordt herhaald (while) totdat er geen parameters meer zijn of de eerste parameter geen streepje meer bevat.

opties02.png

Ten opzichte van de meeste opdrachten zijn er toch en aantal nadelen verbonden aan bovenstaande methode.

Als een oplossing voor de vermelde nadelen kan men de getopts shell opdracht gebruiken.

up.png 8.2 getopts down.png

De shell-opdracht getopts kan worden gebruikt in combinatie met de shell-opdracht while om te zoeken naar geldige opties. Als eerste argument van getopts kan worden meegegeven welke opties geldig zijn. Daarbij kan aangegeven worden welke van deze opties een argument hebben. Als 2'de argument heeft getopts de naam van een variabele dewelke gebruikt wordt om elke optie, terwijl deze verwerkt wordt, aan toe te kennen. 'getopts' blijft in een lus waarbij opties worden afgewerkt totdat een exit status 1 wordt aangetroffen. De exit-status wordt veroorzaakt doordat er geen opties meer zijn of doordat een fout is opgetreden. Zie de volgende screen-shot :

opties03.png

In het bovenstaande script kunnen 5 opties worden meegegeven en de laatste optie -f neemt bovendien een argument. Via de case constructie wordt in het script voor iedere optie een aparte serie opdrachten voorzien. Het argument voor de -f optie word doorgegeven via de OPTARG variabele. Het volgende screen-shot toont een script met iets meer betekenis, onder andere het gebruik van de functie 'usage' om de --usage-- van het script aan de gebruiker te melden ingeval van een verkeerd gebruik van opties.

opties04.png

up.png 9 Afvangen van ongeoorloofde toets combinaties down.png

Een laatste toepassing in scripts is het afvangen van ongeoorloofde toets combinaties, zoals ctrl-c. Hiervoor kan de shell-opdracht trap worden gebruikt. Hiermee kan men een opdracht koppelen aan een signaal dat naar een proces wordt gestuurd, in dit geval is de shell die het script interpreteerd het proces. Dit betekent dat een opdracht wordt uitgevoerd zodra een specifiek signaal naar een proces wordt gestuurd; het signaal zelf wordt niet uitgevoerd. De drie meest voorkomende signalen die naar een proces worden gestuurd zijn

Van deze en andere signalen die naar een proces kunnen worden gestuurd, mag alleen het KILL-signaal niet onderbroken worden. Er moet namelijk altijd de mogelijkheid blijven om het proces van buiten af af te sluiten.

Om met de opdracht trap een signaal dat naar een proces wordt gestuurd af te vangen, dient de volgende syntaxis te worden gebruikt :
trap opdrach[ten] signaal1 signaal2
De werking hiervan wordt gedemonstreerd in het volgende script:

trap01.png

In dit script wordt op de eerste regel de trap gedefinieerd. Deze trap blijft gedurende de hele uitvoering van het script als een soort waakhond actief, om te luisteren of er een ongeoorloofd signaal naar het proces wordt gestuurd. Als dit het geval is wordt de bijbehorende opdracht uitgevoerd.

remark.png
Voorbeelden: Aanpassen van bestands rechten

up.png 10 Logger down.png

Gebruik de opdracht logger om te loggen in de syslog of de system messages.
Voorbeeld:
logger -t FIXEDIP "Starting fixedip $DEVICE:1 $IPADDRESS"

up.png Samenvatting down.png

In dit hoofdstuk hebben we kennis gemaakt met de mogelijkheden die geboden worden door het werken met shell scripts. We zijn daarbij niet echt diep op de materie ingegaan, de nadruk lag eerder op het begrijpen van bestaande shell-scripts dan op het zelf schrijven ervan. We hebben geleerd dat er verschillende manieren zijn waarop men binnen een shell script gegevens kan bewerken, en gebruik maken van gegevens die niet tot het script behoren. We hebben ook gezien hoe de gebruiker parameters aan een script kan meegeven om de werking van het script te vervanderen. Hierbij kan gebruik worden gemaakt van verschillende organismen, waarbij de constructie if-then-else waarschijnlijk de meest bekende is.

up.png Literatuur down.png

previous.png Gratis Hosting
Cursus Linux       Advanced       Shell Scripting   
Last modified: Mon Jun 30 11:17:51 2014