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

Bash und csv

Fly_67

Newbie
Hallo zusammen

Ich bin ein absoluter Anfäger in Bash Script.
Ich muss ein Script schreiben welches die Zweite Line einer CSV Datei kopiert und anschliessend wieder hinzufügt. Dabei sollte der letzte Eintrag gelöscht werden. Leider habe ich keinen Plan wie ich das machen soll.

Die CSV Datei sieht wie folgt aus:
Code:
kunde;nummer;adresse;ort;
meier;2343;Schulweg 12;bern;

Die bearbeitete CSV Datei sollte anschliessend so aussehen:
Code:
kunde;nummer;adresse;ort;
meier;2343;Schulweg 12;bern;
meier;2343;Schulweg 12;;

Kann mir da jemand helfen?
 

framp

Moderator
Teammitglied
Eine Moeglichkeit ist den String auseinanderzunehmen -> http://stackoverflow.com/questions/918886/split-string-based-on-delimiter-in-bash und dann ohne das entsprechende Feld wieder zusammenzusetzen. Ist vielleicht im konkreten Fall etwas umstaendlich aber relativ leicht zu verstehen. Alternativ mit sed die zweite Zeile kopieren und dann den letzten Teil loeschen.
 

abgdf

Guru
bash ist nach meiner Meinung mehr für die Arbeit mit Dateien gedacht als für die Arbeit mit Daten in Dateien.
Die Werkzeuge, die dafür vorgesehen sind, sind in erster Linie sed und awk.

Viele lernen dafür aber die etwas umfangreichere Sprache Perl und bauen damit einen Einzeiler. Möglich wäre z.B. (wenn Deine Daten sich in "test" befinden):
Code:
perl -e '$x=0;while(<>){$x++;chomp;if($x==2){@a=split(";");pop(@a);$b=join(";",@a)}print"$_\n"}print"$b;;\n"' test
 

framp

Moderator
Teammitglied
abgdf schrieb:
bash ist nach meiner Meinung mehr für die Arbeit mit Dateien gedacht als für die Arbeit mit Daten in Dateien.
Die Werkzeuge, die dafür vorgesehen sind, sind in erster Linie sed und awk.
Da stimme ich Dir zu 100% zu.
Fly_67 schrieb:
...Ich bin ein absoluter Anfäger in Bash Script.Ich muss ein Script schreiben...
Jetzt ist die Frage welche Sprache er damit meint. Sowohl sed als auch awk oder Perl sind im engeren Sinne nicht bash. Wenn das Ziel ist, mit der bash zu arbeiten bzw sie kennenzulernen - was ich auch denke - dann kann man die Aufgabe auch mit bash loesen. Das was Du in Perl vorgeschlagen hast geht auch in bash und sieht auch sehr ähnlich aus (Siehe meinen Link oben zu bash arrays un dem splitten in Tokens).
 

abgdf

Guru
:D

Code:
head -n 2 test | tail -n 1 | cut -d ";" -f 1,2,3

Geht aber nur, weil's ein einfach gelagerter Spezialfall ist. ;)
 
OP
F

Fly_67

Newbie
Vielen Dank für die Antworten.

Es muss nicht umbedingt bash sein.
Ich sollte diese Aufgabe lösen, wie ist nicht vorgeben.
Meine Idee, war einfach dies mit Bash zu lösen.
Aber wenn es ohne Bash einfacher zu lösen ist, werde ich es ohne Bash versuchen
 

lin

Hacker
hallo Abgdf

guten abend.

abgdf schrieb:
bash ist nach meiner Meinung mehr für die Arbeit mit Dateien gedacht als für die Arbeit mit Daten in Dateien.
Die Werkzeuge, die dafür vorgesehen sind, sind in erster Linie sed und awk.

Viele lernen dafür aber die etwas umfangreichere Sprache Perl und bauen damit einen Einzeiler. Möglich wäre z.B. (wenn Deine Daten sich in "test" befinden):
Code:
perl -e '$x=0;while(<>){$x++;chomp;if($x==2){@a=split(";");pop(@a);$b=join(";",@a)}print"$_\n"}print"$b;;\n"' test


bei mir ists der umgekerhte weg. Ich habe einen Datensatz der zerlegt werden sollte - in CVS... denke mal mit dem Modul text::CVS gehts wohl ...


Code:
Marias Neustift Neuseetifttown 28 43443 Marias Neussstift Telefonnummer: 009999 FAX-Nummer: 009999-4 E-Mail: prre.inmarsdianeustift@dioesszese-lindddz.at
Marias Puchheim Gmunednertown Stra�e 1b 4800 Attnanger-Puchheim Telefonnummer: 009999 FAX-Nummer: 009999-4 E-Mail: prre.idddnmariapuchheim@dioddezese-lidnddddz.at
Marias Scharten Scharetenstown 1 4612 Schartensbook Telefonnummer: 009999
Marias Schmolln Mareeia Schmollntown 2 5241 Maria Schmolln Telefonnummer: 0072222743/22222209-12 FAX-Nummer: 07722423333/22033333339-17 E-Mail: prre.inmariaschmddolln@ddddioezese-liddnz.at
Mattighofen R�meeeerstra�e 12 5230 Mattighofeeeentown Telefonnummer: 007742/2273 06762222/87762225221 FAX-Nummer: 07222733342/223333373333-22 E-Mail: peipfarre.inmdattighofen@ddddioezese-lddidnz.at
Mauerkirchens Pfarreeeehofstra�e 4 5270 Mauerkirceehentown Telefonnummer: 0077222224/22222262


ich werde mal einiges ausprobieren... Meld mich wieder.

lg lin
 

abgdf

Guru
lin schrieb:
bei mir ists der umgekerhte weg. Ich habe einen Datensatz der zerlegt werden sollte - in CVS... denke mal mit dem Modul text::CVS gehts wohl ...
csv heißt ja "Comma-separated values", Deine Daten sind doch schon im csv-Format, "comma" ist hier das Leerzeichen.

Sag mal, postest Du hier echte Daten, z.B. Telefonnummern?
 

lin

Hacker
hallo und guten Abend abgdf

vielen Dank für die Antwort. Also - ich arbeit mich grade ein in das Thema. Denke, dass ich das Modul Text::CVS eigentlich nicht unbed brauche.

Ist es nicht auch moeglich dass mit dem Split-Befehl zu bewältigen?!

abgdf schrieb:
lin schrieb:
bei mir ists der umgekerhte weg. Ich habe einen Datensatz der zerlegt werden sollte - in CVS... denke mal mit dem Modul text::CVS gehts wohl ...
csv heißt ja "Comma-separated values", Deine Daten sind doch schon im csv-Format, "comma" ist hier das Leerzeichen.

Sag mal, postest Du hier echte Daten, z.B. Telefonnummern?

Dass meine Daten im CSV-Format stehen, das kann ich noch nicht ohne weiteres erkennen. Vielleicht seh ich vor lauter Bäumen den Wald nicht. ;-)

Was hilft denke ich - sind die Doppelpunkte die da im Datenmaterial drinne sind.

Will das Ganze dann mal in ein Spreadsheet bringen.
Freu mich, wenn du mir nochmals hier weiterhilfst.






also der datensatz sieht wie folgt aus - vorweg; Es ist ein (erweiterter) Adressdatensatz, der im Moment in einer Zeilenstruktur vorliegt - einige hundert Zeilen.
Achtung: schon hier muss ich noch dazu sagen dass auch eine encoding iso-8859 Behandlung noetig ist.

Hier nochmals die Beschreibung des Datensatzes u. seiner Struktur im Einzelnen:

Name (des Orts) - kann aus mehreren Worten bestehen

Strasse und Hausnummer
Postleitzahl und Ort: (Ortsname kann aus mehreren Worten bestehen- u. die PLZ sind immer (!!!!) vier Digits)
Telefonnummer: (die dann hinter einem Dopplepunkt folgt - lange Zahlenfolge)
Fax Nummer: (die dann hinter einem Dopplepunkt folgt - lange Zahlenfolge; Achtung diese ist nicht in jeder einzelnen Zeile drinne)
E-Mail: Achtung diese ist nicht in jeder einzelnen Zeile drinne - die kann auch mal fehlen. Ist aber imho nicht soo schlimm. Denn dann gibts einfach nur ein leeres Feld.

also es bleibt festzuhalten: Fax und E-Mail ist nicht in jeder Zeilen drinne.

- die PLZ sind immer (!!!!) vier Digits
- nach dem Namen (der Einrichtung) kommt dann die Strasse mit Hausnummer.

Man kann also davon ausgehen das die erste Zahl im String die Hausnummer ist. Ich denk dass man mit der Split-Funktion hier schonmals ansetzen kann / muss. Der REGEX muss so gebaut werden dass er ..auf die folgenden Besonderheiten "anspricht" :

- die Strasse u. die zugehoerige Hausnummer rausfindet. Diese DATEN kommen vor der ersten Zahl.
- danach - nach der ersten Zahl kommt dann (nach einer Leerstelle) die Postleitzahl (wie gesagt immer vier Digits lang)


hier ein konkretes Beispiel:

PHP:
Pichlsbier beim Wels Pfarrhoferplatz 1 4632 Pichlbier bei Wels Telefonnummer: 0333337444247/6444457770 0655550076/85557765291 FAX-Nummer: 055567247/5556777-4 E-Mail: pichldasbierchen.wels@linzertorte.at
Pierbach Dorfstra�e 1 4282 Pierbach Telefonnummer: 07267/8205 0676/87765292
Pinsdorf Moargasse 2 4812 Pinsdorf Telefonnummer: 07612/63952 0676/87765293 E-Mail: pichelsteiner@linzertorte.at
Pischelsdorf Pischelsdorf 2 5233 Pischelsdorf am Engelbach Telefonnummer: 07742/7207 0676/87765294 E-Mail: pischelsdorf@linzertorte.at


Denke mal dass ich keine Aufwändigen Perl-Operationen oder gar Module wie das Text::CVS_XS benoetige. Das wird wohl alles mit einer Split-Funktion gehen und einem entsprechenden Regex.

was meinste denn!?

lg lin
 

abgdf

Guru
lin schrieb:
Will das Ganze dann mal in ein Spreadsheet bringen.
Na, dann öffne das doch mal in OpenOffice-Calc als "Text - CSV", Trennzeichen "Leerzeichen". Mal sehen, was passiert.
Wenn es sauber formatiertes CSV ist, sollten z.B. alle "Email:"-Einträge untereinander stehen und sich leicht weiterverarbeiten lassen.

Wer ist "Oliver Albers"? Du wirst doch wohl keine Doppelpostings machen, sonst bin ich hier nämlich raus.
 

lin

Hacker
hallo abgdf

also - ich glaub ja nicht dass es sauberformatiertes csv ist. aber ich kann diesen test natürlich machen.

abgdf schrieb:
lin schrieb:
Will das Ganze dann mal in ein Spreadsheet bringen.
Na, dann öffne das doch mal in OpenOffice-Calc als "Text - CSV", Trennzeichen "Leerzeichen". Mal sehen, was passiert.
Wenn es sauber formatiertes CSV ist, sollten z.B. alle "Email:"-Einträge untereinander stehen und sich leicht weiterverarbeiten lassen.

aber dann wird er mir warhscheinlich zw. jedem einzelnen Wort einen trenner machen.

meld mich wieder
lg lin
 

abgdf

Guru
Herz-von-Hessen schrieb:
Kein schlechter Ansatz. Ergibt bei mir mit den Ausgangsdaten aber
Code:
Marias;Neustift;Neuseetifttown;28;
Marias;Puchheim;Gmunednertown;Strae;
Marias;Scharten;Scharetenstown;1;
Marias;Schmolln;Mareeia;Schmollntown;
Mattighofen;Rmeeeerstrae;12;5230;
Mauerkirchens;Pfarreeeehofstrae;4;5270;
Weiß nicht, ob er auch die Telefonnummern usw. mit dabeihaben wollte.
Da die Ausgangsdaten ausgerechnet durch Leerzeichen getrennt sind, und bei "Straße" auch manchmal Leerzeichen vorkommen, würde die Verarbeitung per Skript IMHO umständlich.
Am besten, er läßt sich bessere Ausgangsdaten geben, also solche, die durch Semikolon getrennt sind oder er macht das mit Calc von Hand. Bei ein paar hundert Datensätzen sollte das möglich sein, jedenfalls wenn's wichtig ist.

Oder bezogst Du Dich auf Fly_67?
lin hatte den Thread, der schon gelöst war, ja gekapert ...
 

lin

Hacker
hallo Herz-Von-Hessen, hallo abgdf,

Danke für die Beiträge - insbes @ Herz-von-Hessen für die ausführlichen und weitgehenden Hilfen.
@abgdf: er bezieht sich schon auf mich. - Srry - wg. des Kaperns des Threads. Dachte eben dass es hierher auch passen würde.
. -
Das verrückte ist ja imho auch, dass g ngf. auch Ortsnamen kommen koennen die aus mehreren woertern bestehen. Doch da
ist man auf der sicheren Seite - denn die (se) Daten stehen ja hinter der PLZ - die man ganz gut als klare u. eindeutig identifizierbare Sache festhalten kann.

vielen Dank nochmals für alles.. - ich meld mich morgen wieder

lg lin
 

abgdf

Guru
lin schrieb:
Ist es nicht auch moeglich dass mit dem Split-Befehl zu bewältigen?!
Hier nochmals die Beschreibung des Datensatzes u. seiner Struktur im Einzelnen:

Name (des Orts) - kann aus mehreren Worten bestehen

Strasse und Hausnummer
Postleitzahl und Ort: (Ortsname kann aus mehreren Worten bestehen- u. die PLZ sind immer (!!!!) vier Digits)
Telefonnummer: (die dann hinter einem Dopplepunkt folgt - lange Zahlenfolge)
Fax Nummer: (die dann hinter einem Dopplepunkt folgt - lange Zahlenfolge; Achtung diese ist nicht in jeder einzelnen Zeile drinne)
E-Mail: Achtung diese ist nicht in jeder einzelnen Zeile drinne - die kann auch mal fehlen. Ist aber imho nicht soo schlimm. Denn dann gibts einfach nur ein leeres Feld.

also es bleibt festzuhalten: Fax und E-Mail ist nicht in jeder Zeilen drinne.

- die PLZ sind immer (!!!!) vier Digits
- nach dem Namen (der Einrichtung) kommt dann die Strasse mit Hausnummer.

Man kann also davon ausgehen das die erste Zahl im String die Hausnummer ist. Ich denk dass man mit der Split-Funktion hier schonmals ansetzen kann / muss. Der REGEX muss so gebaut werden dass er ..auf die folgenden Besonderheiten "anspricht" :

- die Strasse u. die zugehoerige Hausnummer rausfindet. Diese DATEN kommen vor der ersten Zahl.
- danach - nach der ersten Zahl kommt dann (nach einer Leerstelle) die Postleitzahl (wie gesagt immer vier Digits lang)
Deine Angaben sind nicht gerade leicht nachzuvollziehen, da ist noch irgendwo die Rede von Doppelpunkten, die Du nicht mal mitgepostet hast. Ich hatte trotzdem Lust, das mal zu bauen, aber in Python. Es wird dabei auch zusätzlich eine Logdatei "regrouplog" im Skriptverzeichnis angelegt, also Vorsicht.
Code:
#!/usr/bin/env python
# coding: iso-8859-1

# regroup.py

import sys

if len(sys.argv) < 2:
    print "Usage: regroup.py data.txt."
    sys.exit(1)

sep = ";"

fh = open(sys.argv[1], "r")
a = fh.readlines()
fh.close()

fh2 = open("regrouplog", "w")
count = 1
for i in a:
    parts = {"strasse": "",
             "hausnr" : "",
             "plz"    : "",
             "ort"    : "",
              "telenr" : "",
             "fax"    : "",
             "email"  : ""}

    i = i.rstrip("\n")
    if not i:
        continue
    if not "Telefonnummer:" in i:
        print "\nFehler: Zeile " + str(count) + ":"
        print i
        print '"Telefonnummer:" fehlt.'
        sys.exit(2)
    b = i.split(" ")
    for u in range(len(b)):
        if len(b[u]) == 4 and b[u].isdigit():
            parts["plz"] = b[u]
            parts["hausnr"] = b[u - 1]
            x = 1
            while b[u + x] != "Telefonnummer:":
                if x > 1:
                    parts["ort"] += " "
                parts["ort"] += b[u + x]
                x += 1
        if b[u] == "Telefonnummer:":
            parts["telenr"] = b[u + 1]
        if b[u] == "FAX-Nummer:":
            parts["fax"] = b[u + 1]
        if b[u] == "E-Mail:":
            parts["email"] = b[u + 1]

    if i.startswith(parts["ort"]):
        i2 = i[len(parts["ort"]) + 1 : ]
        parts["strasse"] = i2.split(" " + parts["hausnr"])[0]
        out = [parts["ort"]]
    else:
        fh2.write("Warnung: Zeile " + str(count) + ":\n")
        fh2.write(i + "\n")
        fh2.write("Korrekte Trennung von erstem Ort und Straße nicht möglich.\n\n")
        parts["strasse"] = i.split(" " + parts["hausnr"])[0]
        parts["strasse"] = parts["strasse"].replace(" ", ";", 1)
        out = []

    out.extend ([parts["strasse"],
                 parts["hausnr"],
                 parts["plz"],
                 parts["ort"],
                "Telefonnummer:",
                parts["telenr"]])

    if parts["fax"]:
        out.append("FAX-Nummer:")
        out.append(parts["fax"])

    if parts["email"]:
        out.append("E-Mail:")
        out.append(parts["email"])

    print sep.join(out)

    count += 1

fh2.close()
Wahrscheinlich hab' ich keine Lust, jetzt zu hören "dies läuft nicht, das läuft nicht"; mußt es dann halt selber fixen. Deine Angaben und Deine Ausgangsdaten sind einfach zu wirr, um das wirklich so hinzubauen, wie Du Dir's vielleicht vorstellst.

Edit: Code verbessert; bzgl. des Encodings (UTF-8, ISO-8859-1, usw.) schau Dir mal bitte "recode" an.
 
Oben