Jedes bash Script beginnt mit einer Idee, wie ein Problem zu lösen ist. Ich will dir in diesen Post zeigen, wie sich so etwas entwickelt.
Die Idee
Ich möchte dir das am Beispiel des SD-Karten Tests demonstrieren. Der Grundgedanke ist, wiederkehrende Aufgaben zu automatisieren.
In vielen Fällen ist es sinnvoll, wenn du den gesamten Ablauf einmal mit diskreten Kommandos von Hand durchspielst. Damit machst du eine Bestandsaufnahme, legst die Kommandos mit ihren Parametern und ihre Reihenfolge fest.
Nachdem die MicroSD Karte im USB-Reader eingelegt ist, zerfällt der Geschwindigkeitstest in folgende Schritte:
Schritt 1: Partition löschen und anlegen
Jede MicroSD Karte wird mit einer Partition mit VFAT Filesystem ausgeliefert. Diese müssen wir erstmal loswerden und eine neue ext4 Partition anlegen. Dies hatte ich immer manuell gemacht, bis ich auf das Programm sfdisk
aufmerksam wurde, dass wie fdisk
arbeitet, aber für den Einsatz in Scripts ausgelegt ist.
Falls sfdisk auf deinem Raspberry Pi nicht installiert ist, musst du es nachinstallieren.
Das Löschen der Partition erfolgt mit der Zeile
sfdisk --delete -w ${DEVICE} 1 && sync
${DEVICE}
enthält das Devicefile der MicroSD Karte, das im Script später als Kommandozeilenparameter übergeben wird.
Die neue Partition wird danach mit
echo ",," |sfdisk ${DEVICE} && sync
angelegt. Da sfdisk für die Partitionsanlage Tastatureingaben erwartetet, pipen wir diese mit dem vorangestellten echo einfach ins Kommando hinein. das nachlaufende sync
dient dazu, alle Änderungen auf die Speicherkarte zu schreiben.
Schritt 2: Filesystem erstellen
Die Partition ist jetzt angelegt, als nächstes muss darauf das ext4 Filesystem erstellt werden. Dazu benutzt Raspberry Pi OS das Kommando mkfs.ext4
Im Script sieht der Aufruf so aus:
echo "y" |mkfs.ext4 ${PARTITION}
Die Sicherheitsabfrage von mkfs.ext4
bestätigen wir über echo mit „y“. Die Variable ${PARTITION}
wird das Script selber setzen.
Schritt 3: Partition einhängen
Die MicroSD Karte ist jetzt vorbereitet und wir hängen sie in den Mountpoint /mnt/speeddtest
. Das Verzeichnis habe ich im Script fest kodiert und wird, falls es nicht existiert, automatisch angelegt.
#check if mount point is available, create otherwise
if [ ! -d "${MOUNTPOINT}" ]; then mkdir "${MOUNTPOINT}"; fi
mount -t ext4 "${PARTITION}" "${MOUNTPOINT}"
mount -t ext4 ${PARTITION} ${MOUNTPOINT} hängt die Partition (bei mir /dev/sda1) in den Mountpoint ein.
Schritt 4: Test vorbereiten
Auf der gemounteten Partition muss sysbench
einige Dateien für den Test anlegen.
sysbench fileio --file-total-size=8G prepare > /dev/null
Die Ausgabe interessiert an dieser Stelle nicht, deshalb wird sie mit > /dev/null ins Null-Device gepiped.
Schritt 5: Test mit 16M Blockgröße
Jetzt kann der erste Testdurchlauf mit einer Blockgröße von 16M gestartet werden.
sysbench fileio --file-block-size=16K --file-total-size=8G --file-test-mode=rndrw --threads=$(nproc) run
Die Variable ${nproc}
wird zu Beginn des Scripts definiert und gibt an, wie viele parallele Threads während des Tests benutzt werden sollen. Der Raspberry Pi hat vier Kerne, die nutzen wir auch voll aus. Im Realbetrieb greifen auch meist mehrere Prozesse gleichzeitig auf die Karte zu.
Schritt 6: Test mit 1M Blockgröße
Im zweiten Testdurchgang erhöhen wir die Blockgröße auf 1M.
sysbench fileio --file-block-size=1M --file-total-size=8G --file-test-mode=rndrw --threads=$(nproc) run
Schritt 7: Aufräumarbeiten
Nach Beendigung des Test räumt unser Script noch ein wenig auf.
sysbench fileio --file-total-size=8G cleanup
cd - >/dev/null
umount ${PARTITION}
sysbench
löscht zunächst seine Dateien wieder ab. Mit cd – springen wir wieder in der Verzeichnis, von wo es gestartet wurde und mit umount ${PARTITION} wird die Partition auf der MicroSD Karte wieder ausgehängt.
Das komplette Script
Hier ist das komplette Script im Zusammenhang. Es ist nicht besonders spektakulär, da es keine großen Verzweigungen oder Schleifen hat.
#! /usr/bin/bash
MOUNTPOINT='/mnt/speedtest'
# number of threads used during testing
nproc=4
if [ "$EUID" -ne 0 ]
then echo "Please run as root"
exit 1
fi
if [ "${1}" ]; then
DEVICE=${1}
PARTITION=${DEVICE}1
echo "using device ${DEVICE}"
else
echo "using default device ${DEVICE}"
fi
# check if sd card is inserted
if [ -e ${DEVICE} ]; then
echo "card inserted"
else
echo "no sd card found"
exit 2
fi
echo "deleting partition #1"
sfdisk --delete -w ${DEVICE} 1 && sync
echo "creating new ext4 partition"
echo ",," |sfdisk ${DEVICE} && sync
echo "creating ext4 filesystem on ${PARTITION}"
echo "y" |mkfs.ext4 ${PARTITION}
#check if mount point is available, create otherwise
if [ ! -d "${MOUNTPOINT}" ]; then mkdir "${MOUNTPOINT}"; fi
mount -t ext4 "${PARTITION}" "${MOUNTPOINT}"
cd "${MOUNTPOINT}"
echo "preparing tests"
sysbench fileio --file-total-size=8G prepare > /dev/null
# Test with 16K block size, random read/write
echo "run test with 16K block size"
sysbench fileio --file-block-size=16K --file-total-size=8G --file-test-mode=rndrw --threads=$(nproc) run
# Test with 1M block size, random read/write
echo "run test with 1M block size"
sysbench fileio --file-block-size=1M --file-total-size=8G --file-test-mode=rndrw --threads=$(nproc) run
# cleanup the test files
echo "cleaning up test files"
sysbench fileio --file-total-size=8G cleanup
cd - >/dev/null
umount ${PARTITION}
Da ich das Script ursprünglich nur für mich selbst geschrieben habe, könnte ich für eine allgemeine Nutzung durchaus noch ein paar Sachen verbessern
- Mountpoint als Parameter übergeben.
- Sicherheitsabfrage vor dem Löschen der VFAT Partition.
Ich wollte es hier mal als praktisches Beispiel für die Nutzung von Parametern, Variablen und exit-Codes hinstellen. Du findest das Script auch im git-Repo.
Testlauf
Das fertige Script muss natürlich getestet werden. Dazu rufe ich es mit sudo /home/pi/bin/sdcardtester /dev/sda
auf.
Der Pfad muss absolut eingegeben werden, da das bin Verzeichnis des Users pi nicht im PATH von root liegt. Vorsicht beim Testen, das angegebene Device wird gnadenlos überschrieben!
Nach dem Durchlauf sehen die letzten Ausgaben dann so aus: