In der Nacht zum 15. März verschwindet aus dem Stadtmuseum Bitstein die Goldene Diskette. Die Polizei hat Akten, aber viel zu viele: einen Bericht mit über tausend Zeilen, 1500 Befragungen, alle Autos und alle Leute der Stadt. Mit grep, head, tail, sort, uniq und Git siebst du heraus, was zählt, und findest, wer es war.
Bitstein ist eine kleine Stadt an der Küste, mit Hafen, Leuchtturm und einem Stadtmuseum. Dessen ganzer Stolz ist die Goldene Diskette: eine Diskette aus Gold, auf der das erste Programm steht, das je in Bitstein geschrieben wurde, 1987 in der Computer-AG der Hafenschule. In der Nacht vom 14. auf den 15. März ist die Vitrine leer. Kein Fenster ist eingeschlagen, und die Alarmanlage hat geschwiegen.
Kommissarin Weber hat alles gesammelt, was es gibt, und schickt es dir. Hol dir die Akten in dein Home-Verzeichnis und pack sie aus:
cd wget https://workspace.hackschule.de/terminal-mystery/bitstein.tar.gz tar xzf bitstein.tar.gz cd bitstein
Mit ls siehst du, was im Ordner liegt. Öffne ihn auch in VS Code mit StrgK StrgO, dann hast du links immer im Blick, was es alles gibt. Fang mit dem Brief der Kommissarin an:
cat LIESMICH.txt

Lade das Archiv mit den Akten herunter und entpacke es in deinem Home-Verzeichnis.
Schreib dir unterwegs auf, was du herausfindest, zum Beispiel in eine Datei notizen.txt. Wie man die Ausgabe eines Befehls direkt dort hineinschreibt, kommt gleich.
Der Bericht vom Tatort ist lang. Wie lang, sagt wc -l, das die Zeilen zählt:
wc -l tatort.txt
Über 1200 Zeilen, in denen vier Leute von der Polizei aufgeschrieben haben, was sie den ganzen Tag gemacht haben. Zum Durchlesen ist das zu viel. grep gibt nur die Zeilen aus, in denen ein Muster vorkommt. Wie oft hat die Polizei Kaffee getrunken?
grep Kaffee tatort.txt

Die wichtigen Stellen hat Kommissarin Weber mit HINWEIS markiert, am Anfang der Zeile. Probier grep HINWEIS tatort.txt und sieh dir jede Zeile genau an. Steht in allen ein Hinweis?
Ein Muster für grep darf mit ^ anfangen. Das heißt »am Anfang der Zeile«: grep '^Kaffee' findet nur Zeilen, die mit Kaffee beginnen. Die Anführungszeichen schützen das Muster vor der Shell; bei Mustern mit Sonderzeichen oder Leerzeichen gehören sie immer dazu.
Schreib einen Befehl, der aus tatort.txt genau die Zeilen ausgibt, die
mit HINWEIS anfangen. Der Befehl läuft in deinem Home-Verzeichnis:
Schreib also bitstein/tatort.txt oder fang mit cd bitstein && an.
Mit > landet die Ausgabe eines Befehls in einer Datei statt im Terminal. So hast du die Hinweise immer zur Hand:
grep '^HINWEIS' tatort.txt > notizen.txt
> überschreibt die Datei, >> hängt an ihr Ende an. Was du später findest, kommt also mit >> dazu.
grep unterscheidet Groß- und Kleinschreibung: HINWEIS ist nicht Hinweis. Mit -i (»ignore case«) ist das egal, und mit -c (»count«) gibt grep statt der Zeilen nur ihre Zahl aus.
In wie vielen Zeilen von tatort.txt steht hinweis, egal ob groß oder klein geschrieben?
grep -c zählt Zeilen, grep -i macht Groß und Klein gleich.
Lies die fünf Hinweise genau. Jeder führt in einen anderen Teil der Akten.
In strassen/ liegt eine Datei für jede Straße. Jede Zeile ist ein Haus, in der Reihenfolge der Hausnummern: Zeile 1 ist Hausnummer 1, Zeile 2 Hausnummer 2. Die ersten drei Häuser der Hafenstraße:
head -n 3 strassen/hafenstrasse.txt
head -n 3 gibt die ersten drei Zeilen aus, tail -n 1 die letzte. Mit | (gesprochen »Pipe«) wird die Ausgabe des einen Befehls zur Eingabe des nächsten. tail bekommt dann nicht die ganze Datei, sondern nur, was head übrig lässt:
head -n 3 strassen/hafenstrasse.txt | tail -n 1

Die zweite Ausgabe ist genau eine Zeile: Hausnummer 3. In welcher Straße und in welchem Haus die Zeugin wohnt, steht in den Hinweisen.
Schreib einen Befehl, der aus strassen/moewenweg.txt genau die Zeile
der Zeugin ausgibt, und nur sie.
head -n 23 gibt die ersten 23 Zeilen aus. Was ist davon die letzte?
Die Zeile nennt die Datei mit ihrer Befragung. Lies sie mit cat und schreib dir auf, was die Zeugin gesehen hat.
Welche der 1500 Befragungen die des Nachtwächters ist, hat niemand notiert. grep kann in vielen Dateien auf einmal suchen. Wer hatte Besuch aus Hamburg?
grep Hamburg befragungen/*
Das * setzt die Shell ein, bevor grep überhaupt startet: befragungen/* wird zu den Namen aller 1500 Dateien im Ordner. Vor jede Fundstelle schreibt grep dann, aus welcher Datei sie stammt. Mit -l (»list«) gibt es nur noch die Dateinamen aus, jeden einmal. Und wc -l zählt sie:
grep -l Hamburg befragungen/* | head -n 5 grep -l Hamburg befragungen/* | wc -l

Such so nach dem Nachtwächter. Ein Name mit Leerzeichen gehört in Anführungszeichen, sonst sucht grep nach dem Vornamen in einer Datei, die so heißt wie der Nachname. Nicht jede Datei, in der er vorkommt, ist seine eigene Befragung: Wer befragt wurde, steht in der zweiten Zeile.
Welche Nummer hat die Befragung des Nachtwächters Ole Brandt?
grep -l nennt nur die Dateien, in denen das Muster vorkommt.
In fahrzeuge.txt steht jedes Auto der Stadt in einer Zeile: Kennzeichen, Farbe, Art und wem es gehört.
head -n 3 fahrzeuge.txt
Mehrere grep hintereinander sieben immer feiner. Alle blauen Autos, deren Kennzeichen mit BS-M anfängt:
grep '^BS-M' fahrzeuge.txt | grep blau

Was hat die Zeugin über das Auto gesagt? Bau daraus eine Kette von grep, bis nur noch die Autos übrig sind, die passen. Hängst du | wc -l an, siehst du, wie viele es nach jedem Schritt noch sind.
Schreib einen Befehl, der aus fahrzeuge.txt genau die Autos ausgibt,
die zur Aussage der Zeugin passen.
Mehrere grep hintereinander, mit | verbunden: Jedes siebt weiter aus, was das vorige übrig lässt.
Vier Autos, vier Leute. Eine davon könnte es gewesen sein.
In vereine/ liegen die Mitgliederlisten von vier Vereinen, ein Name pro Zeile, in der Reihenfolge, in der die Leute eingetreten sind.
sort sortiert Zeilen. Gibst du ihm zwei Dateien, sortiert es beide zusammen. Wer in beiden Vereinen ist, steht danach zweimal, direkt untereinander. uniq -d gibt genau solche Zeilen aus, die doppelt hintereinander stehen, jede einmal. Wer singt im Chor und kegelt?
sort vereine/chor.txt vereine/kegelclub.txt | uniq -d

uniq vergleicht immer nur eine Zeile mit der davor. Ohne sort stehen die beiden Einträge derselben Person irgendwo in den Listen und nie nebeneinander, und uniq -d findet nichts.
Welche Vereine für dich zählen, sagen ein Hinweis und die Befragung des Nachtwächters.
Schreib einen Befehl, der alle ausgibt, die im Schachclub und im Fitnessstudio Muskelkater sind, jeden Namen einmal.
Schreib beide Listen sortiert untereinander. Wer in beiden Vereinen ist, steht dann zweimal direkt hintereinander.
Leg die Namen neben die vier Autobesitzer. Wer steht auf beiden Listen?
Wer hat eins der vier Autos und ist im Schachclub und im Fitnessstudio?
Zwei Verdächtige, und beide passen zu allem, was du bisher weißt. Was jetzt noch fehlt, weiß die Alarmanlage. Sie schreibt jeden Zugang mit einer Schlüsselkarte in eine Datei und macht dafür sofort einen Commit, in einem Git-Repository:
cd alarmanlage ls cat LIESMICH.txt
git log zeigt alle Commits, den neuesten zuerst. Es sind über 90, deshalb kommen sie in less: Die Leertaste blättert weiter, Q beendet. Mit --oneline passt jeder Commit in eine Zeile, mit --author=… bleiben nur die Commits einer Person. Was hat die Museumsleiterin committet?
git log --oneline --author=Hollerbach
Vorn in jeder Zeile steht der Anfang des Hashs, der Kennung des Commits. git show mit diesem Anfang zeigt den ganzen Commit: wer, wann, die Nachricht und darunter, was sich geändert hat. Zeilen mit + sind dazugekommen, Zeilen mit - sind weggefallen.
git show a3ac34a

Eine Commit-Nachricht schreibt, wer committet, und sie kann behaupten, was sie will. Was der Commit wirklich geändert hat, zeigt erst git show. Schau dir an, was in der Nacht passiert ist. In zugaenge.log steht heute nichts Verdächtiges mehr, aber Git vergisst nichts, was einmal committet war.
Mit welcher Karte wurde in der Nacht der Hintereingang geöffnet?
git log --author=Wartung zeigt, was die Wartung committet hat. git show und die ersten Zeichen des Hashs zeigen, was sich dabei geändert hat.
Du hast eine Karte. Wem sie gehört, steht in karten.txt, und wer in Bitstein was arbeitet, in personen.txt. Leg alles zusammen: das Auto, die Vereine, die Karte. Es bleibt eine einzige Person.
Wer hat die Goldene Diskette gestohlen? Vor- und Nachname.
Am Nachmittag des 15. März klingelt Kommissarin Weber an einer Tür in Bitstein. Die Goldene Diskette liegt noch in einer Sporttasche, unbeschädigt, in Zeitungspapier gewickelt. Die Zugangsdaten der Wartung hatte jemand seit dem Einbau der Anlage, und die alte Karte hat nie jemand zurückverlangt. Das Museum ändert noch am selben Abend alle Passwörter und sperrt jede Karte, die es nicht mehr braucht.
Konstantin Reuter streitet alles ab. Er sei die ganze Nacht zu Hause gewesen, und an die Alarmanlage komme man ohnehin nur im Museum heran. Ganz stimmt das nicht: Das Museum hat einen Server, über den man sich von außen verbinden kann, und es hat der Polizei einen Zugang gegeben. Für dich gibt es einen eigenen. Dein Workspace kennt ihn:
echo $CASE_USER $CASE_PASSWORD
Gibt echo nichts aus, lief dein Workspace schon, bevor der Server eingeschaltet wurde. Starte ihn neu, dann ist der Zugang da.
ssh (»Secure Shell«) öffnet ein Terminal auf einem anderen Rechner. Alles, was du tippst und was zurückkommt, geht verschlüsselt durchs Netz.
ssh $CASE_USER@museum.bitstein
Vor dem @ steht, wer du dort bist, dahinter der Rechner. Beim ersten Mal fragt ssh, ob du dem Rechner traust, und zeigt seinen Fingerabdruck, eine Prüfsumme seines Schlüssels. Mit yes merkt sich ssh ihn in ~/.ssh/known_hosts. Gibt sich später ein anderer Rechner als museum.bitstein aus, passt der Fingerabdruck nicht mehr, und ssh warnt laut. Dann fragt es nach dem Passwort. Beim Tippen erscheint nichts, auch keine Sternchen. Einfügen geht mit StrgUmschaltV.

Die Eingabeaufforderung zeigt jetzt @museum: Jeder Befehl läuft dort, nicht in deinem Workspace. hostname sagt, auf welchem Rechner du bist, whoami, wer du dort bist. Schreiben kannst du hier nichts, lesen alles, mit denselben Werkzeugen wie in Bitstein. exit oder StrgD bringt dich zurück.
Von welcher Adresse hat sich die Wartung in der Nacht der Tat mit dem VPN des Museums verbunden?
grep wartung logs/vpn.log
Wem gehört das Gerät mit dieser Adresse?
Die Adressen der Geräte stehen in geraete.txt.
Laut netzplan.txt hängt die Alarmanlage nur im Netz des Museums. Aus deinem Workspace findest du sie gar nicht, nur von museum.bitstein aus. Melde dich dort an und von dort aus weiter:
ssh alarmanlage.bitstein
Ohne Namen vor dem @ nimmt ssh den, unter dem du gerade angemeldet bist, und der ist auf beiden Rechnern derselbe, mit demselben Passwort. Jetzt bist du zwei Rechner weit weg: dein Workspace, darin eine Verbindung zum Museum, darin eine zur Alarmanlage. Jedes exit geht einen Schritt zurück.
Für diesen Weg gibt es eine Abkürzung. -J (»jump«) springt über den ersten Rechner zum zweiten, in einem Befehl, und das Passwort fragt ssh dann für beide:
ssh -J $CASE_USER@museum.bitstein $CASE_USER@alarmanlage.bitstein

Die Steuerung kennst du schon: /srv/alarmanlage ist das Repository aus deinen Akten. Neu ist /home/wartung. Die Bash schreibt in die versteckte Datei .bash_history, was jemand getippt hat, Befehl für Befehl. Über jedem Befehl steht, wann er kam, als Zeile mit # und den Sekunden seit dem 1. Januar 1970. date -d @1773526140 macht daraus eine Uhrzeit.
Mit welchem Befehl wurden die beiden Zeilen aus zugaenge.log gelöscht? Schreib ihn so ab, wie er in der .bash_history steht.
scp (»secure copy«) kopiert Dateien über ssh, auch über einen Sprung. Vor dem : steht der Rechner, dahinter der Pfad dort, und das Letzte ist das Ziel bei dir:
scp -J $CASE_USER@museum.bitstein $CASE_USER@alarmanlage.bitstein:/home/wartung/.bash_history ~/bitstein/beweis.txt
Das kommt in deinem Workspace an, nicht auf einem der Server. Führ den Befehl darum in deinem Workspace aus, nicht in einer Verbindung zum Museum.
Kopier die .bash_history der Wartung mit scp nach bitstein/beweis.txt in deinem Workspace.
Die Adresse aus dem VPN, das Gerät dazu und die Befehle aus der Nacht, minutengenau neben den Commits: Jetzt ist der Fall wasserdicht.
Jetzt bist du dran: Bau einen Fall für jemanden aus deiner Klasse. Er muss nicht groß sein, aber er soll sich nur mit dem Terminal lösen lassen.
mein-fall an, mit einer LIESMICH.txt, die sagt, was passiert ist und wo man anfängt.grep -l, head | tail und sort | uniq -d.Viele Dateien schreibt eine Schleife. Diese hier legt 500 Befragungen an, in denen niemand etwas gesehen hat:
for i in $(seq 1000 1499); do echo "Befragung $i: Ich habe nichts gesehen." > befragung-$i.txt; done
Danach ersetzt du eine davon durch die Befragung, auf die es ankommt. Tauscht eure Fälle aus und löst den der anderen.
Bau einen kleinen Fall für jemanden aus deiner Klasse, in einem Ordner
mein-fall mit einer LIESMICH.txt, und gib ihn ab.
| Befehl | Was er tut |
|---|---|
wc -l datei |
zählt die Zeilen |
grep muster datei |
die Zeilen, in denen das Muster vorkommt |
grep '^muster' datei |
nur Zeilen, die mit dem Muster anfangen |
grep -i, grep -c
|
Groß und Klein egal; nur die Zahl der Zeilen |
grep -l muster ordner/* |
die Dateien, in denen das Muster vorkommt |
head -n 23 datei | tail -n 1 |
genau Zeile 23 |
sort a b | uniq -d |
die Zeilen, die in beiden Dateien stehen |
befehl > datei, >> datei
|
Ausgabe in eine Datei schreiben; an sie anhängen |
git log --oneline --author=name |
die Commits einer Person, jeder in einer Zeile |
git show hash |
ein Commit mit allem, was er geändert hat |
ssh name@rechner, exit
|
ein Terminal auf einem anderen Rechner; zurück |
ssh -J name@a name@b |
über den Rechner a zum Rechner b |
scp name@rechner:pfad ziel |
eine Datei von dort zu dir kopieren |
git log -S 4471 --oneline findet jeden Commit, in dem 4471 dazukommt oder verschwindet, auch wenn seine Nachricht nichts davon sagt. So findest du in einem großen Repository, wann eine Zeile dazukam und wann sie wieder ging.grep -E versteht reguläre Ausdrücke: grep -E '^BS-K[A-Z]* [0-9]*7 +gelb' erledigt die ganze Kette mit dem Auto in einem Schritt. Wie solche Muster funktionieren, zeigt Endliche Automaten.