Tips en Trucs 2014

Scripts met gebruikersinterface

Een veel gehoorde opmerking over scripts is dat ze enkel werken in een terminal. Dit klopt niet. Wel is het zo dat je als script ontwikkelaar rekening moet houden met de grafische omgeving waarin het script moet werken. Dit moet je trouwens bij het schrijven van klassieke applicaties ook doen. Zo zal een script dat enkel in een KDE omgeving werkt, gebruik maken van KDialog, in de Gnome omgeving gebruik je Zenity, in de X omgeving Xdialog en in een tekstomgeving Dialog. Als je gebruik maakt van zeer specifieke bibliotheken zoals KDialog en Zenity, sluit je script beter aan bij KDE en Gnome, heb je meer specifieke mogelijkheden, maar werkt je script minder goed in een andere omgeving. Gebruik je bijvoorbeeld Xdialog, dan werkt uw script in alle grafische omgevingen gebaseerd op X (inclusief KDE en Gnome). Gebruik je Dialog dan werkt uw script in alle tekstomgevingen, dus ook in alle grafische omgevingen, weliswaar in een terminal venster.

Gelukkig lijken alle bibliotheken op elkaar. Als je een script op basis van KDialog hebt geschreven is deze zonder veel moeite om te zetten naar een script op Zenity. De gebruikte opdrachten lijken namelijk sterk op elkaar en verschillen hoofdzakelijk in specifieke opties.

Een praktijkvoorbeeld: RSync

#!/bin/bash
# Titel van de dialoogvensters met datum script (handig om versie te controleren) 
titel="SNT Rsync, PindaNet, Dany Pinoy, `date -r "$0" +'%d %B %Y'`"
# Enkele voorbereidingen, instellen opties
backupip=192.168.1.65
mkdir /usr/home/Documents/SNT
# Aanmaken van een tijdelijke werkmap
tmpdir=$(mktemp -dt "rsync.XXXXXXXXXX")
# Test of de backupserver actief is
rsync $backupip::
if [ $? -ne 0 ]; then   # Als de backupserver niet actief is tonen we dit met een foutmelding
  kdialog  --title $titel --error "Kon geen verbinding met de backupserver opbouwen."
else  # Als de backupserver actief is mag de synchronisatie uitgevoerd worden
  # We plaatsen alle uit te voeren opdrachten in het script konsoleCommand.sh  
  echo "rsync -avHxh --numeric-ids --progress --delete $backupip::SNT/Mac\\ OS\\ X/ /usr/home/Documents/SNT/Mac\\ OS\\ X 2>>$tmpdir/rsync.error" > $tmpdir/konsoleCommand.sh
  echo "rsync -avHxh --numeric-ids --progress --delete $backupip::SNT/Sociale\\ Netwerken/ /usr/home/Documents/SNT/Sociale\\ Netwerken 2>>$tmpdir/rsync.error" >> $tmpdir/konsoleCommand.sh
  echo "rsync -avHxh --numeric-ids --progress --delete $backupip::SNT/Thuisnetwerken/ /usr/home/Documents/SNT/Thuisnetwerken 2>>$tmpdir/rsync.error" >> $tmpdir/konsoleCommand.sh
  echo "rsync -avHxh --numeric-ids --progress --delete $backupip::SNT/Windows\\ 8/ /usr/home/Documents/SNT/Windows\\ 8 2>>$tmpdir/rsync.error" >> $tmpdir/konsoleCommand.sh
  # We starten het konsoleCommand.sh script in een terminal, zo ziet de gebruiker de voortgang van de backup.
  konsole --nofork -e sh $tmpdir/konsoleCommand.sh
fi
if [ -s $tmpdir/rsync.error ]; then   # De synchronisatie verliep niet vlekkenloos, meld dit
  kdialog --title "SNT RSync gaf foutmeldingen." --textbox $tmpdir/rsync.error
else   # De synchronisatie verliep vlekkeloos
  kdialog --title $titel --passivepopup "SNT RSync voltooid." 5
fi
# Verwijder de tijdelijke werkmap
rm -r $tmpdir

Voor het schrijven van opdrachten naar het script konsoleCommand.sh gebruik ik hier echo opdrachten. Daarbij worden variabelen zoals $backupip en $tmpdir vervangen door hun inhoud. De variabele $tmpdir heeft bij elke uitvoering een andere waarde waardoor het konsoleCommand.sh script dus dynamisch wordt opgebouwd. Let bij de eerste echo opdracht op het > redirect teken. Daarmee schrijf je een eventueel bestaand konsoleCommand.sh script over. De daaropvolgende echo opdrachten gebruiken >> om opdrachten aan het konsoleCommand.sh script toe te voegen. Daarenboven moet je letterlijke tekens laten voorafgaan door een dubbele backslash. Dit zie je in de spaties van de paden van de te synchroniseren mappen. De eerste backslash zorgt ervoor dat in het script konsoleCommand.sh de spatie wordt voorafgegaan door een backslash. Deze backslash is nodig om in de rsync opdracht de spatie op te nemen in de padnaam (bijvoorbeeld in de mapnaam Mac OS X) en deze niet als scheidingsteken voor opties te interpreteren.

Het testen van zo'n scripts doe je best in stappen. Zo heb ik eerst de kern van het script konsoleCommand.sh volledig uitgewerkt en getest. Daarna heb ik konsoleCommand.sh in een ander script opgebouwd met echo opdrachten. Daarna komt de omkadering van konsoleCommand.sh aan de beurt.

De laatste kdialog opdracht die wordt uitgevoerd na het vlekkeloos verlopen van de synchronisatie, toont een korte melding van 5 seconden in het notificatie onderdeel van KDE. Zo wordt de gebruiker niet lastig gevallen met een dialoogvenster als alles naar wens verliep.

Opnemen in een installatiescript

Dit script wordt gebruikt op een computer waarop veel geëxperimenteerd wordt. Dit betekend dat ik een basisimage van openSUSE 13.1 installatie heb gemaakt en ik daarna de configuratie met behulp van een script automatisch laat configureren. Het bovenstaande synchronisatie script kan je als volgt in zo'n installatiescript opnemen:

cat <<EOF > $HOME/bin/rsyncSNT.sh
#!/bin/bash
titel="SNT Rsync, PindaNet, Dany Pinoy, \`date -r "\$0" +'%d %B %Y'\`"
backupip=192.168.1.65
mkdir /usr/home/Documents/SNT
tmpdir=\$(mktemp -dt "rsync.XXXXXXXXXX")

rsync \$backupip::
if [ \$? -ne 0 ]; then
  kdialog  --title \$titel --error "Kon geen verbinding met de backupserver opbouwen."
else
  echo "rsync -avHxh --numeric-ids --progress --delete \$backupip::SNT/Mac\\\\ OS\\\\ X/ /usr/home/Documents/SNT/Mac\\\\ OS\\\\ X 2>>\$tmpdir/rsync.error" > \$tmpdir/konsoleCommand.sh
  echo "rsync -avHxh --numeric-ids --progress --delete \$backupip::SNT/Sociale\\\\ Netwerken/ /usr/home/Documents/SNT/Sociale\\\\ Netwerken 2>>\$tmpdir/rsync.error" >> \$tmpdir/konsoleCommand.sh
  echo "rsync -avHxh --numeric-ids --progress --delete \$backupip::SNT/Thuisnetwerken/ /usr/home/Documents/SNT/Thuisnetwerken 2>>\$tmpdir/rsync.error" >> \$tmpdir/konsoleCommand.sh
  echo "rsync -avHxh --numeric-ids --progress --delete \$backupip::SNT/Windows\\\\ 8/ /usr/home/Documents/SNT/Windows\\\\ 8 2>>\$tmpdir/rsync.error" >> \$tmpdir/konsoleCommand.sh
  konsole --nofork -e sh \$tmpdir/konsoleCommand.sh
fi
if [ -s \$tmpdir/rsync.error ]; then
  kdialog --title "SNT RSync gaf foutmeldingen." --textbox \$tmpdir/rsync.error
else
  kdialog --title \$titel --passivepopup "SNT RSync voltooid." 5
fi
rm -r \$tmpdir
EOF

Om het rsyncSNT.sh script op te bouwen, gebruik ik deze keer de opdracht cat in combinatie met een heredoc structuur (EOF). Alles wat tussen de EOF sleutelwoorden staat, wordt als één tekenreeks (string) beschouwd. Dit is een stuk leesbaarder dan echo opdrachten. Let echter wel op met inspringen. De inspringingen tussen de EOF sleutelwoorden maken deel uit van de tekenreeks. En nog belangrijker is dat er voor het afsluitende EOF sleutelwoord geen andere tekens mogen staan, zelfs geen spaties of tabs.

Weer moeten we zorgen dat het script correct wordt opgebouwd. Vermijd het gebruik van tabs, mijn ervaring leert dat dit fout gaat en tabs niet worden weggeschreven. Aangezien variabelen niet in het installatiescript maar in het opgebouwde script gebruikt moeten worden, moet je de dollartekens letterlijk laten wegschrijven door er een backslash voor te plaatsen. Ook backticks om opdrachten binnen tekenreeksen op te nemen, moet je letterlijk wegschrijven. Zo mag de date opdracht in de titel tekenreeks pas uitgevoerd worden in het opgebouwde script, niet in het installatiescript.

Let bij het gebruik van de heredoc structuur op te lange tekenreeksen tussen de EOF sleutelwoorden. Mijn ervaring leert dat er dan foutieve tekenreeksen weggeschreven worden. Dit kan je oplossen door het lange script op te splitsen in meerdere kortere heredoc structuren die je dan wegschrijft naar één script bestand.

Een handig hulpmiddel om zeker te zijn dat alles goed is verlopen is het oorspronkelijke script en het automatische opgebouwde script met een programma zoals Kompare te controleren op verschillen. Als alles goed ging, moeten beide bestanden identiek zijn.

Scripts vergelijken met Kompare