Scripts

I Automatiseren van opdrachten

1 Scripts

Programma's zijn in twee categorieën op te delen: gecompileerde programma's en scripts. Bij gecompileerde programma's wordt programmacode eerst door een compiler vertaald naar machine-uitvoerbare code voordat het gestart wordt. Bij een script is dat anders: daarbij gebeurt alles pas op het moment dat het script opgestart wordt. Worden bij een gecompileerd programma nog veel fouten afgevangen door de compiler, bij een script zijn die controles er pas op 'run-time'.

Het voordeel van scripts is dat ze niet gecompileerd hoeven te worden en dat de ontwikkeltijd een stuk korter is. Scripts zijn uitermate geschikt voor korte programma's om kleine, gespecialiseerde taken mee uit te voeren, zoals systeemtaken. Grote programma's worden over het algemeen geschreven in talen die compilatie vereisen, hoewel steeds vaker ook voor grotere programma's naar scripttalen (en dan de laatste tijd voornamelijk Python) wordt gegrepen.

De scheidslijn tussen script en programmeertaal is de laatste tijd niet echt goed meer te maken. Veel nieuwe(re) talen, zoals Java en C#, worden gecompileerd naar bytecode (gemaakt door een compiler). Bij sommige scriptingtalen (zoals Python) wordt ook bytecode aangemaakt door de interpreter voordat het programma gestart wordt. Als het script niet aangepast is, wordt meteen de bytecode uitgevoerd, wat sneller is.

In Linux hangt de taal waarin je de scripts schrijft af van welke terminal (shell) je gebruikt. De standaard Linux shell is bash, maar csh (C-shell) is ook voorhanden. In dit hoofdstuk bespreken we hoe je een programma kan schrijven met de bash-shell. Andere veel gebruikte scripting talen die geen shell gebruiken zijn perl en python.

De bedoeling van volgende voorbeeldscripts is om op een praktische manier de syntax van Bash-scripts te bekijken.

Maak de map ~/Scripts/ aan waarin je de ingetypte scripts bewaart.

Start een editor (K menu > Programma's > Hulpmiddelen > Editor > Teksteditor) waarmee je het script kunt aanmaken (intypen).

Om het systeem aan te geven dat je de Bash-shell gebruikt zet je in het begin van het programma: #!/bin/bash

Je begint met een eenvoudig script dat enkele regels tekst op het scherm plaatst.

#!/bin/bash
echo Welkom in de wereld van de scripts
echo -n "De huidige tijd en datum: "
date

Het argument -n zorgt ervoor dat er op het einde van de regel geen nieuwe regel wordt aangemaakt.

De "" tekens zorgen ervoor dat de laatste spatie na de : ook deel uitmaakt van de tekenreeks.

Sla het script op als ~/Scripts/datum.

Start een Terminal en ga naar de map ~/Scripts/ met de opdracht cd.

Voor dat je een script uitvoert moet je jezelf ook nog rechten geven om dit bestand uit te voeren. Doe dit met het commando `chmod +x datum`.

Een script start je door de naam in te typen voorafgegaan door het pad (./datum). Test het script.

Je kunt het script korter maken door de opdracht op te nemen in de tekenreeks. Dit kan door de twee laatste regels te vervangen door:

echo "De huidige tijd en datum: `date`"

De backquotes voor en achter date zorgen ervoor dat het resultaat van de opdracht opgenomen wordt in de tekenreeks. Test deze aanpassing.

Argumenten

Je kunt aan een programma ook argumenten meegeven. Je moet hiervoor het script starten met bijkomende data achter de opdracht. Die data wordt dan geïnterpreteerd als argument(en). Je gaat nu een script maken dat gebruik maakt van argumenten.

Met "$#" vraag je het aantal argumenten op.

#!/bin/bash
echo Er zijn $# argumenten.

Sla dit script op als ~/Scripts/arg.

Zorg ervoor dat het script uitvoerbaar is.

Test het script door de opdracht `./arg één twee drie vier` uit te voeren.

Op het scherm verschijnt 'Er zijn 4 argumenten.'.

Voer nu de opdracht `./arg "één twee drie vier"` uit.

Op het scherm verschijnt 'Er zijn 1 argumenten.'. Dit is te wijten aan het gebruik van de aanhalingstekens. Alles wat tussen twee opeenvolgende aanhalingstekens staat, wordt beschouwd als één argument.

Selectie

Net zoals andere programmeertalen heeft bash ondersteuning voor selecties en iteraties.

Pas het script ~/Scripts/arg aan tot je het volgende hebt:

#!/bin/bash
if [ $# = 0 ]; then
echo Er zijn geen argumenten.
else
echo Er zijn $# argumenten.
fi

De selectie heeft de structuur `if [_conditie(s)_]; then (doe iets) else (doe iets) fi`.

De "else" tak mag weggelaten worden. De "fi" toont het einde van dit blok.

Logische operatoren (NOT, OR, AND) worden voorgesteld als !, || en &&.

Test het script na het opslaan met de opdrachten: `./arg één twee` geeft op het scherm 'Er zijn 2 argumenten'; `./arg` geeft op het scherm 'Er zijn geen argumenten'.

Voeg de volgende regels achteraan aan het script toe.

if [ -e $1 ]; then
echo Het bestand $1 bestaat.
fi
if [ ! -e $1 ]; then
echo Het bestand $1 bestaat niet.
fi

Met de opdracht `-e` onderzoek je het bestaan van een bestand (zie man bash voor meer info). De "$1" staat voor de variabele dat het eerste argument bevat.

Voer de volgende opdrachten uit om het script te testen: `./arg één` geeft op het scherm

Er zijn 1 argumenten.
Het bestand één bestaat niet.

`./arg /etc/host.conf` geeft op het scherm

Er zijn 1 argumenten.
Het bestand /etc/host.conf bestaat.

`./arg` geeft op het scherm

Er zijn geen argumenten.
Het bestand bestaat.

Dit gaat mis. Veel beter wordt het als je het script aanpast tot:

#!/bin/bash
if [ $# = 0 ]; then
echo Er zijn geen argumenten.
else
echo Er zijn $# argumenten.
if [ -e $1 ]; then
echo Het bestand $1 bestaat.
else
echo Het bestand $1 bestaat niet.
fi
fi

Dit noemen we selecties nesten. Pas als er argumenten zijn, wordt het eerste argument getest of het een bestaand bestand is.

Voer de volgende opdrachten uit om het script te testen: `./arg één` geeft op het scherm

Er zijn 1 argumenten.
Het bestand één bestaat niet.

`./arg /etc/host.conf` geeft op het scherm

Er zijn 1 argumenten.
Het bestand /etc/host.conf bestaat.

maar `./arg` geeft nu op het scherm

Er zijn geen argumenten.

For-lussen

Met een "for"-loop kun je een lus uitvoeren waarvan je vooraf weet hoeveel keer het uitgevoerd moet worden.

Maak een nieuw script:

#!/bin/bash
for i in 1 2 3 4 5 6 7 8 9 10;
do
echo $i
done

Sla dit script op als ~/Scripts/tel.

Test het script. Je krijgt alle cijfers van 1 t.e.m. 10 mooi onder elkaar op het scherm.

Wijzig lijn 2 in:

for i in `seq 1 10`;

Je zult merken dat dit dezelfde uitwerking heeft.

While-lussen

Indien je voorafgaand niet weet hoeveel keer je door een lus moet, gebruik je best een "while".

Het volgende nieuwe script zoekt een woord op zolang er invoer is, anders stopt het.

#!/bin/bash
word="zoekwoord"
set word
while [ "$word" != "" ]; do
echo -n "Geef het op te zoeken woord (Return om te stoppen): "
read word
if [ "$word" != "" ]; then
grep "$word" /usr/share/myspell/nl_NL.dic
fi
done

Sla dit script op als ~/Scripts/zoek.

Start het script en geef het woord 'school' in. Er verschijnen een aantal woorden op het scherm die allemaal het woord school bevatten. Pas als je als zoekwoord niets ingeeft, stopt het script.

"$word" staat tussen aanhalingstekens zodat het als één enkel geheel beschouwd wordt, zelfs al staan er scheidingstekens zoals spaties in.

Vergelijkingen

Probeer van het volgende nieuwe script te achterhalen wat het precies doet.

#!/bin/bash
declare -i number
class=3
if [ $# == 0 ]; then
echo Geen getal om te classificeren.
else
number=$1
if [ $number -le -100 ]; then
class=0
fi
if [ $number -ge 0 ] && [ $number -le 100 ]; then
class=1
fi
if [ $number -gt 1000 ] && [ $number -lt 2000 ]; then
class=2
fi
echo Het getal hoort in de groep $class
fi

Met "declare" kun je een variabele declareren. Het declareren is niet verplicht, maar kan nuttig zijn indien je de variabele wil blokkeren voor andere types van data. Het argument "-i" forceert een geheel getal. (zie man pages voor de syntax van andere declaraties).

Sla het script op als ~/Scripts/indeling.

Test het script met de argumenten -100, 10, 1000 en 1500. Het opgegeven getal komt daarbij respectievelijke terecht in de groep 0, 1, 3 en 2.

Zoek de betekenis van -le -lt -ge -gt -eq -ne (-eq en -ne komen niet voor in het script) op met behulp van de opdracht `man bash`

Case

Maak onderstaand nieuwe script aan.

#!/bin/bash
echo; echo "Typ een teken, daarna Return."
read Keypress
case "$Keypress" in
[[:lower:]] ) echo "Kleine letter";;
[[:upper:]] ) echo "Hoofdletter";;
[[:digit:]] ) echo "Cijfer";;
* ) echo "Leesteken, spatie of iets anders";;
esac

Sla het op als ~/Scripts/teken.

Test het script met verschillende soorten tekens.

Net zoals in andere programmeertalen heb je ook "case" ter beschikking. Bestudeer de syntax van case (schrijf deze eventueel op).

In het vervolg van de cursus volgen nog vele praktische toepassingen van eenvoudige scripts.

Opdrachten

  1. Maak een script die de tekst 'Deze map bevat de volgende mappen en bestanden:' op het scherm plaatst, gevolgd door de inhoud van de map met de opdracht `ls`.

    1. Sla het script op als ~/Scripts/mapinhoud.

    2. Test het script en pas het aan tot het werkt.

  2. Pas het script ~/Scripts/mapinhoud aan zodat het script eerst vraagt van welke map het de inhoud moet tonen.

    1. Test het script en pas het aan tot het werkt.

  3. Vul het script aan met een melding op het scherm 'De map $dirname bestaat.' indien de map bestaat, of 'De map $dirname bestaat niet.' als de map niet bestaat. Om te testen of een map bestaat kun je gebruik maken van het commando `-d $dirname` (vergelijkbaar met `-e $filename` om te testen of een bestand bestaat).

    1. Test het script tot het werkt.

  4. Maak een nieuw script die de volgende tekst op het scherm plaatst:
    Van welke map wil je de inhoud?
    1) /home
    2) /etc

    1. Het script moet daarna de keuze van de gebruiker opvragen.

    2. Indien de gebruiker het cijfer 1 gevolgd door <Return> ingaf, moet de inhoud van de map /home/ getoond worden.

    3. Indien de gebruiker het cijfer 2 gevolgd door <Return> ingaf, moet de inhoud van de map /etc/ getoond worden.

    4. Indien de gebruiker iets anders ingaf, moet de melding 'Je hebt niet 1 of 2 gekozen!' op het scherm komen.

Je gaat nu scripts maken die er wat beter uitzien, namelijk scripts die werken met een grafisch interface. KDialog zorgt voor een grafische interface voor scripts.

  1. Maak het volgende nieuwe script.

    #!/bin/bash
    kdialog --title "Scripts met KDialog" \
    --msgbox "Dit is een simpel berichtvenster. Je kan om het even welk bericht laten verschijnen. Het venster blijft tot je Return drukt."

    1. Sla het script op als ~/Scripts/msgbox.

    2. Voer het script uit en bekijk het resultaat.

    3. Pas het script aan zodat

      1. de titel van het dialoogvenster 'Welkom in de wereld van de scripts' wordt,

      2. de tekst in het dialoogvenster wordt 'De huidige tijd en datum: ' met daarachter de datum.

  2. Maak het volgende nieuwe script.

    #!/bin/bash
    MAP=`kdialog --title "Maak uw keuze" --getexistingdirectory ~`
    case $? in
    0) echo "\"$MAP\" gekozen.";;
    1) echo "Annuleren geklikt.";;
    esac

    1. Sla het script op als ~/Scripts/dselect.

    2. Voer het script uit en bekijk het resultaat.

    De variabele $MAP bevat hier de door de gebruiker gekozen map; de variabele $? is 0 als de gebruiker een map koos, is ............ als de gebruiker op de knop Annuleren klikte of het dialoogvenster sloot.

    \” zorgt ervoor dat het aanhalingsteken in de tekenreeks gebruikt kan worden en niet als sluit de aanhalingstekens wordt gezien. De opdracht `echo "Hij schreeuwde \"HELP\""` plaatst dus 'Hij schreeuwde "HELP"' op het scherm.

    1. Pas dit script aan zodat:

      1. de titel van het dialoogvenster 'Kies een map' wordt,

      2. de echo opdrachten vervangen worden door een msgbox (zie vorig script).

    2. Test dit script en pas het desnoods aan tot het werkt.

  1. Pas het script verder aan zodat bij de keuze van een map de inhoud van de map in een msgbox wordt getoond.

Als kers op de taart ga je programma's besturen met behulp van scripts. Daarvoor moet het pakket xvkbd geïnstalleerd zijn. Dit pakket kan een toetsenbord nabootsen en toetsaanslagen naar het systeem sturen.

  1. Maak het volgende nieuwe script:

    #!/bin/bash
    echo ------------------------------------
    echo Start een editor en typ wat tekst in
    echo ------------------------------------
    ( # Alles wat binnen deze haken staat wordt parallel uitgevoerd.
    sleep 5 # wacht tot kwrite is opgestart.
    xvkbd -delay 10
    -xsendevent -text "Deze tekst wordt automatisch getypt."
    sleep 1
    xvkbd
    -xsendevent -text "\\Cq" # druk <Ctrl>q (\\C staat voor <Ctrl>);
    sleep 1
    xvkbd
    -xsendevent -text "\\[Tab]" # druk Tab; naar knop Verwerpen
    sleep 1
    xvkbd
    -xsendevent -text "\\[Return]" # bevestig Verwerpen.
    sleep 1
    ) & # alles binnen de haken tegelijkertijd met kwrite uitvoeren.
    kwrite

    1. Sla dit script op als ~/Scripts/typ.

    Let op de vele commentaar (alles wat volgt op een # teken), deze commentaar is hier toegevoegd om uit te leggen hoe alles werkt. Commentaar wordt ook veel gebruikt om collega's wegwijs te maken in het script en als geheugensteun voor de auteur zelf, dan weet je binnen een paar maanden nog hoe het script werkt.

    1. Test het script tot het werkt.

    2. Pas het script aan zodat (Tip: doe het stap voor stap):

      1. er de tekst 'Dit lijkt wel toveren.' op de tweede regel getypt wordt,

      2. de tekst bij het afsluiten opgeslagen wordt als scripttekst.

Zo nu heb je alles in handen om zelf op een gebruiksvriendelijke manier taken te automatiseren met scripts. Meer informatie kun je vinden in de manuals van bash en de gebruikte opdrachten.