Ga naar hoofdinhoud

Bash Scripting

Een shell script is een tekstbestand met een reeks commando's die de shell achter elkaar uitvoert. Scripts automatiseren repetitieve taken: bestanden verwerken, back-ups maken, servers configureren. Als je een reeks commando's meer dan twee keer uitvoert, schrijf je er een script voor.

Je eerste script

Maak een bestand hello.sh aan:

#!/bin/bash

echo "Hallo, wereld!"

De eerste regel — #!/bin/bash — heet de shebang. Ze vertelt het systeem welke interpreter het script moet uitvoeren. /bin/bash is dezelfde bash-shell die we eerder hebben ingesteld als standaard shell voor gebruikers (het laatste veld in /etc/passwd). Zonder de shebang weet het systeem niet welke taal het script spreekt.

Maak het script uitvoerbaar en voer het uit:

chmod +x hello.sh # Geef uitvoerrechten (zie het hoofdstuk Gebruikers & Rechten)
./hello.sh # Voer uit — de ./ geeft aan dat het in de huidige map staat

De ./ voor de naam is nodig omdat Linux scripts in de huidige map niet automatisch uitvoert (om veiligheidsredenen). Je geeft expliciet aan: "dit bestand, hier in deze map".


Variabelen

Variabelen zijn containers die een waarde bewaren, zodat je die later kunt hergebruiken zonder ze opnieuw te typen. In bash wijs je een waarde toe met = en lees je die uit met $.

naam="Linux"
echo "Hallo, $naam!" # Output: Hallo, Linux!
echo "Hallo, ${naam}!" # Met accolades — zelfde resultaat
echo "Versie: ${naam}2" # Werkt correct: "Versie: Linux2"
echo "Versie: $naam2" # Fout: bash zoekt variabele '$naam2'

Regels om te onthouden:

  • Geen spaties rond het =-teken: naam="Linux" ✓, naam = "Linux"
  • Namen zijn hoofdlettergevoelig: naam en NAAM zijn twee verschillende variabelen
  • Gebruik ${var} wanneer de variabelenaam direct gevolgd wordt door tekst

Command substitution

Met $(...) voer je een commando uit en sla je de uitvoer op als variabele. Dit is een van de krachtigste patronen in bash scripting.

datum=$(date +%Y-%m-%d)
echo "Vandaag is het $datum"
# Output: Vandaag is het 2025-06-01

aantal=$(ls | wc -l)
echo "Er staan $aantal bestanden in deze map."

gebruiker=$(whoami)
echo "Je bent ingelogd als $gebruiker"

Argumenten

Je kunt waarden meegeven aan een script via argumenten — net zoals je ls -la /etc typt met argumenten. Binnen het script zijn de argumenten beschikbaar als speciale variabelen.

#!/bin/bash
echo "Scriptnaam: $0"
echo "Eerste argument: $1"
echo "Tweede argument: $2"
echo "Alle argumenten: $@"
echo "Aantal argumenten: $#"
./script.sh Alice Bob
# Scriptnaam: ./script.sh
# Eerste argument: Alice
# Tweede argument: Bob
# Alle argumenten: Alice Bob
# Aantal argumenten: 2

Argumenten maken je script herbruikbaar. In plaats van de mapnaam hardcoded in het script te zetten, geef je hem mee als argument — zo werkt het script voor elke map.


Condities

Met if voer je code enkel uit als een bepaalde voorwaarde geldig is. De conditie staat tussen [ en ], gevolgd door then. Het blok sluit altijd af met fi (omgekeerd van if).

if [ conditie ]; then
# commando's als de conditie waar is
elif [ andere-conditie ]; then
# commando's als de eerste conditie onwaar is maar deze waar
else
# commando's als geen enkele conditie waar is
fi

Testoperatoren

De conditie test iets — een bestand, een string of een getal. Hier zijn de meest gebruikte operatoren:

Bestandstests — controleer of iets bestaat of leesbaar is:

OperatorBetekenis
-f bestandBestaat als gewoon bestand (niet als map)
-d padBestaat als map
-e padBestaat (bestand of map)
-r bestandBestaat en is leesbaar
-w bestandBestaat en is beschrijfbaar
-x bestandBestaat en is uitvoerbaar

Stringvergelijking — vergelijk stukken tekst:

OperatorBetekenis
-z "$var"String is leeg (zero length)
-n "$var"String is niet leeg
"$a" = "$b"Strings zijn gelijk
"$a" != "$b"Strings zijn ongelijk

Getalvergelijking — vergelijk getallen:

OperatorBetekenis
$a -eq $bGelijk aan (equal)
$a -ne $bNiet gelijk aan (not equal)
$a -lt $bKleiner dan (less than)
$a -gt $bGroter dan (greater than)
$a -le $bKleiner of gelijk (less or equal)
$a -ge $bGroter of gelijk (greater or equal)

Logisch combineren:

if [ -f bestand.txt ] && [ -r bestand.txt ]; then # Beide waar (AND)
echo "Bestand bestaat en is leesbaar"
fi

if [ -f a.txt ] || [ -f b.txt ]; then # Minstens één waar (OR)
echo "Minstens één van de bestanden bestaat"
fi

Voorbeeld — controleer een argument

#!/bin/bash

bestand=$1

if [ -z "$bestand" ]; then
echo "Gebruik: $0 <bestandsnaam>"
exit 1
fi

if [ -f "$bestand" ]; then
echo "Bestand gevonden: $bestand"
wc -l "$bestand"
else
echo "Bestand niet gevonden: $bestand"
exit 1
fi

Loops

Loops herhalen een blok code voor elk element in een lijst, of zolang een voorwaarde geldig is. Ze zijn onmisbaar voor het verwerken van meerdere bestanden of het uitvoeren van een taak een vast aantal keren.

for — itereren over een lijst

De for-loop doorloopt één voor één elk element in een lijst. Bij bestanden kun je wildcards gebruiken.

# Elk .txt bestand in de huidige map verwerken
for bestand in *.txt; do
echo "Verwerk: $bestand"
wc -l "$bestand"
done

# Over een expliciete lijst
for omgeving in dev staging prod; do
echo "Deploy naar $omgeving..."
done

# Een numerieke reeks
for i in {1..5}; do
echo "Stap $i van 5"
done

while — herhalen zolang de conditie geldt

De while-loop is handig om een bestand regel voor regel te lezen, of te wachten tot een bepaalde toestand verandert.

# Elke regel van een bestand verwerken
while read regel; do
echo "Verwerk: $regel"
done < bestand.txt

# Teller bijhouden
teller=0
while [ $teller -lt 5 ]; do
echo "Teller staat op $teller"
teller=$((teller + 1))
done

De $((... )) syntax gebruik je voor rekenkundige bewerkingen in bash.


Functies

Functies groeperen herbruikbare commando's onder een naam. Je roept ze aan als gewone commando's. Ze zijn ideaal als je hetzelfde blok meerdere keren nodig hebt — je schrijft de logica één keer en roept hem op waar nodig.

groet() {
local naam=$1 # 'local' beperkt de variabele tot deze functie
echo "Hallo, $naam!"
}

groet "Jan" # Output: Hallo, Jan!
groet "Marie" # Output: Hallo, Marie!

Functies kunnen ook een return-waarde teruggeven via de exit code (0 = succes, niet-nul = fout):

bestand_bestaat() {
if [ -f "$1" ]; then
return 0 # Succes (true)
else
return 1 # Fout (false)
fi
}

if bestand_bestaat "data.csv"; then
echo "Bestand gevonden, verwerking starten..."
else
echo "Bestand niet gevonden."
exit 1
fi

Foutafhandeling

Exit codes

Elk commando geeft na afloop een exit code terug: 0 betekent succes, alles anders betekent een fout. Je scripts kunnen dit ook gebruiken om aan te geven of ze gelukt zijn.

ls bestand.txt
echo "Exit code: $?" # $? bevat de exit code van het laatste commando

exit 0 # Script met succes beëindigen
exit 1 # Script met een fout beëindigen

set -e — automatisch stoppen bij fouten

Zonder extra configuratie gaat bash gewoon door na een fout — ook als een tussenstap mislukt is. Dat kan leiden tot onverwacht gedrag. set -e (exit on error) stopt het script zodra een commando faalt.

#!/bin/bash
set -e

mkdir backup
cp data.csv backup/ # Als dit mislukt, stopt het script hier
gzip backup/data.csv
echo "Back-up geslaagd" # Dit wordt nooit uitgevoerd als cp mislukt

Zonder set -e zou het script doorgaan na een mislukte cp en proberen een niet-bestaand bestand te comprimeren. Met set -e stopt het netjes op de fout.


Praktisch voorbeeld: back-up script

Dit script combineert alles: argumenten, condities, functies en archieven.

#!/bin/bash
set -e

# Gebruik: ./backup.sh <bronmap> <doelmap>
BRON=$1
DOEL=$2
DATUM=$(date +%Y-%m-%d)
ARCHIEF="$DOEL/backup-$DATUM.tar.gz"

controleer_argumenten() {
if [ -z "$BRON" ] || [ -z "$DOEL" ]; then
echo "Gebruik: $0 <bronmap> <doelmap>"
exit 1
fi
if [ ! -d "$BRON" ]; then
echo "Fout: bronmap '$BRON' bestaat niet."
exit 1
fi
}

maak_back_up() {
mkdir -p "$DOEL"
tar -czf "$ARCHIEF" "$BRON"
echo "Back-up aangemaakt: $ARCHIEF"
}

controleer_argumenten
maak_back_up
chmod +x backup.sh
./backup.sh projecten/ /mnt/backup/