Tips en Trucs 2012

Bash for lussen

Sommige opdrachten gebruik je zoveel dat het een sleur wordt om ze telkens opnieuw uit te voeren. Bestanden verplaatsen, zoeken in bestanden of het aanmaken van gebruikers zijn daar voorbeelden van.

Je kunt veel tijd winnen door zo'n herhalende opdrachten uit te voeren in een lus. Hoewel er drie soorten lussen bestaan (for, while en until) bespreek ik in deze tip enkel mijn favoriete for lus.

Een for lus gebruikt de volgende syntax (schrijfwijze):
for plaatshouder in x y z ; do opdracht plaatshouder ; done

De plaatshouder mag je om het even welke naam geven. Veel mensen gebruiken i of teller als plaatshouder (variabele), maar daar ben je vrij in. x y z bevat de informatie die je aan de opdracht wilt meegeven. Vergeet ook niet na het uitvoeren van de opdracht de lus met done te beëindigen.

Het volgende voorbeeld toont hoe je een eenvoudige afteller met een for lus kunt maken:
for i in 10 9 8 7 6 5 4 3 2 1 ; do echo $i ; done

Zoals je ziet, gebruiken we i als plaatshouder. In deze plaatshouder geven we de getallen 10 tot en met 1 door aan de opdracht echo. In Bash 4 kan je de getallen vervangen door {10..1}. Bash 4 heeft namelijk een ingebouwde ondersteuning voor waarden in stappen {START..EINDE..STAP}.

In het volgende voorbeeld laten we de computer in stappen van 5 luidop tellen tot 100. De eerste regel is enkel nodig indien espeak nog niet op de computer (openSUSE) geïnstalleerd is.
sudo zypper install espeak
for x in {5..100..5} ; do echo $x | espeak ; done

In deze lus gebruiken we de variabele x. Deze start met de waarde 5 en telt door in stappen van 5 tot 100. Via de opdracht echo wordt deze waarde aan espeak doorgegeven waardoor de computer het getal in het Engels uitspreekt.

En nu een praktisch systeembeheer voorbeeld. Een tekstbestand bevat een lijst met linux servers (computers) die je beheert en waar je een bestand wilt naartoe sturen.
linux01 192.168.1.1
linux05 192.168.1.5
linux10 192.168.1.10
linux20 192.168.1.20

Met een for lus is dit zeer eenvoudig en snel. We gaan de inhoud van het tekstbestand met de servers met de opdracht cat aan de opdracht awk doorgeven om de ip adressen van de servers te achterhalen. De cat opdracht geeft daarbij de inhoud via een pipe (|) door aan de awk opdracht. Daar we het resultaat van deze twee opdrachten gebruiken in de for lus moeten we deze opdrachten binnen backquotes (`) plaatsen.
for f in `cat linux_servers | awk '{ print $2}'` ; do scp pakket.rpm gebruiker@$f:/map ; done

Deze lus werkt enkel indien de servers via ssh en publieke sleutels toegankelijk zijn (zie de tip Kennismaken met rsync bij Back-ups zonder toezicht), het bestand pakket.rpm op de lokale computer bestaat en de map /dir op de externe server bestaat. De lus gebruikt de plaatshouder f die respectievelijk de vier ip adressen nodig om via de opdracht scp het bestand pakket.rpm naar de vier servers te kopiëren bevat.

Als je niet zeker bent van het resultaat van de cat en awk opdracht, gebruik dan een lusopdracht (echo $f) om dit resultaat te bekijken.
for f in `cat linux_servers | awk '{ print $2}'` ; do echo $f ; done

Nu het bestand pakket.rpm op de servers staat, kun je een opdracht naar de servers sturen om het bestand te installeren als update.
for r in `cat linux_servers | awk '{ print $2}'` ; do ssh gebruiker@$r rpm -Uvh /map/pakket.rpm ; done

Op openSUSE servers kan je i.p.v. de rpm opdracht, gebruik maken van de opdracht zypper om de update uit te voeren. Om te controleren of de update uitgevoerd is, kun je de rpm database controleren door het resultaat van de opdracht rpm -qa via een pipe met de opdracht grep te doorzoeken.
cat linux_servers | awk '{ print $2}'` ; do ssh gebruiker@$r rpm -qa | grep pakket ; done

Om nog sneller te werken kun je alle opdrachten in een script plaatsen.
#!/bin/bash
servers=”linux_server”
files=”te_verzenden_bestanden”

# Hier verzenden we de bestanden
for f in `cat $servers | awk '{ print $2}'` ; do scp $files user@$f:/dir ; done

# Hier installeren we de verzonden bestanden
for r in `cat $servers | awk '{ print $2 }` ; do ssh user@$r rpm -Uvh /dir/$files ; done

# Hier controleren we of de installatie van de verzonden bestanden is gelukt.
for x in `cat $servers | awk '{ print $2 }` ; do ssh user@$x rpm-qa | grep $files ; done

exit 0

Een goede systeembeheerder vindt steeds een eenvoudige methode om steeds terugkerende taken snel af te handelen, waardoor tijd vrijkomt om complexere problemen aan te pakken. Daarbij kan het gebruik van lussen zeker helpen.