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

Dateikonvertierung mit sed

Hallo,

ich will ein Skript schreiben, welches die folgende Datei konvertiert (von *.csv nach *.xls).


  • Dezimalzahlen: 123,456 nach 123.456
    Anführungszeichen: doppelte "" nach "
    Feldtrenner: ; nach ,
    Feldtrenner2: wenn sich innerhalb eines Feldes ein Zeichen befindet, weches nicht alnum
    bzw. alpha ist, so muss das Feld in "" gesetzt werden.


Code:
           Original                                                 geändert

PG;Produktgruppe;Absatz;Umsatz;            --->PG,Produktgruppe,Absatz,Umsatz,
0;- nicht zugeordnet-;7648;-198298,09;    ---> 0,"-nicht zugeordnet-",7648,-198298.09,
10000;L;2;--2,52;                           ---> 10000,L,2,"--2,52",
12000;Ver,s,a""n"d"kosten;49;+261,42;  ---> 12000,"Ver,s,a"n"d"kosten",49,+261.42,
11013;BIBB-K, test;5;v77,65;               --->..........usw...........
11021;F-BP\; Test;61;15,76;
11023;F-U,e\;test;23;42,03;
11043;O-G;4;14,47;
11044;O-V;24;55,09;
11050;StA Werbeartikel;38;736,20;
11051;StA Formulare;2412;35772,23;
11052;StA Stammbuch;3425;69914,64;
11053;StA Aktionsstammbuch;32;666,15;
11054;StA Traumappe;11;738,00;
11055;StA Zubehör;1534;609,10;
11061;V-A;26;249,33;
11091;V-BFZ;1;9,05;
11111;V-BUS + V-R;1;2,10;
11171;V-DIHT;2;49,82;
11201;V-F;4;111,78;
11261;V-OECD;1;40,65;
11271;V-Politik;2;27,86;
11401;Z-BWP;1298;16006,72;
11411;Z-DA;302;10001,88;
11451;Z-PRIMAR;854;15865,52;
11461;Z-WM;2;24,30;
11920;DIHT-I;55;114,34;

Bisheriges Skript:
Code:
#!/bin/sh
#
# cvs_sed: Konvertiert *.csv-Dateien nach *.xls-Dateien

if [ "$#" -lt 1 ]

        then
                echo "Aufruf: csv_sed dateiname(n).cvs"

        else
                while [ "$#" -gt 0 ]
                do

                        dateiname=$1

                        sed 's/\([0-9]\{1,\}\),\([0-9]\{1,\}\)/\1.\2/g' $dateiname > $(basename $dateiname .csv).xls
        
                        shift

                done

fi



Mit meinem bisherigen Skript werden die Dezimalkommas in Dezimalpunkte umgewandelt, aller dings habe ich nicht den leisesten Schimmer, wie ich den Rest anfangen soll.

Ich schreib die fehlenden Bedingungen mal im Textformat auf:

- durchsuche Zeile nach "
---> wenn Anführungszeichen doppelt ""
------->gebe nur eines der beiden Anführungszeichen aus
-----------> Feld in "" setzen

-durchsuche Zeile nach Komma
----> wenn gefunden
---------> Feld in "" setzen

- erst jetzt dürfen die Feldtrenner ; in , umgewandelt werden.

Danke für eure Hilfe(stellung)

Gruß
Simon
 
also *DAS* würde ich nicht mit sed machen.... das wäre mir zu umständlich, OK geht zwar :) , aber hier würde ich zu awk oder perl greifen...

Du musst die Zeile in die Elemente zerlegen und dann für jedes Element den entsprechenden Ersetzungsausdruck überlegen und ausführen.

und diese erste Zerlegung ist mit awk oder perl einfacher :)


Algo:
\forall lines in file
trenne Felner an ;
\forall Felder in Zeile:
wende passende Ersetzungen auf das Feld an (Dezimalzeichen, Sonderzeichen, ...)
schreibe Felder mit , als Trenner raus
 
mit awk machen, aber da ich in der Ausbildung bin zum FIAE und ich die verschiedenen Sprachen lernen soll und mit awk schon ein anderes Konvertierungsprogramm geschrieben hab, wird verlangt dass ich das Programm mit sed schreib. Ich bin noch Anfänger in sed obwohl ich mir jetzt schon die dritte Doku reingezogen hab weiß ich einfach nicht WO ICH ANFANGEN SOLL!!!
Bezieht sich Dein algo auf awk??? Könntest Du mir vllt Hilfe für das sed-Skripot geben??? Wäre echt nett ansonsten sitze ich ziemlichin der Tinte.... :cry:

Gruß Simon
 
Manches sollte über "Klammern" gehen. Das Skript wird aber schnell unübersichtlich...
Unten ist nur ein "Ansatz"

echo ";-123,456;+4567.67;12A.67" |
sed -e "s/;\([+-]*\)\([0-9]*\)[,.]\([0-9]*\);/;\1\2.\3;/g' \
-e "...nächste Aktion...."

Das erste ist zu lesen:
Erst ";"
\([+-]*\) Nur + oder - beliebig oft, durch \( \) als "\1" ansprechbar
\([0-9]*\) Nur Ziffern, beliebieg oft. als "\2" ansprechbar
[,.] Punkt oder Komma...
\([0-9]*\) Nur Ziffern, beliebieg oft. als "\3" ansprechbar
";" nächstes Feld...


/;\1\2.\3;/ Und dann ersetzen mit ";<vorzeichen><zahlteil1>.<zahlteil2>;

das "g" bewirkt das mehrfache umsetzen, nicht nur der ersten Zahl..

Als weiteren "Trick" könnte man auch "Hilfszeichen" verwenden.
Z.B. alle " erst in @ umwandeln => Rest kann ohne Verwechselung mit "" geklammert werden, am Ende @ => \"



Haveaniceday

PS: Das ganze sieht hinterher ganz schön krank aus...
 
Okay ich hab meinem Chef gezeigt wie das nachher ungefähr ausehen wird. Der hat nur mit den Augen gekullert. Also will ich das Skript so einfach wie möglich halten.
Was würden die Experten mir da empfehlen. Reines awk-Skript? Mischung aus awk/sed???
Gimme Tips.

Thx

Gruß
Simon

PS: Nicht böse sein, aber kommt bitte nicht mit Perl, das kann ich überhaupt noch nicht.
 
also *ich* würde das dann eher in awk machen (wenn denn nur awk oder sed zur Verfügung stehen...)

*aber* mein Algo lässt sich auch mit Shell + Sed machen, also:

Code:
INPUTFILE=$1
OUTPUTFILE=$2

# out leeren 
echo -n > $2

while read line 
do
  # zerlege Zeile in Elemente
  # *wenn* alle Zeilen der gleichen Stuktur folgen, 
  # aka die gleiche Feldzahl haben
  # hier also bei: PG;Produktgruppe;Absatz;Umsatz;
  # 4 Felder
  for i in $(seq 1 4)
  do
    FIELD[$i]=$( echo $line | cud -d ";" -f $i )
  done
  ### 

  ## hier alle Felder passend mit sed übersetzen... 
  ## (Klammerschlacht ;) )
  ## da brauchen dann nur die für jedes Feld relevanten Infos
  ## geändert zu werden. 
  
  ###
  # Schreibe neue Feldzeile:
  line=""
  for i in $(seq 1 4)
  do
    line="$line,${FIELD[$i]}"
  done
  echo $line >> $2
done < $1

d.h. das interessante ist dann die Regeln

Dezimalzahlen: 123,456 nach 123.456
Anführungszeichen: doppelte "" nach "
Feldtrenner2: wenn sich innerhalb eines Feldes ein Zeichen befindet, weches nicht alnum
bzw. alpha ist, so muss das Feld in "" gesetzt werden.
in sed-Regexes zu schreiben :) hier ist dann noch die Frage, müssen Anführungszeichen in der Eingabe "matchen" d.h. kannst Du stumpfsinning jedes " durch ' ersetzen oder musst Du zählen und passende Klammerungen finden ...

die Idenn von haveaniceday gehen in die richtige Richtung, aber Du solltest Dich mit den Zichenklassen vertraut machen (man sed, bzw. man grep), das macht das alles IMHO etwas lesbarer ;)

also für die Zahlenkonversion:
Code:
echo "123,456" | sed -e 's/\([[:digit:]]*\),\([[:digit:]]*\)/\1.\2/'
finde 2 Zahlenblöcke durch Komma getrennt und ersetze Komma durch Punkt

für die Felder mit nicht Alphanum -> das ist etwas trickier :) wenn zeile nonalnum enthält -> quoten:

Code:
echo "foo bar" | sed -e '/[^[:alnum:]]/{s/\(.*\)/"\1"/} '

das mit den Anführungszeichen überlasse ich Dir mal ;)
 
dukenuker schrieb:
Benutzen wir in der Firma aber einfach nicht. Ich darfs auch nicht.
Eine Argumentationshilfe: Mittlerweile wird perl auch für eine Menge Installationsscript usw. verwendet. Naja, wenn perl streng verboten ist, dann darf die Firma aber keine SuSE-Linux Standard-Installation fahren... :wink:

Eine Frage in den Raum: Kann man die perl-Pakete überhaupt aus der Installation entfernen, oder sind die ein Muss? Hab's noch nie ausprobiert...
 
notoxp schrieb:
Eine Frage in den Raum: Kann man die perl-Pakete überhaupt aus der Installation entfernen, oder sind
die ein Muss? Hab's noch nie ausprobiert...

das ist spontan schwer zu sagen, im LSB steht perl nicht drin... (http://www.linuxbase.org/futures/candidates/perl/index.html)

muss mal mit apt die dependencies von "basis"-paketen checken.
 
@ TeXpertIch verstehe nicht allesvon dem Skript; was heißt zum Beispiel der Befehl "cud"?? :oops:

Meinst Du das im Prinzip so, dass ich ein bash-Skript schreiben soll welches die Zeilen einliest, mit awk die Felder trennt und mit sed die Ersetzungen durchführt??

Danke für die Hilfe.

Gruß
Simon
 
dukenuker schrieb:
@ TeXpertIch verstehe nicht allesvon dem Skript; was heißt zum Beispiel der Befehl "cud"?? :oops:

:) Fipptehler sind schon blöd: "cut" ist das was da stehen sollte...

Meinst Du das im Prinzip so, dass ich ein bash-Skript schreiben soll welches die Zeilen einliest, mit awk die Felder trennt und mit sed die Ersetzungen durchführt??

Danke für die Hilfe.

nein, wenn es sed sein muss, dann steht doch da schon das Script, einfach auf die passenden FIELD[$i] Einträge die entsprechenden sed-Command loslassen...
 
Oben