Author: Tim Weber Title: Bash für CTFler Date: 2008 Nov 12 __ __ __ __ __ _ __ __ __ __/ // /_ / / _/_// /_ (_)____ _/_// /_ ____ _ _____ / /_ /_ _ __// / _/_/ / __ \ / // __ \ _/_/ / __ \ / __ `// ___// __ \ /_ _ __//_/_/_/ / /_/ // // / / /_/_/ / /_/ // /_/ /(__ )/ / / / /_//_/ (_)/_/ /_.___//_//_/ /_//_/ /_.___/ \__,_//____//_/ /_/ Die Unix-Philosophie ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Schreibe Computerprogramme so, dass sie nur eine Aufgabe erledigen und diese gut machen. Schreibe Programme so, dass sie zusammen arbeiten. Schreibe Programme so, dass sie Textdateien verarbeiten, denn dies ist eine universelle Schnittstelle. Was ist die bash? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ "Bourne-again shell", Anspielung darauf, dass sie eine Weiterentwicklung der Bourne-Shell ist. Entwickelt Ende der 80er. Viele Alternativen (zsh, csh, ksh, ash, ...). Gemeinsamer Standard (POSIX), Shellscripte _sollten_ portierbar sein. (Sind sie aber selten.) Eigentlich nur Benutzerinterface sowie "Mittler" zwischen einzelnen Befehlen, die manchmal zwar direkt in die Shell eingebaut sind (z.B. echo), aber meist zu anderen Paketen (z.B. coreutils). Befehlssyntax ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .--------------------------- | $ variable=wert # Wichtig: ohne Leerzeichen | $ echo $variable | wert | $ echo "$variable mit double quotes" '$variable mit single quotes' | wert mit double quotes $variable mit single quotes | $ echo 'Jetzt kommt ein Verzeichnislisting:'; ls | Jetzt kommt ein Verzeichnislisting: | a.txt b.txt `--------------------------- Ein- und Ausgabe ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Unter Unix ist bekanntlich alles eine Datei. Will ein Programm mit einer Datei arbeiten, bekommt es vom Betriebssystem einen Handle auf diese Datei zurück (beliebige Zahl, also eine "Zugriffs-ID"). Vordefinierte Handles: 0 (Standardeingabe), 1 (Standardausgabe), 2 (Standardfehlerausgabe). Diese werden vom Betriebssystem oder der Shell bereits mit den korrekten "Dateien" verbunden, z.B. der Tastatur, dem Bildschirm, einer echten Datei oder einer Pipe ("Röhre"), die zu einem anderen Programm führt. Der Befehl cat (concatenate) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .--------------------------- | $ cat a.txt | Das hier ist Text. | $ cat b.txt | Hier steht auch Text drin. | $ cat b.txt a.txt | Hier steht auch Text drin. | Das hier ist Text. `--------------------------- Der Befehl echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Gibt den angegebenen Text aus. Der Unterschied zu cat liegt darin, dass die Parameter nicht Dateinamen, sondern direkt den Text angeben. .--------------------------- | $ echo Hallo. | Hallo. | $ cat Hallo. | cat: Hallo.: No such file or directory. `--------------------------- Der Befehl head ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Zeigt nur die ersten X Zeilen einer Datei an (Standard: 10). Alternative auch: die ersten X Byte, oder "alle bis auf die letzten X Zeilen/Byte". .--------------------------- | $ head -n 2 erlkoenig.txt | Wer reitet so spät durch Nacht und Wind? | Es ist der Vater mit seinem Kind. | $ head -c 7 erlkoenig.txt | Wer rei | $ head -n -3 allemeineentchen.txt | Alle meine Entchen | Schwimmen auf dem See `--------------------------- Der Befehl tail ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Zeigt nur die letzten X Zeilen oder Byte an, bzw. "alle bis auf die ersten X Zeilen/Byte". .--------------------------- | $ tail -n 1 erlkoenig.txt | In seinem Arm das Kind war tot. | $ tail -c 5 erlkoenig.txt | tot. | $ tail -n -4 allemeineentchen.txt | Schwänzchen in die Höh'. `--------------------------- Der Befehl wc (word count) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .--------------------------- | $ cat a.txt | Das hier ist Text. | $ wc a.txt | 1 4 19 a.txt | $ wc -l a.txt | 1 a.txt `--------------------------- Umleiten in eine Datei ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > Leitet die Ausgabe in eine Datei, die neu angelegt wird. Falls sie bereits existiert, wird sie überschrieben. Hier gilt der Grundsatz: "Unix wurde nicht entwickelt, um seine Benutzer daran zu hindern, dumme Dinge zu tun, denn das würde diese auch davon abhalten, schlaue Dinge zu tun." (Doug Gwyn) .--------------------------- | $ cat a.txt > b.txt | $ cat b.txt | Das hier ist Text. `--------------------------- Anhängen an eine Datei ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ >> Leitet die Ausgabe in eine Datei und hängt dabei an den bestehenden Inhalt an. Falls die Datei noch nicht existiert, wird sie angelegt. .--------------------------- | $ cat a.txt >> b.txt | $ cat b.txt | Das hier ist Text. | Das hier ist Text. `--------------------------- A Series of Tubes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | Verbindet die Standardausgabe des linken Befehls mit der Standardeingabe des rechten Befehls. Auf Deutsch: Leitet die Ausgabe des ersten Befehls an den zweiten. .--------------------------- | $ echo Hier kommt Kurt. > kurt.txt | $ cat kurt.txt b.txt | wc -l | 3 `--------------------------- Merke: Wenn gar keine Dateien als Parameter angegeben werden, lesen die meisten Befehle von der Standardeingabe. Standardeingabe von der Tastatur ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Falls die Standardeingabe für einen Befehl nicht aus einer Pipe kommt, liest er eben von der Tastatur (die ja die "Default-Standardeingabe" ist), und zwar so lange, bis man über die Tastenkombination Strg+D (abgekürzt ^D) sagt, dass der Eingabestrom beendet ist. (Wenn dein Script irgendwie "hängt", ist ein vergessener Eingabedateiname oft der Grund.) .--------------------------- | $ wc -l | Ich muss durch den Monsun | Hinter die Welt | Ans Ende der Zeit | ^D | 3 `--------------------------- Standardeingabe ändern ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Eingabe aus einer Datei (<), direkt bis zum Nennen eines Stoppwortes (<<, praktisch in Scripts) oder wie bei einer echo-Pipe (<<<). .--------------------------- | $ wc -w < kurt.txt | 3 | $ wc -l < "${x}.txt"; cat "${x}.txt"; done | a | b | c | $ for x in ?.txt; do mv "$x" "${x}.aha"; done; ls | a.txt.aha b.txt.aha c.txt.aha `--------------------------- Zählende for-Schleifen ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .--------------------------- | $ # Maximale Kompatibilität, minimale Geschwindigkeit: | $ # "seq" aus den coreutils | $ seq 1 10 | 1 2 3 4 5 6 7 8 9 10 | $ for i in $(seq 1 10); do echo -n .; done; echo | .......... | $ # Ab bash 3: | $ for i in {1..10}; do echo -n .; done; echo | .......... | $ # Womöglich auch bash-only, kA... | $ for (( i=1; i<10; i++ )); do echo -n .; done; echo | .......... `--------------------------- Verzweigung mit if ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ if BEFEHL; then BEFEHLE elif BEFEHL; then BEFEHLE else BEFEHLE fi .--------------------------- | $ if mkdir verzeichnis; then echo Erstellt; else echo PANIK; fi | Erstellt. `--------------------------- Verwendung von Ausgaben mit $() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .--------------------------- | $ date +%F | 2008-11-12 | $ echo Heute ist Workshop. > "$(date +%F).txt" | $ ls | 2008-11-12.txt | $ cat 2008-11-12.txt | Heute ist Workshop. `--------------------------- `Backticks` sind äquivalent, aber nicht schachtelbar und schwerer zu lesen, daher wird $() dringend empfohlen. Rechnen und so mit expr ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .--------------------------- | $ expr 9 - 12 | -3 | $ expr length foobar | 6 | $ expr index foobar b | 4 `--------------------------- Die einzelnen Operanden einer Rechenanweisung müssen als einzelne Parameter übergeben werden (also mit Leerzeichen abgetrennt). Von der Shell ausgewertete Zeichen wie * (Multiplikation) müssen natürlich mit \ oder Singlequotes escaped werden. expr arbeitet nur mit ganzen Zahlen. Für komplexere Berechnungen hilft bc, aber auch bash kann in begrenztem Maße rechnen: $(()). HTTP-Zugriffe mit wget und curl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ wget ist auf den meisten Systemen verfügbar, während curl praktische Funktionen zum Senden von Daten und Simulieren von Formularaktivitäten besitzt, daher sollte man zumindest mit beiden Tools ansatzweise umgehen können. .--------------------------- | $ # Äquivalente Nutzung von wget und curl (Ausgabe auf stdout): | $ wget -q -O - http://scytale.name/files/scytale.opml | head -n 1 | | $ curl -s https://scytale.name/files/2007/11/eurosignal.py | wc | 154 563 4717 `--------------------------- Merke: "-" ist bei vielen Tools eine Abkürzung für die Standardausgabe. Alternativ kann man meist auch "/dev/stdout" verwenden. Zeichen ersetzen mit tr (translate) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .--------------------------- | $ echo foo | tr o e | fee | $ echo 'Drei Chinesen mit nem Kontrabass' | tr aeiou i | Drii Chinisin mit nim Kintribiss | $ # Kombination von "Löschen" und "Komplement": | $ echo 0621 / 181-2342 | tr -cd 0-9 | 06211812342 `--------------------------- Filtern mit grep: Parameter ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -i case-insensitive (Groß-/Kleinschreibung ignorieren) -E erweiterte reguläre Ausdrücke (auch "egrep") -v nur Zeilen, die _nicht_ matchen -q keine Ausgabe, nur Returncode ob Match oder nicht -o nur matchenden Teil der Zeile ausgeben -c nur Anzahl matchender Zeilen ausgeben -l nur Namen der matchenden Dateien ausgeben -m nach X Matches Suche in der Datei beenden -r rekursives Matchen in Unterverzeichnissen -H Dateiname mit ausgeben (-h zum Deaktivieren) -n Zeilennummer mit ausgeben Filtern mit grep: Beispiele ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ grep arbeitet zeilenweise, Matches über mehrere Zeilen hinweg sind also nicht möglich (aber man kann z.B. mit "tr -d '\n'" schummeln). .--------------------------- | $ # Flags aus einer Datei holen. | $ grep -oE '[0-9a-f]{32}' foo.txt | f16928d17c54213e06e68224b5bba68a | 96a92e6d00292f85b09ca5ba0d7d9806 | $ # URLs aus den Chatlogs fischen. | $ grep -Ero '[a-z0-9+-]+://[^ ]+' ~/.irssi/logs | [...] `--------------------------- Manipulationen mit sed (stream editor) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ s[uche]/Ausdruck/Ersetzung/Flags .--------------------------- | $ echo foo dee doo | sed -e 's/oo/u/g' -e 's/ee/i/g' | fu di du | $ echo Bar Foo | sed -r -e 's/^(.+) (.+)$/\2 \1/' | Foo Bar | $ echo -e 'ab5\nxyz\no2u' | sed -rn -e 's/.*([0-9]+).*/\1/p' | 5 | 2 `--------------------------- Flags: g (global, mehr als ein Match pro Zeile), p (print, Ergebnis ausgeben (in Kombination mit -n)). Parameter: -r (erweiterte Ausdrücke), -n (nur Matches mit p-Flag ausgeben, nicht matchende Zeilen nicht). Interessantes, das weg gelassen werden musste ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ find, tee, case, tac, der Unterschied zwischen [ und [[, Befehle gruppieren mit () und {}, Funktionen, Aliase Ach ja... ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Man machte mich nach dem Vortrag darauf aufmerksam, dass es praktisch wäre, die Leute darauf hinzuweisen, dass man Bash-Befehle auch in einer Datei niederschreiben kann, um sie dann alle auf einmal auszuführen; sowas nennt sich dann ein "Script". Ich hielt das für trivial genug, um es nicht zu erwähnen, aber ja, das kann man. Präferierte Dateinamenserweiterung ist .sh, man kann das Script mit "chmod u+x dateiname.sh" ausführbar machen, wenn als erste Zeile folgendes in der Datei steht: #!/bin/bash