Übungsaufgaben zu Skripten zur Systemautomatisierung

https://bildung.social/@oerinformatik/

https://oer-informatik.de/skripte-uebungsaufgaben

Skripte, Systemautomatisierung, PowerShell, Bash, Python, JSON, cron, Struktogramm, Übungsaufgaben, Prüfungsvorbereitung

tl/dr; (ca. 88 min Lese- und Bearbeitungszeit): Eine umfangreiche Übungsaufgabe: Für einen Fahrrad-Onlineshop automatisierst Du Routinearbeiten. Du erzeugst aus einem JSON-Export eine Ladeliste, verfolgst und korrigierst ein Struktogramm zum Abgleich von DHCP-Zuweisungen mit dem Inventar, bildest Benutzernamen aus einer CSV-Datei, kommentierst ein Sicherungsskript, korrigierst ein fehlerhaftes Prüfskript mit englischer Befehlsreferenz, findest Fehler in einem find-Befehl und erweiterst ihn, überwachst einen Webshop und beurteilst die Sicherheit von Skripten. Alle Lösungen sind ausgeblendet und mit Punkteverteilung versehen [AI-Assisted]. (Zuletzt geändert am 29.10.2026)

Diese Übungsaufgabe gehört zum Artikel Skripte zur Systemautomatisierung.

Hinweise zur Bearbeitung: Als FIA musst Du zur Teil-2-Prüfung Aufgaben auf Kernkompetenz-Ebene lösen können, das ist Aufgabe 2.1. Wenn nichts anderes verlangt ist, wählst Du die Notation selbst: Pseudocode, PowerShell, Bash oder Python. Bewertet wird die Logik, nicht jedes Satzzeichen – kommentiere Deine Schritte, damit die Logik erkennbar ist. Schreib Deine Lösung zuerst vollständig auf, bevor Du die Beispiellösung aufklappst.

Die Codebeispiele kannst Du in vier Notationen anzeigen. Die Notationen sind im Artikel Algorithmen mit Pseudocode dokumentieren beschrieben.

laden: integer := 0

Du möchtest eigene Aufgaben zum Üben von einem KI-Sprachmodell generieren lassen?

Kopiere den Text in ein KI-Sprachmodell Deiner Wahl und passe Kompetenzstufe, Anzahl, Bearbeitungszeit und Szenario bei Bedarf an. Wie die Aufgaben aufgebaut sind, beschreibt der Artikel Wie sind die Aufgaben aufgebaut?. Prüfe die Ergebnisse mit dem Lehrartikel: KI-Sprachmodelle machen Fehler.

Erstelle neue Übungsaufgaben zum Thema „Skripte zur
Systemautomatisierung“.

Bauplan: Halte Dich an Aufbau, Kompetenzstufen, Operatoren,
Anforderungsbereiche, Punkte und Zeitschätzung, die im Artikel
https://oer-informatik.de/aufgaben-aufbau beschrieben sind.

Fachliche Grundlage:
- https://oer-informatik.de/skripte
- Stilbeispiel: https://oer-informatik.de/skripte-uebungsaufgaben

Die Aufgaben sollen Inhalte zu diesen Schlagwörtern abdecken:
- Skripte
- Systemautomatisierung
- PowerShell
- Bash
- Python
- JSON
- cron
- Struktogramm

Kompetenzstufe: Kernkompetenz (alternativ: Basiskompetenz, Profilkompetenz, Expertise)
Anzahl: 1 Aufgabe mit Teilaufgaben
Bearbeitungszeit in Minuten ca.: <5 bis 30 min>
Szenario: eines der Szenarien aus dem Bauplan

Vorgehen:
1. Lies zuerst die verlinkten Artikel. Kannst Du keine Webseiten
   abrufen, sag mir das, statt zu raten.
2. Steht bei einer Angabe noch ein Platzhalter in <…>, frag mich
   zuerst danach. Richte Punkte und Zahl der Teilaufgaben nach der
   Bearbeitungszeit (ca. 0,9 min je Punkt).
3. Gib je Aufgabe Titel mit Kompetenzstufe, Ausgangssituation und
   Teilaufgaben mit Operator und Punktzahl aus, dazu die geschätzte
   Bearbeitungszeit.
4. Gib die Beispiellösungen mit Punkteverteilung erst aus, wenn ich
   danach frage.

Aufgabe 2.1: Routinearbeiten in der IT eines Fahrrad-Onlineshops automatisieren (Kernkompetenz)

Die CSB-Rad GmbH in Bielefeld verkauft Fahrräder und Komponenten über einen Onlineshop und lässt Rahmen nach Maß fertigen. Du bist Auszubildende*r der Fachinformatik und arbeitest zusammen mit Deinem Mit-Azubi Jonas für den Geschäftsführer Technik und IT, Cem Sarıkaya.

Nach einem Sicherheits-Check wurde die IT im Werk Sennestadt umgebaut: Die Daten liegen jetzt auf einem neuen NAS (Linux-basiert, Hostname nas-sen), die Handscanner (MDE-Geräte) im Lager und die Tablets der Werkstatt hängen in einem eigenen Netz 192.168.20.0/24, und statt des gemeinsam genutzten Kontos admin bekommt jede Person ein eigenes Konto im Verzeichnisdienst. Cem Sarıkaya möchte, dass wiederkehrende Arbeiten künftig von Skripten erledigt werden: „Was wir jeden Morgen von Hand prüfen, vergessen wir irgendwann.“

a) Jonas würde die Werkzeuge lieber als C#-Programm schreiben: „Skriptsprachen sind doch nur Spielerei.“

aa) Erläutere je einen Vorteil einer Skriptsprache und einer Compilersprache für die Automatisierung administrativer Aufgaben. (4 Punkte)

Je 2 Punkte für einen erläuterten Vorteil (Vorteil nennen 1 Punkt, Begründung 1 Punkt), zum Beispiel:

Skriptsprache (PowerShell, Bash, Python):

  • Änderungen sind sofort wirksam, weil der Quelltext zur Laufzeit von einem Interpreter ausgeführt wird; es muss nicht neu kompiliert und verteilt werden. Das passt zu Verwaltungsaufgaben, die sich häufig ändern (neuer Pfad, neue Schwelle).

  • Gute Eignung für systemnahe Aufgaben: Befehle des Betriebssystems (Konten anlegen, Dateien kopieren, Dienste prüfen) lassen sich direkt aufrufen und verketten.

  • Der Quelltext ist auf dem Zielsystem lesbar und damit leicht prüfbar und dokumentiert.

Compilersprache (C#, Java, C++):

  • Übersetzte Programme laufen meist schneller – wichtig bei rechenintensiven Aufgaben oder sehr großen Datenmengen.

  • Der Compiler findet Syntax- und Typfehler vor dem ersten Lauf, statt dass das Skript erst mitten in der Ausführung abbricht.

  • Leistungsfähige Entwicklungsumgebungen (Debugger, Refactoring, Tests) unterstützen größere, langlebige Programme.

ab) Nenne zwei weitere Routineaufgaben aus der Systembetreuung, die sich gut mit einem Skript automatisieren lassen. (2 Punkte)

Je 1 Punkt, zum Beispiel:

  • Benutzerkonten aus einer Liste anlegen, sperren oder löschen

  • Datensicherung erstellen, prüfen und alte Sicherungen löschen

  • Speicherplatz, Erreichbarkeit von Geräten und Diensten oder Ablaufdaten von Kennwörtern und Zertifikaten überwachen und bei Grenzwerten warnen

  • Geräte im Netz inventarisieren

  • Protokolldateien auswerten oder archivieren, temporäre Dateien aufräumen

  • Software-Updates oder Konfigurationen auf viele Rechner verteilen

ac) Welche der folgenden Aussagen über Skriptsprachen trifft zu? Gib die Ziffer an und begründe kurz. (2 Punkte)

  1. Skriptsprachen können nur Befehle der Kommandozeile aneinanderreihen, Schleifen und Verzweigungen gibt es nicht.

  2. Skripte werden meist von einem Interpreter ausgeführt, der auf dem Zielsystem vorhanden sein muss.

  3. Skriptsprachen werden ausschließlich in der Webprogrammierung eingesetzt.

  4. Skripte laufen nur auf dem Prozessor, für den sie geschrieben wurden.

Aussage 2 trifft zu (1 Punkt): Der Quelltext wird zur Laufzeit von einem Interpreter (z. B. bash, powershell.exe/pwsh, python3) gelesen und ausgeführt; fehlt der Interpreter auf dem Zielsystem, läuft das Skript nicht (1 Punkt Begründung).

Die anderen Aussagen sind falsch: Skriptsprachen haben Variablen, Schleifen, Verzweigungen und Funktionen (1), werden vor allem auch in der Systemverwaltung, Datenanalyse und Automatisierung eingesetzt (3) und sind gerade nicht an einen Prozessor gebunden, weil der Interpreter die Übersetzung übernimmt (4).

b) Die fünf MDE-Geräte im Lager und die vier Tablets der Werkstatt werden über eine Geräteverwaltung (Mobile Device Management, MDM) betreut. Sie legt jeden Morgen um 5:30 Uhr einen Export C:\Skripte\mdm_export.json ab, der für jedes Gerät den zuletzt gemeldeten Akkustand und die Minuten seit der letzten Meldung enthält. Ein Ausschnitt:

[
  {"inventarnr": "MDE-02", "typ": "MDE",    "akku": 22, "minutenSeitKontakt": 5},
  {"inventarnr": "TAB-W2", "typ": "Tablet", "akku": 55, "minutenSeitKontakt": 600},
  …
]

Die Schichtleitung will vor Schichtbeginn keine vollständige Übersicht, sondern nur eine kurze Liste der Geräte, um die sich jemand kümmern muss:

  • Handscanner mit weniger als 25 % und Tablets mit weniger als 40 % Akku kommen an die Ladestation (laden).

  • Hat sich ein Gerät seit mehr als 60 Minuten nicht gemeldet, ist der gespeicherte Akkustand nicht mehr aussagekräftig: Das Gerät muss gesucht werden (suchen) – unabhängig vom Akkustand.

  • Geräte ohne Handlungsbedarf erscheinen nicht in der Liste.

ba) Entwirf ein Skript, das aus dem Export die Datei C:\Berichte\ladeliste.csv mit den Spalten Inventarnr und Aktion (Trennzeichen ;) erzeugt und zum Schluss meldet, wie viele Geräte zu laden und wie viele zu suchen sind. Die Notation wählst Du selbst. (10 Punkte)

Punkteverteilung:

Bewertung Teilaufgabe ba)
Teil Punkte
Export einlesen und in eine Liste von Objekten umwandeln 1
Schleife über alle Geräte 1
Grenzwert abhängig vom Gerätetyp (25 bzw. 40) 2
Zuerst suchen (> 60 Minuten) prüfen, nur sonst laden (< Grenzwert) 2
Nur Geräte mit Handlungsbedarf in die Ergebnisliste aufnehmen 1
CSV-Datei mit Kopfzeile und Trennzeichen ; schreiben 2
Zähler für laden und suchen und Ausgabe am Ende 1

Lösung in PowerShell:

# Ladeliste fuer die Schichtleitung aus dem MDM-Export erzeugen
$geraete = Get-Content -Path "C:\Skripte\mdm_export.json" -Raw | ConvertFrom-Json
$liste   = @()                             # nur Geraete mit Handlungsbedarf
$laden   = 0
$suchen  = 0

foreach ($g in $geraete) {
    if ($g.typ -eq "MDE") { $grenze = 25 } else { $grenze = 40 }
    if ($g.minutenSeitKontakt -gt 60) {     # Meldung zu alt: Akkuwert unbrauchbar
        $liste += [PSCustomObject]@{ Inventarnr = $g.inventarnr; Aktion = "suchen" }
        $suchen++
    } elseif ($g.akku -lt $grenze) {
        $liste += [PSCustomObject]@{ Inventarnr = $g.inventarnr; Aktion = "laden" }
        $laden++
    }
}

$liste | Export-Csv -Path "C:\Berichte\ladeliste.csv" -Delimiter ";" -NoTypeInformation -Encoding utf8
Write-Host "$laden Geraete laden, $suchen Geraete suchen"

Lösung in Python:

import csv
import json

with open(r"C:\Skripte\mdm_export.json", encoding="utf-8") as datei:
    geraete = json.load(datei)             # Liste von Dictionaries

liste = []
laden = 0
suchen = 0
for g in geraete:
    grenze = 25 if g["typ"] == "MDE" else 40
    if g["minutenSeitKontakt"] > 60:
        liste.append([g["inventarnr"], "suchen"])
        suchen += 1
    elif g["akku"] < grenze:
        liste.append([g["inventarnr"], "laden"])
        laden += 1

with open(r"C:\Berichte\ladeliste.csv", "w", encoding="utf-8", newline="") as datei:
    schreiber = csv.writer(datei, delimiter=";")
    schreiber.writerow(["Inventarnr", "Aktion"])
    schreiber.writerows(liste)
print(f"{laden} Geraete laden, {suchen} Geraete suchen")

Lösung in Pseudocode:

Prozedur ladeliste()
    geraete := leseJSON("C:\Skripte\mdm_export.json")
    liste := leere Liste
    laden: integer := 0
    suchen: integer := 0
    grenze: integer := 0
    für jedes g in geraete
        wenn g.typ = "MDE" dann
            grenze := 25
        sonst
            grenze := 40
        Ende wenn
        wenn g.minutenSeitKontakt > 60 dann
            liste.anhaengen([g.inventarnr, "suchen"])
            suchen := suchen + 1
        sonst wenn g.akku < grenze dann
            liste.anhaengen([g.inventarnr, "laden"])
            laden := laden + 1
        Ende wenn
    Ende für
    schreibeCSV("C:\Berichte\ladeliste.csv", ";", ["Inventarnr", "Aktion"], liste)
    Ausgabe laden & " Geräte laden, " & suchen & " Geräte suchen"
Ende ladeliste

Bewertungshinweise:

  • Die Bedingung suchen muss vor der Akkuprüfung stehen. Sonst landet ein verschwundener Handscanner mit altem, niedrigem Akkuwert als laden auf der Liste – und niemand sucht ihn.

  • „Weniger als“ heißt <: Ein Handscanner mit genau 25 % und ein Tablet mit genau 40 % stehen nicht auf der Liste; ein Gerät, das sich vor genau 60 Minuten gemeldet hat, gilt noch nicht als verschwunden.

  • In PowerShell kennt Export-Csv die Spalten nur aus den übergebenen Objekten: Bei einer leeren Liste entsteht keine Kopfzeile. Ein robustes Skript legt dann selbst eine Datei mit Kopfzeile an oder meldet ausdrücklich „keine Geräte“.

Probe mit einem vollständigen Export:

Probe für die Ladeliste
Gerät Typ Akku Minuten seit Meldung Aktion
MDE-01 MDE 64 3 –
MDE-02 MDE 22 5 laden
MDE-03 MDE 12 145 suchen
MDE-04 MDE 25 2 –
MDE-05 MDE 9 12 laden
TAB-W1 Tablet 38 7 laden
TAB-W2 Tablet 55 600 suchen
TAB-W3 Tablet 40 1 –
TAB-W4 Tablet 71 60 –

Ausgabe: 3 Geraete laden, 2 Geraete suchen. Die Datei ladeliste.csv enthält die Kopfzeile und fünf Zeilen, z. B. MDE-03;suchen.

bb) Cem Sarıkaya möchte außerdem wissen, ob sich im Netz 192.168.20.0/24 Geräte angemeldet haben, die nicht im Inventar stehen. Der DHCP-Server liefert dazu die aktuellen Adresszuweisungen als Liste leases; jeder Eintrag hat die Felder mac, ip und hostname (leer, wenn das Gerät keinen Namen übermittelt). Die Liste inventar enthält die MAC-Adressen aller bekannten Geräte in Kleinbuchstaben, enthaelt(liste, wert) liefert true, wenn der Wert in der Liste vorkommt. Jonas hat die Funktion so entworfen:

Struktogramm der Funktion fremdeGeraete: Schleife über alle Leases, Prüfung, ob die MAC-Adresse im Inventar fehlt, und je nach leerem Hostnamen zwei Varianten des Listeneintrags (mit KI erstellt)

Für einen Test enthält inventar die Adressen 00:00:5e:00:53:01 bis 00:00:5e:00:53:09. Der DHCP-Server meldet:

Adresszuweisungen des DHCP-Servers beim Test
mac ip hostname
00:00:5e:00:53:01 192.168.20.101 mde-01
00:00:5E:00:53:04 192.168.20.104 mde-04
00:00:5e:00:53:2a 192.168.20.187 android-7f3c
00:00:5e:00:53:07 192.168.20.112 tab-w2
00:00:5e:00:53:b7 192.168.20.193

Verfolge den Ablauf und gib an, welche Liste die Funktion zurückgibt. (4 Punkte)

Rückgabe (je richtiger Eintrag 1 Punkt, 1 Punkt dafür, dass :01 und :07 fehlen):

["00:00:5E:00:53:04 mde-04", "00:00:5e:00:53:2a android-7f3c", "00:00:5e:00:53:b7 (ohne Namen)"]

:01 und :07 stehen im Inventar und werden übersprungen. Für :b7 ist der Hostname leer, deshalb greift der Ja-Zweig der inneren Verzweigung.

bc) Einer der Einträge ist ein Fehlalarm. Erkläre die Ursache und beschreibe, welche Anweisung Du an welcher Stelle im Struktogramm ergänzen bzw. ändern musst. (3 Punkte)

  • Ursache: MDE-04 ist bekannt, der DHCP-Server meldet die MAC-Adresse aber in Großbuchstaben (00:00:5E:…). Der Vergleich in enthaelt beachtet Groß- und Kleinschreibung, deshalb gilt das Gerät als fremd (1 Punkt).

  • Änderung: als erste Anweisung in der Schleife mac := kleinbuchstaben(l.mac) einfügen (1 Punkt) und danach in der Bedingung und in beiden Listeneinträgen mac statt l.mac verwenden (1 Punkt).

Gleichwertig: die Inventarliste und die gemeldete Adresse vor dem Vergleich in eine gemeinsame Schreibweise bringen (auch Trennzeichen wie - statt : vereinheitlichen).

Korrigiertes Struktogramm der Funktion fremdeGeraete mit Umwandlung der MAC-Adresse in Kleinbuchstaben zu Beginn der Schleife (mit KI erstellt)

bd) Jonas ruft die Funktion im Hauptskript auf und möchte danach mit ausgabe(fremd) die Liste anzeigen. Das Skript meldet, dass fremd nicht definiert ist. Erläutere die Ursache und wie Jonas an das Ergebnis kommt. (3 Punkte)

  • fremd ist eine lokale Variable der Funktion: Sie existiert nur innerhalb der Funktion, solange diese läuft, und ist von außen nicht sichtbar (1 Punkt).

  • Das ist gewollt: Lokale Variablen verhindern, dass Funktionen versehentlich Werte im Hauptskript oder in anderen Funktionen überschreiben, und machen Funktionen wiederverwendbar. Globale Variablen wären überall les- und änderbar, damit aber auch überall versehentlich veränderbar (1 Punkt).

  • Lösung: den Rückgabewert der Funktion einer Variablen zuweisen, z. B. neu := fremdeGeraete(leases, inventar) und dann ausgabe(neu) (1 Punkt).

be) Jonas schlägt vor, auch die Ladeliste als JSON-Datei zu speichern statt als CSV – „dann ist alles im selben Format wie der Export“. Welches Format passt besser für die Ladeliste? Triff eine Wahl und wäge dabei einen Vorteil und einen Nachteil des gewählten Formats ab. (4 Punkte)

Wahl mit einem Vorteil und einem Nachteil, je 2 Punkte. Beide Entscheidungen sind möglich, zum Beispiel:

CSV

  • Pro: einfach aufgebaut, platzsparend, von der Schichtleitung direkt in jeder Tabellenkalkulation zu öffnen, zu sortieren und auszudrucken; für zwei Textspalten reicht das völlig.

  • Kontra: nicht einheitlich genormt (Trennzeichen, Anführungszeichen, Zeichenkodierung, Dezimalkomma), keine Datentypen (alles ist Text), keine Verschachtelung – soll die Liste später z. B. den Akkuverlauf je Gerät enthalten, stößt CSV an Grenzen.

JSON

  • Pro: Datentypen bleiben erhalten (Zahl, Text, true/false, null), verschachtelte Daten sind möglich, und Webanwendungen, Dashboards oder andere Skripte können JSON ohne eigene Umwandlung einlesen – das MDM liefert ja bereits JSON.

  • Kontra: Für Menschen weniger übersichtlich; in der Tabellenkalkulation nur über einen Import zu öffnen; mehr Speicherbedarf durch die wiederholten Schlüsselnamen.

c) Für die Handscanner bekommen fünf Lagerbeschäftigte eigene Konten. Ein Skript soll sie aus der Datei lager_neu.csv anlegen:

Vorname;Nachname;Abteilung
Uwe;Brinkmann;Lager
Svetlana;Krüger;Lager
Malte;Groß;Lager
Aylin;Kaya;Lager
Jörn;Möller;Lager

Der Benutzername wird nach folgender Regel gebildet: erster Buchstabe des Vornamens und der Nachname, alles in Kleinbuchstaben; ä, ö, ü werden durch ae, oe, ue ersetzt und ß durch ss; Leerzeichen und Bindestriche werden entfernt.

ca) Schreibe eine Funktion benutzername(vorname, nachname), die den Benutzernamen nach dieser Regel zurückgibt. Du darfst übliche Zeichenkettenfunktionen wie teilzeichenkette, kleinbuchstaben und ersetze verwenden. (6 Punkte)

Punkteverteilung: Funktionskopf mit zwei Parametern und Rückgabe 1 Punkt, erster Buchstabe des Vornamens 1 Punkt, Verkettung mit dem Nachnamen 1 Punkt, Kleinschreibung 1 Punkt, Ersetzen der vier Sonderzeichen 1 Punkt, Entfernen von Leerzeichen und Bindestrichen 1 Punkt.

Pseudocode:

Funktion benutzername(vorname: string, nachname: string): string
    name: string := kleinbuchstaben(teilzeichenkette(vorname, 0, 1) & nachname)
    name := ersetze(name, "ä", "ae")
    name := ersetze(name, "ö", "oe")
    name := ersetze(name, "ü", "ue")
    name := ersetze(name, "ß", "ss")
    name := ersetze(name, " ", "")
    name := ersetze(name, "-", "")
    Rückgabe name
Ende benutzername

PowerShell:

function Get-Benutzername {
    param([string]$Vorname, [string]$Nachname)
    $name = ($Vorname.Substring(0, 1) + $Nachname).ToLower()   # Initiale + Nachname, klein
    $name = $name -replace 'ä', 'ae' -replace 'ö', 'oe' -replace 'ü', 'ue' -replace 'ß', 'ss'
    $name = $name -replace '[ -]', ''                          # Leerzeichen und Bindestriche entfernen
    return $name
}

Python:

def benutzername(vorname: str, nachname: str) -> str:
    name = (vorname[0] + nachname).lower()            # Initiale + Nachname, klein
    for alt, neu in (("ä", "ae"), ("ö", "oe"), ("ü", "ue"), ("ß", "ss")):
        name = name.replace(alt, neu)
    return name.replace(" ", "").replace("-", "")     # Leerzeichen und Bindestriche entfernen

Erst in Kleinbuchstaben umwandeln, dann ersetzen: So reicht je eine Ersetzung für ä und Ä (ein Vorname wie „Ümit“ ergibt ue…).

cb) Gib die Benutzernamen für Svetlana Krüger, Malte Groß und Jörn Möller an. (3 Punkte)

Je 1 Punkt: skrueger, mgross, jmoeller.

cc) Erläutere, warum Benutzernamen und E-Mail-Adressen ohne Umlaute gebildet werden. (2 Punkte)

Je 1 Punkt für zwei Gründe, zum Beispiel:

  • Nicht alle Systeme und Protokolle verarbeiten Umlaute zuverlässig: Ältere Anwendungen, Datenbanken oder Mailserver nutzen andere Zeichenkodierungen; E-Mails an Adressen mit Umlauten (internationalisierte Adressen) werden nicht überall zugestellt.

  • Personen mit einem Tastaturlayout ohne Umlaute (z. B. Gäste, ausländische Partner, Bildschirmtastaturen der MDE-Geräte) können solche Namen nur umständlich eingeben.

  • Uneinheitliche Schreibweisen (Groß, Gross, GROSS) führen zu doppelten oder nicht gefundenen Konten.

cd) Im nächsten Jahr soll Maike Groß eingestellt werden. Beschreibe, welches Problem das Skript dann hat, und wie es gelöst werden kann. (2 Punkte)

  • Problem (1 Punkt): Für Maike Groß ergibt sich ebenfalls mgross. Benutzernamen müssen eindeutig sein; das Anlegen schlägt fehl oder – schlimmer – das Skript ändert das vorhandene Konto von Malte Groß.

  • Lösung (1 Punkt): Vor dem Anlegen prüfen, ob der Name schon existiert, und dann z. B. eine fortlaufende Zahl anhängen (mgross2) oder einen weiteren Buchstaben des Vornamens verwenden (magross); zumindest den Fall protokollieren und zur manuellen Bearbeitung melden.

d) Auf dem NAS sichert das folgende Bash-Skript jede Nacht die Daten auf eine USB-Festplatte. Es wurde vom Lieferanten des NAS eingerichtet.

#!/bin/bash
# Schritt 1
QUELLE="/volume1/daten"
ZIEL="/mnt/usb-sicherung"
DATUM=$(date +%F)
LOG="/var/log/sicherung.log"

# Schritt 2
if ! mountpoint -q "$ZIEL"; then
    echo "$DATUM FEHLER: Sicherungsplatte nicht eingehaengt" >> "$LOG"
    exit 1
fi

# Schritt 3
tar -czf "$ZIEL/daten_$DATUM.tar.gz" "$QUELLE"

# Schritt 4
sha256sum "$ZIEL/daten_$DATUM.tar.gz" > "$ZIEL/daten_$DATUM.sha256"

# Schritt 5
find "$ZIEL" -name "daten_*" -mtime +13 -delete

# Schritt 6
echo "$DATUM OK: daten_$DATUM.tar.gz erstellt" >> "$LOG"

Auszug aus der Befehlsreferenz:

date +%F prints the current date in the format YYYY-MM-DD. mountpoint -q DIR returns 0 if DIR is a mount point, otherwise a non-zero value; -q suppresses all output. tar -czf ARCHIVE FILES creates (c) a gzip-compressed (z) archive file (f). sha256sum FILE prints the SHA-256 checksum of FILE. find DIR -name PATTERN -mtime +N -delete deletes files below DIR whose names match PATTERN and whose data was last modified more than N days ago (fractional days are ignored, so +13 means at least 14 days).

da) Kommentiere für die Dokumentation die Schritte 1 bis 6. (6 Punkte)

Je 1 Punkt:

Kommentare zum Sicherungsskript
Schritt Kommentar
1 Variablen festlegen: Quellordner der Daten, Zielordner auf der USB-Platte, aktuelles Datum im Format JJJJ-MM-TT (für Dateinamen) und Pfad der Protokolldatei.
2 Prüfen, ob die USB-Platte unter dem Zielordner eingehängt ist. Falls nicht: Fehler mit Datum ins Protokoll schreiben und das Skript mit Exit-Code 1 (Fehler) beenden – sonst würde die Sicherung auf die interne Platte geschrieben.
3 Den Quellordner in ein gzip-komprimiertes Archiv daten_<Datum>.tar.gz auf der USB-Platte packen (Vollsicherung).
4 Die SHA-256-Prüfsumme des Archivs berechnen und in einer eigenen Datei speichern, um später prüfen zu können, ob das Archiv unverändert und unbeschädigt ist.
5 Alte Sicherungen (Archive und Prüfsummendateien), deren Änderung mindestens 14 Tage zurückliegt, löschen, damit die Platte nicht vollläuft (Rotation).
6 Erfolgreiche Sicherung mit Datum und Dateinamen im Protokoll vermerken (Anhängen mit >>).

db) Gib an, wie viele Tagessicherungen das Skript bei täglicher Ausführung ungefähr vorhält, und begründe Deine Angabe. (2 Punkte)

Etwa 14 Sicherungen (1 Punkt): Schritt 5 löscht nur Dateien, die mindestens 14 volle Tage alt sind. Nach dem Lauf bleiben die heutige Sicherung und die der 13 Vortage übrig (1 Punkt). Weil die Archive nicht jede Nacht auf die Minute gleich fertig werden, kann an der Grenze auch einmal eine 15. Sicherung übrig bleiben; diese Angabe ist ebenfalls richtig.

dc) Cem Sarıkaya kopiert das Skript in einen neuen Ordner und startet es mit ./sicherung.sh. Die Shell antwortet mit Permission denied. ls -l zeigt -rw-r--r-- 1 root root 512 sicherung.sh. Gib den Befehl an, der nur dem Besitzer das Ausführungsrecht gibt, und erläutere die Aufgabe der ersten Zeile #!/bin/bash. (3 Punkte)

  • chmod u+x sicherung.sh (gleichwertig: chmod 744 sicherung.sh) – 2 Punkte. Danach zeigt ls -l -rwxr--r--.

  • Die erste Zeile (Shebang) legt fest, welcher Interpreter das Skript ausführt, wenn es direkt mit ./sicherung.sh gestartet wird – hier die Bash (1 Punkt).

dd) Das Skript soll von cron montags bis samstags um 23:30 Uhr gestartet werden. Es liegt unter /volume1/skripte/sicherung.sh. Gib die Zeile für die crontab an. (3 Punkte)

crontab(5), excerpt: Each line has five time and date fields followed by the command: minute (0–59), hour (0–23), day of month (1–31), month (1–12), day of week (0–7, where both 0 and 7 mean Sunday). A field may be an asterisk (*), which stands for “every value”. Ranges of numbers are allowed, e.g. 8-11, and lists, e.g. 1,3,5.

30 23 * * 1-6 /volume1/skripte/sicherung.sh

1 Punkt für Minute und Stunde (30 23), 1 Punkt für die Wochentage 1-6 (gleichwertig 1,2,3,4,5,6) mit * für Tag und Monat, 1 Punkt für den vollständigen Pfad des Skripts. Auf einem Windows-Server übernimmt die Aufgabenplanung diese Rolle.

e) Natalia Wróbel aus der Buchhaltung soll jeden Morgen sehen, ob die Sicherung gelaufen ist. Jonas hat dazu das folgende PowerShell-Skript geschrieben:

1  $Ordner  = "\\nas-sen\sicherung"
2  $Neueste = Get-ChildItem -Path $Ordner -Filter "daten_*.tar.gz" |
3             Sort-Object -Property LastWriteTime |
4             Select-Object -First 1
5  $Alter   = (Get-Date) - $Neueste.LastWriteTime
6  if ($Alter.TotalHours > 26) {
7      Write-Warning "Letzte Sicherung ist $([int]$Alter.TotalHours) Stunden alt!"
8  } else {
9      Write-Host "Sicherung aktuell: $($Neueste.Name)"
10 }

Obwohl die USB-Platte seit zwei Tagen fehlt, meldet das Skript jeden Morgen „Sicherung aktuell“ – und zwar mit dem Namen einer zwei Wochen alten Sicherung. Außerdem liegt im Ordner, aus dem das Skript gestartet wird, plötzlich eine Datei namens 26.

Auszug aus der Befehlsreferenz:

Sort-Object -Property sorts objects by the values of the given property. -Descending indicates that the objects are sorted in descending order; the default is ascending order. Select-Object -First N selects the first N objects. Comparison operators: -eq equal, -ne not equal, -gt greater than, -ge greater than or equal, -lt less than, -le less than or equal. The characters > and >> are redirection operators: they send the output of a command to a file.

Korrigiere die zwei Fehler. Gib jeweils die Zeile, die korrigierte Anweisung und eine kurze Begründung an. (6 Punkte)

Je Fehler 1 Punkt für die Zeile, 1 Punkt für die Korrektur, 1 Punkt für die Begründung:

Korrekturen im Prüfskript
Zeile Korrektur Begründung
3 Sort-Object -Property LastWriteTime -Descending (oder in Zeile 4 Select-Object -Last 1) Sort-Object sortiert standardmäßig aufsteigend; -First 1 liefert dann die älteste Sicherung statt der neuesten.
6 if ($Alter.TotalHours -gt 26) { > ist in PowerShell kein Vergleich, sondern eine Umleitung: Der Wert wird in die Datei 26 geschrieben, der Ausdruck liefert nichts, die Bedingung ist nie erfüllt – es wird immer der else-Zweig ausgeführt.

Zusätzlicher Hinweis (ohne Punkte): Liegt gar keine Sicherung im Ordner, ist $Neueste leer. Ein robustes Skript prüft das vorher (if ($null -eq $Neueste) { … }) und warnt ebenfalls.

f) Die Werkstatt fotografiert jedes Fahrrad bei Annahme und Übergabe. Die Tablets laden die Fotos auf das NAS, dort liegen sie unter /volume1/werkstatt/fotos in vielen Unterordnern (je Jahr und Auftrag). Je nach Tablet enden die Dateien auf .jpg, .JPG oder .heic; eine App legt ihre Fotos als versteckte Dateien ab (Name beginnt mit einem Punkt). Jonas will die Fotos mit GNU find direkt auf dem NAS auflisten und nutzt dazu diese Kurzreferenz:

Kurzreferenz zu GNU find
Ausdruck Bedeutung
-type f nur reguläre Dateien, keine Ordner
-name "MUSTER" Dateiname passt zum Muster, Groß-/Kleinschreibung wird unterschieden
-iname "MUSTER" wie -name, aber ohne Unterscheidung von Groß-/Kleinschreibung
-mtime +N Inhalt zuletzt vor mehr als N ganzen Tagen geändert (angebrochene Tage zählen nicht)
-size +NM größer als N MiB
A B A und B (wird zuerst ausgewertet)
A -o B A oder B (wird nach dem „und“ ausgewertet)
\( … \) Klammer, der Backslash schützt sie vor der Shell
-exec mv -t ZIEL {} + verschiebt alle Treffer in den Ordner ZIEL

Sein erster Versuch:

find /volume1/werkstatt/fotos -type f -name "*.jpg" -o -name "*.heic"

In der Ausgabe fehlen alle Fotos von TAB-W3, dafür erscheint der Ordner /volume1/werkstatt/fotos/2026/import.heic.

fa) Erkläre die beiden Ursachen, gib einen korrigierten Befehl an und beurteile, ob Jonas für die versteckten Dateien noch etwas ergänzen muss. (5 Punkte)

  • Ordner in der Ausgabe: „und“ bindet stärker als -o. Der Ausdruck wird als (-type f und -name "*.jpg") oder -name "*.heic" ausgewertet, -type f gilt also nur für die JPG-Fotos (1 Punkt).

  • Fehlende Fotos: TAB-W3 speichert offenbar mit der Endung .JPG; -name unterscheidet Groß- und Kleinschreibung und findet sie nicht (1 Punkt).

  • Korrigierter Befehl (2 Punkte, je 1 für Klammer und -iname):

find /volume1/werkstatt/fotos -type f \( -iname "*.jpg" -o -iname "*.heic" \)
  • Versteckte Dateien: Nichts zu ergänzen – find liefert Dateien und Ordner, deren Name mit einem Punkt beginnt, ohne zusätzliche Option (anders als ls ohne -a oder Get-ChildItem ohne -Force) (1 Punkt).

fb) Ins Archiv /volume1/archiv/fotos verschoben werden sollen alle Fotos, die seit mindestens 731 Tagen nicht geändert wurden, und außerdem alle Fotos größer als 50 MiB, die seit mindestens 31 Tagen nicht geändert wurden. Erweitere den korrigierten Befehl aus fa). (6 Punkte)

find /volume1/werkstatt/fotos -type f \( -iname "*.jpg" -o -iname "*.heic" \) \
     \( -mtime +730 -o \( -size +50M -mtime +30 \) \) \
     -exec mv -t /volume1/archiv/fotos {} +

Punkteverteilung: Tage richtig umgerechnet (+730 und +30, siehe Hinweis) 1 Punkt, -mtime +730 1 Punkt, -size +50M -mtime +30 als Und-Verknüpfung 1 Punkt, Verknüpfung beider Regeln mit -o 1 Punkt, Klammer um die Oder-Verknüpfung, damit Typ und Endung für beide Regeln gelten, 1 Punkt, Verschieben mit -exec mv -t … {} + 1 Punkt.

Hinweise: „Mindestens 731 Tage“ heißt +730, weil -mtime nur ganze Tage zählt und „mehr als“ prüft. Zum gefahrlosen Testen lässt Du -exec … zunächst weg und siehst Dir nur die Trefferliste an (oder ersetzt mv durch echo mv). mv -t legt alle Dateien direkt in den Zielordner; soll die Ordnerstruktur erhalten bleiben oder kommen gleiche Dateinamen in verschiedenen Unterordnern vor, muss das Skript den Zielpfad je Datei bilden (z. B. mit rsync --remove-source-files und einer Dateiliste).

g) Am Black Friday war der Onlineshop eine Stunde lang nicht nutzbar, ohne dass es jemand bemerkte. Jonas testet deshalb vom Büro aus:

PS C:\> Test-NetConnection -ComputerName shop.csb-rad.example -Port 443      (1)

ComputerName     : shop.csb-rad.example
RemoteAddress    : 203.0.113.80                                              (2)
RemotePort       : 443
InterfaceAlias   : Ethernet
SourceAddress    : 192.168.10.23                                             (3)
TcpTestSucceeded : True                                                      (4)

Zur selben Zeit zeigt der Browser beim Aufruf des Shops die Fehlermeldung „503 Service Unavailable“.

ga) Beschreibe stichwortartig die Bedeutung der mit (1) bis (4) markierten Ein- und Ausgaben. (4 Punkte)

Je 1 Punkt:

  1. Es wird geprüft, ob zum Rechner shop.csb-rad.example eine TCP-Verbindung auf Port 443 (HTTPS) aufgebaut werden kann.

  2. Die IP-Adresse, in die der Name per DNS aufgelöst wurde (Adresse des Shop-Servers).

  3. Die eigene (lokale) IP-Adresse, von der aus die Verbindung aufgebaut wurde – Jonas’ Rechner im Büronetz.

  4. Der TCP-Verbindungsaufbau zu Port 443 war erfolgreich: Auf dem Server nimmt ein Dienst auf diesem Port Verbindungen an.

gb) Erläutere, warum dieser Test nicht ausreicht, um die Funktion des Shops zu überwachen, und beschreibe eine bessere Prüfung. (3 Punkte)

  • TcpTestSucceeded zeigt nur, dass der Port erreichbar ist (der Webserver bzw. Lastverteiler nimmt Verbindungen an). Ob die Anwendung dahinter funktioniert – Shop-Software, Datenbank, Zahlungsanbindung –, wird nicht geprüft. Hier antwortet der Webserver zwar, liefert aber den Fehler 503 (2 Punkte).

  • Besser: eine echte HTTP(S)-Anfrage an den Shop oder an eine eigene Statusseite stellen (z. B. Invoke-WebRequest, curl) und den Statuscode 200 sowie einen erwarteten Inhalt (z. B. einen Text der Startseite oder "status": "ok" einer Statusseite, die auch die Datenbank prüft) auswerten; gegebenenfalls die Antwortzeit messen (1 Punkt).

gc) Ein Überwachungsskript soll jede Minute den HTTP-Statuscode des Shops abfragen. Erst wenn drei Abfragen nacheinander fehlschlagen, soll einmal ein Alarm verschickt werden. Sobald der Shop wieder antwortet, beginnt die Zählung neu. Ergänze die Lücken (1) bis (5) im Pseudocode. (6 Punkte)

fehler: integer := 0
alarmGesendet: boolean := false
wiederhole
    status := httpStatus("https://shop.csb-rad.example/status")
    wenn status = 200 dann
        (1) ..................................
        (2) ..................................
    sonst
        (3) ..................................
        wenn (4) ................................ dann
            sendeAlarm("Shop seit " & fehler & " Minuten gestört")
            (5) ..................................
        Ende wenn
    Ende wenn
    warte(60 Sekunden)
solange true
Lösung der Lücken im Überwachungsskript
Lücke Inhalt Punkte
(1) fehler := 0 1
(2) alarmGesendet := false 1
(3) fehler := fehler + 1 1
(4) fehler >= 3 und nicht alarmGesendet 2
(5) alarmGesendet := true 1
  1. und (2) dürfen vertauscht sein. Statt >= 3 ist auch = 3 richtig – dann ist alarmGesendet streng genommen überflüssig, schadet aber nicht.

Probe mit der Folge 200, 503, 503, 503, 503, 503, 200, 503, 503, 200, keine Antwort, keine Antwort, keine Antwort (eine Abfrage je Minute): Alarm in Minute 4 und Minute 13, sonst keiner.

h) Die Skripte sollen sicher betrieben werden.

ha) Cem Sarıkaya bildet von jedem freigegebenen Skript einen SHA-256-Hashwert und legt ihn getrennt vom Skript ab. Erläutere, wozu das dient. (3 Punkte)

  • Ein Hashwert ist eine Prüfsumme fester Länge, die sich schon bei der kleinsten Änderung der Datei vollständig ändert (1 Punkt).

  • Bildet man vor dem Start (oder regelmäßig) den Hashwert erneut und vergleicht ihn mit dem hinterlegten Wert, erkennt man jede Veränderung des Skripts – absichtlich (Manipulation, z. B. eingeschleuster Schadcode in einem Skript, das mit hohen Rechten läuft) oder versehentlich (1 Punkt).

  • Der Wert muss getrennt abgelegt werden, weil jemand, der das Skript verändert, sonst auch den Hashwert anpassen könnte. Noch besser ist eine digitale Signatur, die zusätzlich zeigt, wer das Skript freigegeben hat (1 Punkt).

hb) Auf dem neuen Verwaltungsrechner startet das PowerShell-Skript nicht, weil die Ausführungsrichtlinie (execution policy) nur signierte Skripte zulässt. Jonas will die Richtlinie dauerhaft auf Unrestricted stellen. Nimm Stellung. (3 Punkte)

Stellungnahme mit Begründung, zum Beispiel:

  • Dagegen: Mit Unrestricted laufen alle Skripte, auch heruntergeladene oder per E-Mail eingeschleuste. Die Richtlinie ist kein vollständiger Schutz, verhindert aber, dass Benutzer*innen versehentlich fremde Skripte starten (1 Punkt).

  • Besser: die eigenen Skripte mit einem Codesignatur-Zertifikat signieren und die Richtlinie AllSigned beibehalten, oder mindestens RemoteSigned (lokale Skripte laufen, aus dem Internet geladene nur signiert) (1 Punkt).

  • Für geplante Aufgaben lässt sich das Skript gezielt freigeben, ohne die Richtlinie für den ganzen Rechner zu lockern; die Entscheidung gehört in die Sicherheitsrichtlinie von Cem Sarıkaya (1 Punkt).

hc) Nenne zwei Regeln für den Umgang mit Zugangsdaten in Skripten. (2 Punkte)

Je 1 Punkt, zum Beispiel:

  • Keine Kennwörter im Klartext im Quelltext (der Quelltext landet in Sicherungen, Versionsverwaltung und Tickets) – stattdessen Tresor für Zugangsdaten, sicher gespeicherte Anmeldeinformationen oder Abfrage beim Start.

  • Skripte unter einem eigenen Dienstkonto mit minimalen Rechten ausführen, nicht unter einem persönlichen Administratorkonto oder einem gemeinsam genutzten Konto wie admin.

  • Für SSH-Verbindungen (z. B. Kopie auf einen zweiten Standort) ein Schlüsselpaar mit einem modernen Verfahren wie Ed25519 statt eines Kennworts verwenden; den privaten Schlüssel schützen.

  • Startkennwörter für neue Konten nicht ins Skript schreiben, sondern zufällig erzeugen oder beim Start abfragen, und beim ersten Anmelden eine Änderung erzwingen.

  • Kennwörter nie in Protokolldateien oder Bildschirmausgaben schreiben.

Gesamtpunktzahl Aufgabe 2.1: 97 Punkte (a: 8, b: 24, c: 13, d: 14, e: 6, f: 11, g: 13, h: 8).


Hinweis zur Nachnutzung als Open Educational Resource (OER)

Dieser Artikel und seine Texte, Bilder, Grafiken, Code und sonstiger Inhalt sind - sofern nicht anders angegeben - lizenziert unter CC BY 4.0. Nennung gemäß TULLU-Regel bitte wie folgt: “Übungsaufgaben zu Skripten zur Systemautomatisierung” von oer-informatik.de (H. Stein), Lizenz: CC BY 4.0. Der Artikel wurde unter https://oer-informatik.de/skripte-uebungsaufgaben veröffentlicht, die Quelltexte sind in weiterverarbeitbarer Form verfügbar im Repository unter https://gitlab.com/oer-informatik/it-systeme/administration. Erstellt mit KI-Assistenz; von mir umfassend redaktionell überarbeitet, geprüft und freigegeben. Stand: 29.10.2026.

[Kommentare zum Artikel lesen, schreiben] / [Artikel teilen] / [gitlab-Issue zum Artikel schreiben]

Kommentare gerne per Mastodon, Verbesserungsvorschläge per gitlab issue (siehe oben). Beitrag teilen: