• Willkommen im Linux Club - dem deutschsprachigen Supportforum für GNU/Linux. Registriere dich kostenlos, um alle Inhalte zu sehen und Fragen zu stellen.

[gelöst]Mit awk (?) Feld komplett ersetzen

Heute bitte ich um Eure Hilfe bei folgendem "Problem", welches für Euch sicher keines ist :

Ich habe mir ein Skript für die Bearbeitung meiner downgeloadeten Bankumsätze erstellt.

Hauptaufgabe des Skripts ist es, den Verwendungszweck im Feld 2 auf eine Maximallänge zu kürzen und "unnötige" Informationen zu elimieren, abzukürzen oder zu ersetzen. Weiter wird jeweils der neue Saldo errechnet, weil die Bank ihn leider nicht liefert. Dies habe ich auch zufriedenstellend gelöst.

Was mir nicht gelingt, ist das komplette Ersetzen des Feldinhaltes durch eine konstante Zeichenfolge, wenn irgendwo im Feld eine bestimmte andere Zeichenfolge vorhanden ist.

Konkret liegt die gesuchte Zeichenfolge irgendwo jenseits der Position 180 im Feld. Die Informationen davor sind unnötig und für mich nicht informativ.

Ich möchte entweder beim Vorliegen der Zeichenfolge den Inhalt des Feldes komplett austauschen oder alternativ alle Zeichen vor der Zeichenfolge löschen.

Das Skript sieht (eingekürzt) bisher so aus :

Code:
#!/bin/sh
#LANG="de_DE"

csvnam1="Eingabe"
ausnam1="Ausgabe"

recode ISO-8859-1..UTF-8 /home/benutzer/Daten/$csvnam1.csv

echo "Gib Vorsaldo mit Dezimalpunkt (.)"
read Vsaldo

cat /home/benutzer/Daten/$csvnam1.csv | gawk -v sum=$Vsaldo -F ";" '{OFS=";"} \
{sub("LASTSCHRIFT ", "LS ", $2)} {sub("GUTSCHRIFT ", "GS ", $2)} \
{sub("RWE VERTRIEB AG RWE RR BELEG", "RWE", $2)} \
{sub("UMBUCHUNG BARGELDABHEBUNG", "BARGELDABHEBUNG", $2)} \
{sub(",", ".", $3)} {gsub("\"", "")} \
{print substr($1,1,10), substr($2,1,35), substr($3,1,8), sum+=$3}' > arb-bank.csv

echo '"Datum";"Verwendungszweck";"Betrag";"Saldo"' > /Datenpartition/Privat/BANK/Banking-Ex-Import/$ausnam1.csv

cat arb-bank.csv | \
gawk -F ";" '{OFS=";"} {sub("\\.", ",", $3)} {sub("\\.", ",", $4)} {print $0}' \
>> /Datenpartition/Privat/BANK/Banking-Ex-Import/$ausnam1.csv

Die Suche mittels Wiki oder Google bringt mich mangels gut gewählter Suchkriterien nicht weiter.

Für Eure "Nachhilfe" wäre ich Euch dankbar.

Gruss H.
 

framp

Moderator
Teammitglied
Hilfreich wäre es wenn Du ein paar Beispieleingabezeilen liefern würdest sowie die Ausgabezeilen und dasselbe auch für Deine zwei Problemfälle, denn mir ist es nicht so ganz klar was denn nun genau Dein Problem ist.
 
Die "recode"-Zeile hast Du von mir, oder?

Mein Tipp zum Problem: Tu' Dir einen Gefallen und lerne eine vernünftige Sprache zur Datenverarbeitung, am besten Python. Die ist sehr einfach zu lernen, äußerst mächtig, und der Code ist sehr sauber und daher besonders gut zu warten.
Mit Python kannst Du fliegen:

http://xkcd.com/353/

Ansonsten: Wenn Du nicht ein paar (anonymisierte) Beispieldaten postest und sagst, was davon wohin soll, kann man Dir schlecht helfen, weil man so kein Skript schreiben und dann testen kann.
 
Danke Euch beiden für Euer Hilfsangebot.

Zunächst für framp eine Beispieleingabezeile :

Code:
02.10.2013;"SEPA LASTSCHRIFT 000000000000000012345678 1234567890123 2011-11-20 DE3000100000001272 RUNDFUNK ARD ZDF DRADIO 0001111111 12345678 123456789 2013090212345678 RUNDFUNK 10 2013 - 12 2013 VIELEN DANK FUER IHREN RUNDFUNKBEITRAG";"-53,94";"";"";'DE12345678901234567890'

Dieser Verwendungszweck ist mir (und auch meinem Bankingprogramm hibiscus) einfach zu lang. Konstant ist immer der String "SEPA LASTSCHRIFT" mit etlichen Nullen (die mich auch "null" interessieren), der Rest ist variabel bis auf "RUNDFUNK ARD ZDF DRADIO".

Ich möchte gerne entweder die Zeichen vor dieser Info eleminieren oder das ganze Feld $2 durch eine Zeichenfolge z.B. "RUNDFUNKBEITRAG" ersetzen.

Jetzt zu abgdf :

Ja, die "recode-Zeile" habe ich mir von Dir abgeguckt. Danke nochmal dafür.

Jetzt aber zu Deinem Tipp

... lerne eine vernünftige Sprache zur Datenverarbeitung ...

hiernach habe ich eigentlich kein grosses Verlangen. Im Laufe meines - inzwischen vergangenen - beruflichen Werdegangs habe ich zunächst RPG, dann Assembler (auch Easycoder), wenig PL/1, Fortran und Cobol im "Grossrechnerumfeld" erlernt und angewendet. Eigentlich möchte ich mir schon für meine Zwecke mit der Shell und awk, sed und Konsorten weiterhelfen können. Deinen Link werde ich mir trotzdem mal anschauen.

Nochmals Dank und Gruss H.
 

framp

Moderator
Teammitglied
halo44 schrieb:
...Ich möchte gerne entweder die Zeichen vor dieser Info eleminieren oder das ganze Feld $2 durch eine Zeichenfolge z.B. "RUNDFUNKBEITRAG" ersetzen...
Den zweiten Teil kannst Du z.B. so erschlagen:
Code:
/SEPA LASTSCHRIFT/ { print "$1 RUNDFUNKBEITRAG $3 $4 $5"; }
Den ersten Teil habe ich immer noch nicht verstanden wie Du ihn haben willst :???: Gib doch einfach ein Beispiel wie in- und output aussehen sollen ;)
 
Code:
#!/usr/bin/python
# coding: iso-8859-1

a = """02.10.2013;"SEPA LASTSCHRIFT 000000000000000012345678 1234567890123 2011-11-20 DE3000100000001272 RUNDFUNK ARD ZDF DRADIO 0001111111 12345678 123456789 2013090212345678 RUNDFUNK 10 2013 - 12 2013 VIELEN DANK FUER IHREN RUNDFUNKBEITRAG";"-53,94";"";"";'DE12345678901234567890'"""

b = a.split(";")
if "SEPA LASTSCHRIFT" in b[1] and "RUNDFUNK ARD ZDF DRADIO" in b[1]:
    b[1] = "RUNDFUNKBEITRAG"
c = ";".join(b)
print c
Ist das nicht einfach?
 
framp schrieb:
... Den zweiten Teil kannst Du z.B. so erschlagen:
Code:
/SEPA LASTSCHRIFT/ { print "$1 RUNDFUNKBEITRAG $3 $4 $5"; }

Angewandt bei mir in der Form
Code:
$2~/RUNDFUNK ARD ZDF DRADIO/ { print "$1 RUNDFUNKBEITRAG $3 $4"; }

bringt mir
Code:
$1 RUNDFUNKBEITRAG $3 $4
was nicht der Zweck der Übung ist.

Ich möchte gerne erhalten
Code:
02.10.2013;"RUNDFUNKBEITRAG";"-53,94";"";"";'DE12345678901234567890'
Den Input habe ich ja vorhin schon geliefert.

Hoffentlich habe ich mich jetzt verständlich ausgedrückt.

Gruss H.
 

framp

Moderator
Teammitglied
halo44 schrieb:
...
Code:
$2~/RUNDFUNK ARD ZDF DRADIO/ { print "$1 RUNDFUNKBEITRAG $3 $4"; }
bringt mir
Code:
$1 RUNDFUNKBEITRAG $3 $4
was nicht der Zweck der Übung ist...
:eek:ps: Man sollte vorher doch mal den Code testen :eek:ps: Nimm besser
Code:
$2~/RUNDFUNK ARD ZDF DRADIO/ { print $1,"RUNDFUNKBEITRAG",$3,$4; };
 
Hallo H.
halo44 schrieb:
doch, das sieht gut aus

Habe Ähnliches gelernt [damals noch Business Basic für Nixdorf :)] und mir die Grundzüge Python vor ein paar Tagen beigebracht... ist leicht - und man rostet nicht, im Kopf

Code:
#!/usr/bin/python
# coding: iso-8859-1

dateiname = '/home/BENUTZER/daten/linux/python/test/bank.txt'

# Datei lesend öffnen, gesamt einlesen
with open(dateiname,  'r') as d:
    zeilen = d.readlines()
# Schliessen der Datei übernimmt die with-Klammerung

# Verarbeitung aller Zeilen
for zeile in zeilen:
    a = zeile
    
    # dann anschliessend den Rest aus dem Forumspost EINGERÜCKT einfügen

Viel Spass
 
Danke für die weiteren Infos.

Die Lösung von framp funktioniert jetzt. Ich muß nur noch sehen, wie ich dann die übrigen Zeilen, auf die die Bedingung nicht zutrifft, in meinem anfangs gezeigten Skript verarbeite.

Dazu komme ich ich erst morgen früh, weil ich jetzt zu einer Einladung muß :/

Auch Dank für den weiteren Hinweis zu python.

Bis morgen.

Gruss H.
 
halo44 schrieb:
abgdf schrieb:
... Ist das nicht einfach?

doch, das sieht gut aus, aber wie übergebe ich den Inhalt von a aus meiner Datei an das Skript ? Und c aus dem Skript in die Datei ins Feld 2?
Ok, das ist eine berechtigte Frage. Es stimmt schon, daß man bei Python eher davon ausgeht, daß man das ganze Skript darin schreibt und nicht, daß man ein bash-Skript hat, in das man Zusatzcode irgendwie hineinwurschteln möchte, wie es vielleicht für awk und sed typisch ist.
Python bietet zwar u.a. einen Switch "-c" für Einzeiler, aber das ist nicht so leicht zu benutzen, weil ja die Einrückungen von Bedeutung sind und man weniger mit Semikola arbeitet, um Codezeilen zu trennen.
Wenn Du jetzt denkst, Python sei "nicht so leicht zu benutzen" wäre das aber ein Irrtum, nur weil es eben für diesen einen Spezialfall, der eigentlich sowieso ein schlimmer Hack ist, nicht so gut ausgelegt ist. Natürlich könnte ein Pythonskript über Pipe Input empfangen ("echo Hallo | pythonscript.py"), aber dann hättest Du zwei Skripte, was ja auch nicht Sinn der Sache ist.

Ich kann nur sagen, in bash mit Daten umzugehen, ist generell viel schwieriger, weil die Trennung bei Leerzeichen, das automatische Expandieren von Strings und das unerwartete Auftreten von Subshells mit eigenem Gültigkeitsbereich für Variablen einen in den Wahnsinn treiben können. All diese Probleme treten in Python nicht auf.
 
Gut, heute morgen habe ich auch das Python-Skript, dankenswerterweise von komma4 ergänzt, getestet. Die Ausgabe des print landet allerdings nur in der Konsole. Ich bräuchte sie allerdings in der (oder einer anderen) Datei. Ausserdem fehlen noch die anderen Modifizierungen aus dem Bash-Skript, z.B.

Code:
{sub("LASTSCHRIFT ", "LS ", $2)} {sub("GUTSCHRIFT ", "GS ", $2)} \
{sub("RWE VERTRIEB AG RWE RR BELEG", "RWE", $2)} \
{sub("UMBUCHUNG BARGELDABHEBUNG", "BARGELDABHEBUNG", $2)} \
{sub(",", ".", $3)} {gsub("\"", "")} \
und die Rechenoperation
Code:
... sum+=$3 ...

Ansonsten scheint mir (zumindest bis jetzt) python weniger "kryptisch" zu sein, als ich glaubte.

Die Lösung im Bash-Skript von framp funktioniert für die Zeile, welche die Bedingung erfüllt. Die übrigen Zeilen, auf die zunächst auch die erwähnten anderen Modifizierungen angewandt und dann ausgegeben werden müssen, fallen unter den Tisch.

Ich ahnte schon, dass die Lösung nicht ganz so trivial ist :???:

Mit meinem alten 16-bittigen COBOL-Compiler kann ich zwar eine exe erstellen, welche das Problem löst. Allerdings läuft die nicht mit Unterstützung von wine, der 16-bit wegen. Ich muss dann über Virtualbox gehen und da über XP, weil auch Win7 bei 16-bit mit dem Kopf schüttelt.

Vielleicht findet doch noch jemand einen weniger aufwendigen Weg.

Gruss H.
 

framp

Moderator
Teammitglied
Mit gefällt bei der Problemlösung auch Python besser. Mit awk bekommt man das aber auch hin. Anbei ein erweitertes PythonSnippet basierend auf dem Vorschlag von abdgf:
Code:
#!/usr/bin/python
# coding: iso-8859-1

#import sys

#for line in sys.stdin:
#    process line

x = """02.10.2013;"SEPA LASTSCHRIFT 000000000000000012345678 1234567890123 2011-11-20 DE3000100000001272 RUNDFUNK ARD ZDF DRADIO 0001111111 12345678 123456789 2013090212345678 RUNDFUNK 10 2013 - 12 2013 VIELEN DANK FUER IHREN RUNDFUNKBEITRAG";"-53,94";"";"";'DE12345678901234567890'"""
xx = """02.10.2013;"NIX AENDERN 000000000000000012345678 1234567890123 2011-11-20 DE3000100000001272 RUNDFUNK ARD ZDF DRADIO 0001111111 12345678 123456789 2013090212345678 RUNDFUNK 10 2013 - 12 2013 VIELEN DANK FUER IHREN RUNDFUNKBEITRAG";"-53,94";"";"";'DE12345678901234567890'"""
y = """02.10.2013;"GUTSCHRIFT 2011-11-20 DE3000100000001272 RUNDFUNK ARD ZDF DRADIO 0001111111 12345678 123456789 2013090212345678 RUNDFUNK 10 2013 - 12 2013 VIELEN DANK FUER IHREN RUNDFUNKBEITRAG";"-53,94";"";"";'DE12345678901234567890'"""
z = """02.10.2013;"UMBUCHUNG BARGELDABHEBUNG 000000000000000012345678 1234567890123 2011-11-20 DE3000100000001272 RUNDFUNK ARD ZDF DRADIO 0001111111 12345678 123456789 2013090212345678 RUNDFUNK 10 2013 - 12 2013 VIELEN DANK FUER IHREN RUNDFUNKBEITRAG";"-53,94";"";"";'DE12345678901234567890'"""
zz = """02.10.2013;"BARGELDABHEBUNG 000000000000000012345678 1234567890123 2011-11-20 DE3000100000001272 RUNDFUNK ARD ZDF DRADIO 0001111111 12345678 123456789 2013090212345678 RUNDFUNK 10 2013 - 12 2013 VIELEN DANK FUER IHREN RUNDFUNKBEITRAG";"-53,94";"";"";'DE12345678901234567890'"""

lines=[x,xx,y,z,zz]

tokenMap={"SEPA LASTSCHRIFT": "*RUNDFUNK ARD ZDF DRADIO*",
       "GUTSCHRIFT": "*GS*",
       "UMBUCHUNG BARGELDABHEBUNG": "*BARGELDABHEBUNG*" }

tokenKeys=tokenMap.keys()

for line in lines: 
    token = line.split(";") 
    replacement=[tokenMap[key] for key in tokenKeys if key in token[1]]
    if replacement:
        token[1]=replacement[0]
        line = ";".join(token)
    print line
 
framp schrieb:
... Anbei ein erweitertes PythonSnippet basierend auf dem Vorschlag von abdgf ...

Diese Erweiterung macht exakt das, was sie soll. Allerdings auf Basis der im Skript über die "lines" übermittelten Werte mit Ausgabe in die Konsole.

Wenn dies auch aus einer Datei gelingen und das Ergebnis wieder in die (oder eine andere) Datei ausgegeben würde, wären wir - den Rechenvorgang noch aussen vor gelassen - am Ziel.

Vielen Dank auch jetzt nochmal für das, was ich seit gestern wieder "Neues" erfahren habe. Ob ich auch etwas gelernt habe, wird sich noch zeigen. Zumindest befasse ich mich parallel schon mal mit python :D

Gruss H.

Edit : inzwischen habe ich schon mal gelernt, wie man eine Datei als Eingabe und eine andere als Ausgabe zuweist. In der Ausgabe sind auch die Ersetzungen korrekt vorgenommen. Jetzt muss ich noch das verbliebene Feld 2 auf die Maximallänge von 35 kürzen und die Rechenoperation vollziehen. Wenn "alle Stricke reissen" mache ich das in einem nachgeschalteten Bash-Skript, wo ich die Syntax beherrsche :

Code:
{print substr($1,1,10), substr($2,1,35), substr($3,1,8), sum+=$3}'
 

framp

Moderator
Teammitglied
halo44 schrieb:
...Wenn dies auch aus einer Datei gelingen und das Ergebnis wieder in die (oder eine andere) Datei ausgegeben würde, wären wir - den Rechenvorgang noch aussen vor gelassen - am Ziel...
Mit folgendem Snippet kannst Du in bash Script per Python die Eingabe lesen.
Code:
#!/bin/bash
cat $0 | python -c \
'
import sys
for line in sys.stdin:
    print "-- %s" % line,
'
 
@ framp

Danke für den weiteren Tip - werde mich später am Abend oder morgen damit beschäftigen.

Ansonsten funktioniert mein Versuch mit nachgeschaltetem Bash-Skript :

Code:
#!/bin/sh
./arb-test.py
sh arb-test.sh

Gruss H.
 

framp

Moderator
Teammitglied
Das ist vermutlich der bessere Weg. Wenn Du das Python in die bash mit einbindest wie ich oben vorgeschalgen habe musst Du mit den ' Zeichen aufpassen und die escapen. Ich persönlich bin kein Freund von escapen und würde das Python Script auch in eine eigene Datei stecken. Aber das Lesen von sysin funktioniert trotzdem wie beschrieben. Dann kannst Du im bash Script sowas schreiben wie
Code:
cat meineDaten.da | meinPython.py > meineAusgabe.dat
 
Tut mir leid, daß ich mich gestern vielleicht etwas zu sehr für Python ereifert habe. :)
Es ist nur, solche Verarbeitungen von Kontodaten können unter Umständen ja recht komplex werden. Das Skript (in Python), das bei mir die E/Ü-Rechnung vorbereitet, hat z.B. 2.189 Zeilen. Dabei hat sich bei mir Python eben bewährt, ich glaube auch nicht, daß ich das mit Shell-Tools und Pipes überhaupt geschafft hätte; das hätte ich allerdings auch gar nicht erst versucht. Die Sprache auszuwählen, die am besten für ein Projekt geeignet ist, gehört ja auch als erste Weichenstellung zum Projekt selbst.

Ich meine auch, daß dieses Thema hier eigentlich eher nach "Programmierung" als nach "Konsole" gehören würde. Aber laßt es vielleicht doch erstmal hier, sonst findet's nach dem Verschieben wieder keiner.
 
Oben