CSV-Datei mit sed transformieren

7

Um einige Daten in ein bestimmtes Tool importieren zu können, muss ich eine CSV-Datei aus diesem Format transformieren

"data","data","data data","data","123"

in dieses Format

data;data;data data;data;123

Die Spalten enthalten nie ", ;oder ,aber es können Räume. Derzeit verwende ich Folgendes

sed -e 's/","/;/g' -e 's/"//g' input.csv > output.csv

Obwohl dies gut funktioniert, frage ich mich, ob dies eleganter gemacht werden kann, dh

  • Ist sed das richtige (Standard Unix) Tool für den Job?
  • Wäre es möglich, beide Ausdrücke zu einem zusammenzuführen?

Danke für deinen Beitrag!

middus
quelle

Antworten:

6
( tr , ';' | tr -d '"' ) < input.csv > output.csv

Ich würde Perl verwenden

perl -pe 'tr/,"/;/d' input.csv > output.csv

- aber diese spezielle Aufgabe ist nicht jenseits von sed. Sie können die beiden Ausdrücke nicht zusammenführen.

Ayrnieu
quelle
Vielen Dank für Ihre Antwort, IMHO zwei schöne Lösungen. Könnten Sie das erklären? in dem mit tr? Es ist nicht dasselbe wie [: punct:], richtig? man tr hilft mir nicht. Es scheint eine Geschmackssache zu sein, welche Antwort die beste ist. Wenn die Autoren der anderen Antworten keine Einwände erheben, werde ich dies als akzeptierte Antwort festlegen, da es für mich sehr elegant aussieht und die Community es bisher am höchsten bewertet hat.
Middus
Es macht mir nichts aus. Ich bin selbst Teil der Perl-Version. Perls Tr Felsen.
Quacksalber 11.
Entschuldigung - das sollte sein
ayrnieu
5

Was Sie bevorzugen (Perl, Sed, Awk), liegt bei Ihnen; Sie werden alle die Arbeit erledigen. Da Sie nach sed gefragt haben und die anderen veröffentlicht sind, können Sie loslegen. Dies ist eine einfachere Form Ihrer Regex und funktioniert mit Ihrer Beispielzeile:

$ sed -e 's/"//g; s/,/;/g' infile.csv > outfile.csv

Beachten Sie können die beiden Ausdrücke mit einem Semikolon nach jeder Substitution verbinden. Getestet mit GNU sed v4.1.5.

Hier sind Ihre ursprünglichen Ausdrücke verbunden:

$ sed -e 's/","/;/g; s/"//g' infile.csv > outfile.csv

Ich bin mir ziemlich sicher, dass es möglich ist, die beiden Substitutionen zusammenzuführen. Ich bin mir nicht sicher, was es ohne weiteres sein würde, und ich bin mir ziemlich sicher, dass das Ergebnis viel weniger lesbar sein wird als das Skript oben. Wenn mir etwas einfällt (oder jemand anderes in den Kommentaren wiegt), füge ich es hier hinzu.

Quacksalber
quelle
"Sie können die beiden Unterstationen verbinden" - Sie können nicht. Sie haben zwei Ausdrücke genommen und durch zwei Ausdrücke ersetzt.
Ayrnieu
sein original war '-e "foo" -e "bar"', ich habe sie zu '-e "foo; bar"' zusammengefügt. Das -e ist der Ausdruck, auf den ich mich beziehe, und angenommen, er bezog sich auf. Sie haben vielleicht Recht - ich habe falsch interpretiert, wonach er fragt -, aber Sie haben auch meine Aussage falsch verstanden.
Quacksalber Quijote
geklärt. Ich hoffe. :)
Quacksalber Quijote
Das ist cool, ich wusste nicht, dass man solche Ausdrücke einfach verbinden kann. Danke für deine Antwort!
Middus
4

Da Sie mit Aufzeichnungen zu tun haben, awkist dies sinnvoller. Das heißt, es ist nicht wirklich gut in CSV, da die Feldbegrenzer etwas variabel sind. Wenn Sie jedoch sicher sind, dass alle Felder von doppelten Anführungszeichen umgeben sind, funktioniert dies:

awk -F'","' 'BEGIN {OFS=";"} { gsub(/(^")|("$)/, ""); $1=$1; print }'

Dies setzt das Eingabefeldtrennzeichen von awk auf " ","" (einschließlich des inneren Satzes von Doppelzitaten). Dies funktioniert fast, außer dass Sie sich mit den führenden und nachfolgenden Doppelzitaten befassen müssen, die mit der gsubFunktion entfernt werden. Das $1=$1zwingt ihn, den Datensatz mit dem neuen Ausgabefeldtrennzeichen neu zu kompilieren, das wie ;im BEGIN-Block definiert wurde. Dann printdruckt den gesamten Datensatz aus.

Das ist etwas aufgeräumter:

awk -F '(^")|(",")|("$)' 'BEGIN {OFS=";"} { $1=$1; print }'

Es setzt das Eingabefeldtrennzeichen auf einen regulären Ausdruck, der die doppelten Anführungszeichen am Anfang und Ende des Datensatzes enthält, bewirkt aber auch, dass ein leeres Anfangs- und Nachlauffeld ausgedruckt wird. Sie können das nachfolgende Feld leicht entfernen:

awk -F '(^")|(",")|("$)' 'BEGIN {OFS=";"} { NF=NF-1; $1=$1; print }'

NFist die Anzahl der Felder, und die Reduzierung um eins springt vom letzten Feld ab. Aber ich kann mir keinen Weg vorstellen, das erste Feld abzuhacken.

Wenn Sie jedoch wissen, dass die Eingabe immer fünf Felder enthält, können Sie dies tun:

awk -F '(^")|(",")|("$)' 'BEGIN {OFS=";"} { print $2,$3,$4,$5,$6 }'

Beachten Sie, dass dadurch das $1=$1Konstrukt entfernt wird, das wir nur benötigen, wenn wir die (implizierten) $ 0 drucken.

Alles in allem würde ich wahrscheinlich Perl und eines der vielen verfügbaren CSV-Module auf CPAN verwenden .

wfaulk
quelle
Okay, das sieht etwas komplexer aus als die anderen Lösungen und ist nicht zu lesbar. Wenn ich in einem Jahr darauf stoßen würde, müsste ich mich wahrscheinlich fragen, was es tut. Es ist jedoch schön zu sehen, dass verschiedene Werkzeuge (awk, sed ...) für die Aufgabe geeignet sind. Vielen Dank für Ihre ausführliche Antwort. Ich nehme es als Einstiegspunkt, um nach anderen Problemen zu suchen.
Middus
es sieht schlimmer aus als es ist. Sobald Sie anfangen, ein wenig awk zu lernen, wird es einfacher zu lesen. :)
Quacksalber Quijote
Es ist komplexer, weil es intelligenter ist und versucht, Datensätze als Datensätze zu behandeln, anstatt mit Zeichenfolgen, die wie CSV aussehen, als Zeichenfolgen. Dies leidet unter einer viel geringeren „Komplexitätswand“ - ein Punkt, an dem eine kleine Ergänzung der Problembeschreibung eine enorme Änderung der Lösung bewirkt (z. B. die gesamte Lösung wegzuwerfen und eine
neue