Mit nicht einmal 100 Zeilen Programmcode verschaffen Sie sich auf der Kommandozeile einen umfassenden Überblick aller Bewegungen auf Ihrem Bankkonto.
Kreditinstitute bieten beim Online-Banking die Möglichkeit an, Kontenbewegungen ins CSV-Format zu exportieren und auf der Festplatte abzuspeichern. Beim Export der Daten können Sie einen Zeitraum von zum Beispiel einem Jahr festlegen. Dabei stehen oft verschiedene CSV-Formate zur Wahl, von denen Sie am besten das CSV-Camt-Format wählen, einen bankenübergreifenden Standard.
Verfügen Sie über mehrere Konten bei verschiedenen Kreditinstituten, können Sie so die Software für alle nutzen. Im Bedarfsfall führen Sie die CSV-Dateien verschiedener Banken via Cat zusammen, um so einen Überblick über all Ihre Konten zu erhalten. Spätestens dann fällt auf, dass es sich um eine Unmenge von Daten handelt, die sehr unübersichtlich wirkt.
Insbesondere lässt sich nur schwer erkennen, welche Datenfelder die CSV-Datei – in unserem Beispiel soll sie umsaetze.csv heißen – eigentlich enthält. Der Befehl aus Listing 1 bringt Licht in dieses Dunkel und zeigt eine durchnummerierte Liste der einzelnen Felder (Abbildung 1).
Listing 1
$ head -n 1 umsaetze.csv | sed -r 's/^"|"$//g;s/"\;"/\n/g' | cat -n

Abbildung 1: Mithilfe einiger Linux-Befehle erhalten Sie eine perfekte Übersicht der Datenfelder einer CSV-Datei.
Für die weitere Auswertung greifen Sie dann am besten zur Skriptsprache Gawk [1], deren C-ähnlicher und dementsprechend einfacher Befehlssatz sich sehr leicht erlernen und anwenden lässt. Zudem kann Gawk sehr gut mit Zeichenketten umgehen und eignet sich dadurch bestens dazu, die Kontendaten auszuwerten und übersichtlich darzustellen.
Datenfelder sprechen Sie in Gawk über die Variablen $1, $2, $3 und so weiter an. Wollen Sie zum Beispiel das erste Datenfeld einer Datei ausgeben, erledigen Sie das mit der Anweisung print $1. Zur Ausgabe mehrerer Felder trennen Sie diese im einfachsten Fall jeweils durch einen Tabulator. Für komplexere Daten mit unterschiedlicher Breite greifen Sie besser zur Anweisung printf, die Sie vielleicht schon aus C kennen.
Wie Sie in Abbildung 1 sehen, fällt die Felderliste in unserem Beispiel recht umfangreich aus, sodass Sie sich als Erstes die für Sie relevanten Felder aussuchen. Hier handelt es sich dabei um den Buchungstag, den Buchungstext, den "Beguenstigter/Zahlungspflichtiger" und den Betrag. Diese entsprechen den Feldnummern 2, 4, 12 und 15, die Sie in Gawk entsprechend mit $2, $4, $12 und $15 ansprechen beziehungsweise verarbeiten.
Eigene Banking-Software
Im Folgenden stellen wir Ihnen ein relativ kurzes Beispielskript vor, das Sie als Basis für eigene Anpassungen und Erweiterungen nutzen können. Sie finden den Code aus Listing 2 auch auf der Heft-DVD dieser Ausgabe.
Das Skript beginnt damit, dass Sie mit einem Zenity-Dateiauswahldialog die CSV-Datei auswählen, die Sie aus dem Online-Banking heruntergeladen haben (Zeile 3). Fehlt Zenity [2] auf Ihrem System, installieren Sie es über die Kommandozeile nach, etwa mit sudo apt-get install zenity oder mit zypper install zenity. Im Zweifelsfall genügt es aber auch schon, einfach den Pfad zur CSV-Datei mit dem Kommando datei=$(HOME)/Pfad/umsaetze.csv in der Variablen datei abzuspeichern. Enthält der Dateipfad Leerzeichen, schließen Sie ihn in Anführungszeichen ein.
Damit Gawk die CSV-Datei auswerten kann, müssen Sie die Daten ein wenig anpassen. Die Skriptsprache unterscheidet die Datenfelder per Feldtrenner voneinander. Dabei stören die erste Zeile mit den Namen der Datenfelder sowie die Anführungszeichen am Anfang und am Ende jedes Datensatzes. Darum kümmert sich die Sed-Anweisung in der Zeile 5 des Skripts. Anschließend wird die Ausgabe mithilfe einer Pipe an das eigentliche Gawk-Skript weitergeleitet.
Gawk braucht zudem einen explizit definierten Feldtrenner, den die Zeile 28 mit dem Befehl FS="\";\"" definiert. FS ist nur eine der in Gawk vordefinierten Variablen. Man kann sich solche Variablen leicht einprägen, indem man sich die englische Bezeichnung vor Augen führt, in diesem Fall “Field Separator”. Eine andere vordefinierte Variable in Gawk wäre RS, ausgeschrieben “Record Separator”. Mit dieser Variablen definieren Sie bei Bedarf einen anderen Trenner für Datensätze.
Weiter mit Gawk
Das eigentliche Gawk-Skript beginnt in der Zeile 7, der Programmcode für Gawk steht innerhalb von Hochkommas. Die Funktion einzeln() legt die einzelnen Begünstigten beziehungsweise Zahlungspflichtigen im Array uniq[] ab (Zeile 8 bis 19). Sie kommt in Zeile 40 beim Lesen jeder Dateizeile zum Einsatz und fügt dem Array nur bislang noch nicht erfasste Elemente hinzu.
Variablen oder Arrays müssen Sie in Gawk nicht erst mühsam deklarieren: Schon eine neue Bezeichnung und Wertzuweisung genügt, um die entsprechende Variable oder das Array im weiteren Programmverlauf nutzen zu können. Das und auch andere Features erleichtert es ungemein, Gawk zum Prototyping zu benutzen, also schnell eine Software zu erstellen und sie dann eventuell mit einer anderen Programmiersprache und vielleicht auch einer passenden GUI umzusetzen.
Eine weitere im Skript des Öfteren genutzte Funktion sehen Sie in den Zeilen 21 bis 25: trennzeile() beschicken Sie mit einer Zahl als Parameter, woraufhin sie eine entsprechend lange Folge von Minuszeichen mit einem Zeilenumbruch am Ende ausgibt. So können Sie im späteren Programmverlauf Überschriften oder Ergebnisse besser hervorheben und übersichtlicher darstellen.
Listing 2
#!/bin/bash
datei=$(zenity --file-selection)
sed '1d;s/^"//;s/"$//' $datei |
gawk '
function einzeln(suche){
hinzu = 1
for (x in uniq){
if (uniq[x] == suche){
hinzu = 0
}
}
if ( hinzu == 1 ) {
uniq[iu++] = suche
hinzu = 0
}
}
function trennzeile(zz){
for( i = 0 ; i < zz ; i++ )
printf "%s", "-"
printf "\n"
}
BEGIN{
FS="\";\""
}
$12 ~ / {3,}/{
sub(/ {3,}/,"",$12)
}
$12 == "" {
$12 = $4
}
{
einzeln($12)
length($12) >= breite ? breite = length($12) : breite = breite
sub(",",".",$15)
datum[z] = $2
zahlpfl_beg[z] = $12
betrag[z] = $15
z++;
}
END{
for (x in uniq){
printf "\n"
printf "%*s\n", breite, uniq[x]
trennzeile(breite)
for ( i = 0; i <= z ; i++){
if ( uniq[x] == zahlpfl_beg[i] ){
printf "%-*s%10.2f\n", breite-10, datum[i],betrag[i]
zsumme+=betrag[i]
positionen++
}
}
trennzeile(breite)
printf "%-12s%*s\n", "Positionen:", breite-12, positionen
printf "%-10s%*.*f\n", "Summe:", breite-10, 2, zsumme
positionen == 0 ? positionen = 1 : positionen = positionen
printf "%-17s%*.*f\n", "Durschschnitt:", breite-17, 2, zsumme/positionen
bilanz[uniq[x]] = zsumme
trennzeile(breite)
zsumme = 0
positionen = 0
}
print "\n\n\n"
trennzeile(breite + 15)
printf "%-*s%15s\n",breite, "Bilanz", "Beträge in ¤"
trennzeile(breite + 15)
for ( b in bilanz ){
printf "%-*s%15.2f\n",breite, b, bilanz[b]
bilanzsumme+=bilanz[b]
}
trennzeile(breite + 15)
printf "%-*s%15.2f\n",breite, "Bilanzsumme", bilanzsumme
trennzeile(breite + 15)
trennzeile(breite + 15)
}
'
Aller Anfang …
Im BEGIN-Block ab Zeile 27 können Sie Vordefinitionen treffen oder Variablen definieren. Er wird nur einmal durchlaufen, bevor das Skript irgendeine Zeile einliest. Hier bietet sich also die passende Gelegenheit, den Feldtrenner zu definieren, falls Sie das nicht schon vorher in Form einer Aufrufoption erledigt haben.
Sogenannte Patterns entscheiden im weiteren Verlauf, wann Gawk in Aktion tritt. Mit solchen Patterns beziehungsweise Bedingungen bewirken Sie beispielsweise, dass das Skript nur ganz bestimmte Zeilen ausliest und bearbeitet. Sie müssen nicht zwangsläufig Patterns definieren: Soll Gawk alle Zeilen bearbeiten, dann lassen Sie einen solchen Bedingungsausdruck einfach weg. Die Anweisungen innerhalb des Patterns wendet Gawk also auf alle Zeilen an.
In der Zeile 31 sehen Sie die Bedingung $12 ~ / {3,}/, die abfragt, ob das Feld 12 eine Folge von drei oder mehr Leerzeichen enthält, und eine solche gegebenenfalls durch die Anweisungen innerhalb der folgenden geschweiften Klammern entfernt. Dort kommt dazu in der Zeile 32 die Funktion sub() (“substitute”) zum Einsatz, eine vordefinierte Gawk-Funktion.
Ein weiterer Umstand, den es zu bedenken gilt: Viele Kontenbewegungen werden maschinell angestoßen und enthalten aus diesem Grund in bestimmten Datenfeldern viele Spaces oder andere für die Ausgabe sinnlose Zeichen. In einigen Fällen bleibt das Feld für Begünstigte oder Zahlungspflichtige leer, etwa bei bankinternen Buchungen wie beispielsweise für Zinsen oder Abschlüsse.
Um trotzdem zu erkennen, um was für einen Vorgang es sich handelt, kann man andere Felder zurate ziehen. Die Zeilen 35 bis 37 kümmern sich um alle Datensätze, bei denen das Feld 12 keine Informationen enthält: Das Skript füllt es dann mit dem Text aus Feld 4, also dem Buchungstext. So haben Sie später eine Information zur getätigten Buchung an der Hand.
Im Block ab Zeile 40 wird zunächst die Funktionen einzeln() aufgerufen. Zeile 42 ersetzt im Feld 15, das die Beträge enthält, das Komma durch einen Dezimalpunkt, damit Gawk mit den Beträgen rechnen kann. Die Zeilen 43 bis 45 definieren drei Arrays für Datum, Betrag und Empfänger/Zahlungspflichtige und lesen die entsprechenden Felder der Zeilen ein.
Ausgabe
Der END-Block ab Zeile 48 kümmert sich um die Ausgabe der Daten. Die For-Schleife von Zeile 49 bis Zeile 70 durchläuft alle Elemente des Arrays uniq[]. Die Zeilen 50 bis 52 erstellen eine Überschrift für die einzelnen Abschnitte. Eine innere For-Schleife in den Zeilen 53 bis 60 prüft nun alle Elemente des Arrays zahlpfl_beg[] auf Gleichheit und veranlasst bei einem Treffer in der Zeile 55 die Ausgabe des Datums und des Betrags. In den Zeilen 56 und 57 addieren Variablen die Zwischensumme auf und zählen die Anzahl der Positionen hoch.
Die Zeilen 61 bis 69 sorgen für die Ausgabe der errechneten Zwischensummen, Anzahl der Positionen und auch des Durchschnitts der einzelnen Begünstigten/Zahlungspflichtigen. Die Zeile 66 legt das assoziative Array bilanz[] an, das alle Zwischensummen für die einzelnen Begünstigten/Zahlungspflichtigen aufnimmt (Abbildung 2). Diese brauchen Sie im späteren Verlauf, um eine Bilanz zu erstellen.

Abbildung 2: Sie können Ihre Kontenbewegungen im Terminal betrachten, dabei innerhalb des Terminals scrollen oder einen Pager wie etwa Less benutzen.
Dabei gehen Sie ganz ähnlich vor wie bei den einzelnen Abschnitten. Die Zeilen 72 bis 75 sorgen für eine Überschrift mit Trennlinien. In den Zeilen 77 bis 80 durchläuft in einer Schleife das Array bilanz[]und gibt die Assoziationen, die Begünstigten/Zahlungspflichtigen und die Zwischensummen aus. Die Beträge werden in Zeile 79 in der Variablen bilanzsumme aufsummiert. Zu guter Letzt folgt in den Zeilen 82 bis 85 die Ausgabe der Bilanzsumme.
Erster Test
Haben Sie das Skript eingegeben, starten Sie es von der Kommandozeile und leiten es am besten gleich an einen Pager weiter, etwa mit dem Befehl ./bank2.sh | less. Das bietet den Vorteil, dass Sie durch die Ergebnisse scrollen können.
Sobald Sie das Skript einmal ablaufen lassen, stellen Sie fest, dass das Gesamtkonstrukt T-Konten aus der Buchführung [3] ähnelt und am Ende eine Bilanz mit entweder einem Gewinn oder Verlust steht. Sie können ganz genau ablesen, ob Sie für einen Zeitraum ein Plus oder Minus erzielt haben (Abbildung 3). Auch in den Blocks zu den einzelnen Begünstigten/Zahlungspflichtigen erkennen Sie auf einen Blick, welche Summen und Durchschnittsbeträge pro Bewegung eingenommen oder ausgegeben wurden.

Abbildung 3: Die Bilanz sagt Ihnen dann schließlich, ob Sie aufs Jahr gesehen entweder einen Gewinn oder Verlust eingefahren haben.
Mehr Komfort
Eine noch komfortablere Darstellung erhalten Sie, indem Sie die Ausgabe des Programms einfach in eine Textdatei umleiten und anschließend mit einem Editor oder einer Textverarbeitung öffnen. Für das Umleiten nutzen Sie den Befehl ./bank2.sh > bilanz.txt.
Wenn Sie diese Datei dann mit LibreOffice Writer öffnen, eröffnet sich damit eine einfache Möglichkeit, die Ausgabe in ein PDF-Dokument umzuwandeln. Auf diesem Weg lassen Sie bei Bedarf auch zwei oder mehr Seiten auf ein Blatt ausgeben, sodass Sie mehrere Blocks von Begünstigten/Zahlungspflichtigen einfach überblicken.
Mithilfe des Skripts erfassen Sie bequem, wie hoch Ihre jährlichen Stromkosten, die Miete oder andere Fixkosten ausfallen. Parallel erhalten Sie eine perfekte Jahresübersicht Ihres Gehalts – oder auch mehrerer Gehälter, wenn Sie für verschiedene Unternehmen tätig waren. Während Sie die einzelnen Begünstigten/Zahlungspflichtigen durchsehen, haben Sie stets auch den Durchschnitt der zugehörigen Bewegungen im Auge.
Darüber hinaus erkennen Sie mithilfe des Skripts genau, wo, wann und wie oft Sie bei einem Händler eingekauft haben. Sie sehen, welche Beträge dort im Einzelfall und im Durchschnitt über den Ladentisch gingen. Die Bilanz am Ende der Ausgabe verdeutlicht, wohin größere Summen flossen, die Sie dann einfach im Block zum entsprechenden Begünstigten näher unter die Lupe nehmen. Dadurch erkennen Sie schnell, an welchen Stellen Sie eventuell den Gürtel enger schnallen sollten.
Fazit
Die übersichtliche Zusammenfassung aller Bankbewegungen verschafft Ihnen nicht nur einen besseren Überblick aller Kontenbewegungen, sondern hilft auch dabei, Einsparpotenziale zu erkennen – und das mit nicht einmal 100 Zeilen Programmcode. Davon können andere App- oder Programmentwickler nur träumen.
Zudem eröffnet die selbst erstellte Banking-Software die Möglichkeit zu maßgeschneiderten Modifikationen und Erweiterungen. So wäre es auch interessant, Maximal- und Minimalwerte (etwa beim Gehalt) auf das Jahr oder über die Jahre zu berechnen und durchzusehen. Solche Ergänzungen lassen sich im vorliegenden Skript dank der vielfältigen Möglichkeiten von Gawk schnell realisieren.
Glossar
-
Camt
-
Kurz für Cash Management. Von der SEPA definiertes XML-Format für den Datenaustausch zwischen Banken und Kunden. Camt hat 2014 die früher verbreiteten SWIFT-EDIFACT-Formate abgelöst.
-
T-Konten
-
Für Lehrzwecke und als Hilfsmittel zur Kontierung beliebte Darstellung. Über einem waagerechten Strich befinden sich Nummer und Bezeichnung des Kontos, darunter teilt ein senkrechter Strich Soll- (links) und Haben-Buchungen (rechts).
Infos
-
Dokumentation zu Gawk: https://www.gnu.org/software/gawk/manual/html_node/index.html
-
Zenity und Alternativen: Kristian Kissling, “Klacker, klacker, klick, klick”; LU 06/2008, S. 36, https://www.linux-community.de/15794





