Projekt (Bash)Im Handbuch: Terminal: Dateien und Verzeichnisse
image: liesmich.webp kind: projekt language: Bash duration: 2 Doppelstunden requires: [working-with-files, git] reference: [terminal]

Ein Krimi im Terminal

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.

1. Der Fall

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

VS Code mit dem Ordner bitstein: links die Akten von alarmanlage bis tatort.txt, rechts der Brief von Kommissarin Weber über den Diebstahl der Goldenen Diskette.

Aufgabe 1Die Akten

Lade das Archiv mit den Akten herunter und entpacke es in deinem Home-Verzeichnis.

Lös den Fall im Terminal. Du könntest jede Datei in VS Code öffnen, aber 1500 Befragungen liest niemand durch, und genau darum geht es: Die Werkzeuge finden in Sekunden, wofür du Tage bräuchtest. Raten gilt nicht. Jeder Schritt soll sich aus den Akten ergeben, und am Ende kannst du sagen, warum es nur eine Person gewesen sein kann.

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.

2. Der Tatort

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

Vier Zeilen aus tatort.txt, in denen Kommissar Okafor über den Kaffee schreibt, vom Automaten im Foyer bis zum Kaffee über dem Lageplan.

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.

Aufgabe 2Die Hinweise

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.

Aufgabe 3Groß oder klein

In wie vielen Zeilen von tatort.txt steht hinweis, egal ob groß oder klein geschrieben?

Tipp

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.

3. Die Zeugin

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

Zuerst die ersten drei Zeilen der Hafenstraße, jede mit einem Namen und dem, was die Person gesagt hat. Darunter nur die dritte Zeile, das Haus mit der Nummer 3.

Die zweite Ausgabe ist genau eine Zeile: Hausnummer 3. In welcher Straße und in welchem Haus die Zeugin wohnt, steht in den Hinweisen.

Aufgabe 4Die Zeugin

Schreib einen Befehl, der aus strassen/moewenweg.txt genau die Zeile der Zeugin ausgibt, und nur sie.

Tipp

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.

4. Der Nachtwächter

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

Fünf Dateinamen von befragungen/befragung-1221.txt bis befragung-1435.txt, darunter 145: So viele Befragungen erwähnen Hamburg.

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.

Aufgabe 5Der Nachtwächter

Welche Nummer hat die Befragung des Nachtwächters Ole Brandt?

Tipp

grep -l nennt nur die Dateien, in denen das Muster vorkommt.

5. Das Auto

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

Die ersten drei Autos aus fahrzeuge.txt, darunter fünf blaue Autos mit Kennzeichen BS-M und ihren Besitzern.

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.

Aufgabe 6Das gelbe Auto

Schreib einen Befehl, der aus fahrzeuge.txt genau die Autos ausgibt, die zur Aussage der Zeugin passen.

Tipp

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.

6. Die Vereine

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

Drei Namen: die Leute, die im Chor und im Kegelclub sind.

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.

Aufgabe 7In beiden Vereinen

Schreib einen Befehl, der alle ausgibt, die im Schachclub und im Fitnessstudio Muskelkater sind, jeden Namen einmal.

Tipp

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?

Aufgabe 8Zwei Verdächtige

Wer hat eins der vier Autos und ist im Schachclub und im Fitnessstudio?

Zwei Verdächtige

7. Die Alarmanlage

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

Zwei Commits von Ingrid Hollerbach. Darunter der Commit »Die Diskette ist weg« vom 15. März, 8:31 Uhr, der die Datei NOTIZ.txt mit einer Zeile anlegt.

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.

Aufgabe 9Die gelöschte Karte

Mit welcher Karte wurde in der Nacht der Hintereingang geöffnet?

Tipp

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.

8. Wer war es?

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.

Aufgabe 10Wer war es?

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.

9. Beweise vom Server

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.

Anmelden mit ssh

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.

Im Terminal: ssh mit dem eigenen Zugang zu museum.bitstein, die Begrüßung des Servers der Verwaltung, die Eingabeaufforderung mit @museum, darunter ls mit LIESMICH.txt, geraete.txt, logs und netzplan.txt und der Inhalt von LIESMICH.txt.

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.

Aufgabe 11Wer kam in der Nacht?

Von welcher Adresse hat sich die Wartung in der Nacht der Tat mit dem VPN des Museums verbunden?

Tipp

grep wartung logs/vpn.log

Aufgabe 12Wessen Gerät?

Wem gehört das Gerät mit dieser Adresse?

Tipp

Die Adressen der Geräte stehen in geraete.txt.

Von Rechner zu Rechner

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

Im Terminal: ssh -J über museum.bitstein zu alarmanlage.bitstein, die Begrüßung der Alarmanlage, hostname gibt alarmanlage aus, und ls -a /home/wartung zeigt die versteckte Datei .bash_history.

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.

Aufgabe 13Der Befehl aus der Nacht

Mit welchem Befehl wurden die beiden Zeilen aus zugaenge.log gelöscht? Schreib ihn so ab, wie er in der .bash_history steht.

Beweise sichern

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.

Aufgabe 14Beweise sichern

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.

10. Ein eigener Fall

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.

  • Leg einen Ordner mein-fall an, mit einer LIESMICH.txt, die sagt, was passiert ist und wo man anfängt.
  • Jede Spur führt zur nächsten: ein Hinweis zu einer Datei, eine Zeile zu einer anderen Datei, eine Liste zu einem Namen.
  • Für jeden Schritt braucht man ein anderes Werkzeug, zum Beispiel grep -l, head | tail und sort | uniq -d.
  • Falsche Fährten machen es spannend: ein zweites gelbes Auto, eine Person mit demselben Nachnamen.

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.

Aufgabe 15Ein eigener Fall

Bau einen kleinen Fall für jemanden aus deiner Klasse, in einem Ordner mein-fall mit einer LIESMICH.txt, und gib ihn ab.

11. Spickzettel

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

12. Wie geht es weiter?

  • 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.
  • Die Idee zu dieser Seite stammt aus The Command Line Murders von Noah Veltman, einem Krimi auf Englisch, den man auch nur im Terminal lösen kann. Er ist größer und schwerer als der Fall in Bitstein und braucht noch ein paar Werkzeuge mehr.