Wie kann ich mit sed
oder eine CSV-Datei bearbeiten awk
?
- Löschen Sie eine Spalte
- Duplizieren Sie eine Spalte
- Eine Spalte verschieben
Ich habe einen großen Tisch mit über 200 Zeilen und bin damit nicht so vertraut sed
.
Wie kann ich mit sed
oder eine CSV-Datei bearbeiten awk
?
Ich habe einen großen Tisch mit über 200 Zeilen und bin damit nicht so vertraut sed
.
Antworten:
Abgesehen davon, wie Sie die Felder ausschneiden und neu anordnen (siehe die anderen Antworten), gibt es auch das Problem der skurrilen CSV-Felder.
Wenn Sie Ihre Daten in dieser „quirky“ Kategorie fällt, ein bisschen vor und Post - Filterung kann sich darum kümmern. Die Filter unten erfordern die Zeichen \x01
, \x02
, \x03
, \x04
nicht überall in Ihren Daten erscheinen.
Hier sind die Filter, die um einen einfachen Feldspeicherauszug gewickelt sind awk
.
Hinweis: Feld 5 hat ein ungültiges / unvollständiges Layout für Felder in Anführungszeichen, ist jedoch am Ende einer Zeile harmlos (abhängig vom CSV-Parser). Aber natürlich wäre es verursacht problematisch unexpedted Ergebnisse , wenn sie von ihrem aktuellen werden sollten getauscht weg End-of-Row - Position.
Aktualisieren; user121196 hat auf einen Fehler hingewiesen, wenn vor einem nachgestellten Anführungszeichen ein Komma steht. Hier ist die Lösung.
Die Daten
cat <<'EOF' >file
field one,"fie,ld,two",field"three","field,\",four","field,five
"15111 N. Hayden Rd., Ste 160,",""
EOF
Der Code
sed -r 's/^/,/; s/\\"/\x01/g; s/,"([^"]*)"/,\x02\1\x03/g; s/,"/,\x02/; :MC; s/\x02([^\x03]*),([^\x03]*)/\x02\1\x04\2/g; tMC; s/^,// ' file |
awk -F, '{ for(i=1; i<=NF; i++) printf "%s\n", $i; print NL}' |
sed -r 's/\x01/\\"/g; s/(\x02|\x03)/"/g; s/\x04/,/g'
Die Ausgabe:
field one
"fie,ld,two"
field"three"
"field,\",four"
"field,five
"15111 N. Hayden Rd., Ste 160,"
""
Hier ist der Vorfilter , erweitert mit Kommentaren.
Der Nachfilter ist nur eine Umkehrung von \x01
. \x02
, \x03
,\x04
sed -r '
s/^/,/ # add a leading comma delimiter
s/\\"/\x01/g # obfuscate escaped quotation-mark (\")
s/,"([^"]*)"/,\x02\1\x03/g # obfuscate quotation-marks
s/,"/,\x02/ # when no trailing quote on last field
:MC # obfuscate commas embedded in quotes
s/\x02([^\x03]*),([^\x03]*)/\x02\1\x04\2/g
tMC
s/^,// # remove spurious leading delimiter
'
Dies hängt davon ab, ob Ihre CSV-Datei Kommas nur für Trennzeichen verwendet oder ob Sie den Wahnsinn haben:
Feld eins, "Feld zwei", Feld drei
Dies setzt voraus, dass Sie eine einfache CSV-Datei verwenden:
Sie können eine einzelne Spalte auf viele Arten loswerden. Ich habe als Beispiel Spalte 2 verwendet. Am einfachsten ist wahrscheinlich die Verwendung cut
, mit der Sie ein Trennzeichen angeben -d
und welche Felder Sie drucken möchten -f
. Dies teilt es in Kommas und Ausgabefeld 1 und die Felder 3 bis zum Ende auf:
$ cut -d, -f1,3- /path/to/your/file
Wenn Sie tatsächlich verwenden müssen sed
, können Sie einen regulären Ausdruck schreiben, der mit den ersten n-1
Feldern, dem n
th-Feld und dem Rest übereinstimmt , und die Ausgabe des n
th -Felds überspringen (hier n
ist 2, damit die erste Gruppe nach 1
Zeit abgeglichen wird:) \{1\}
:
$ sed 's/\(\([^,]\+,\)\{1\}\)[^,]\+,\(.*\)/\1\3/' /path/to/your/file
Dafür gibt es eine Reihe von Möglichkeiten awk
, von denen keine besonders elegant ist. Sie können eine for
Schleife verwenden, aber mit dem nachgestellten Komma umzugehen ist ein Schmerz; ignorieren, dass es so etwas wie wäre:
$ awk -F, '{for(i=1; i<=NF; i++) if(i != 2) printf "%s,", $i; print NL}' /path/to/your/file
Ich finde es einfacher, Feld 1 auszugeben und dann substr
alles nach Feld 2 abzurufen:
$ awk -F, '{print $1 "," substr($0, length($1)+length($2)+3)}' /path/to/your/file
Dies ist jedoch für weiter entfernte Kolumnen ärgerlich
In sed
dieser ist im Wesentlichen der gleiche Ausdruck wie zuvor, aber Sie auch die Zielspalt erfassen und umfassen die Gruppe mehrfach in dem Ersatz:
$ sed 's/\(\([^,]\+,\)\{1\}\)\([^,]\+,\)\(.*\)/\1\3\3\4/' /path/to/your/file
In awk
der for-Schleife wäre es so etwas wie (wieder ohne das nachstehende Komma):
$ awk -F, '{
for(i=1; i<=NF; i++) {
if(i == 2) printf "%s,", $i;
printf "%s,", $i
}
print NL
}' /path/to/your/file
Der substr
Weg:
$ awk -F, '{print $1 "," $2 "," substr($0, length($1)+2)}' /path/to/your/file
(tcdyl hat in seiner Antwort eine bessere Methode gefunden )
Ich denke, die sed
Lösung folgt natürlich aus den anderen, aber es wird langsam lächerlich lang
awk
ist Ihre beste Wette. awk
druckt Felder nach Nummer, also ...
awk 'BEGIN { FS=","; OFS=","; } {print $1,$2,$3}' file
So entfernen Sie eine Spalte, ohne sie zu drucken:
awk 'BEGIN { FS=","; OFS=","; } {print $1,$3}' file
So ändern Sie die Reihenfolge:
awk 'BEGIN { FS=","; OFS=","; } {print $3,$1,$2}' file
Umleiten in eine Ausgabedatei.
awk 'BEGIN { FS=","; OFS=","; } {print $3,$1,$2}' file > output.file
awk
kann auch die Ausgabe formatieren.
Gegeben sei eine durch Leerzeichen getrennte Datei im folgenden Format:
1 2 3 4 5
Sie können Feld 2 mit awk wie folgt entfernen:
awk '{ sub($2,""); print}' file
was zurückkehrt
1 3 4 5
Ersetzen Sie Spalte 2 gegebenenfalls durch Spalte n.
So duplizieren Sie Spalte 2:
awk '{ col = $2 " " $2; $2 = col; print }' file
was zurückkehrt
1 2 2 3 4 5
So wechseln Sie zwischen Spalte 2 und 3:
awk '{temp = $2; $2 = $3; $3 = temp; print}'
was zurückkehrt
1 3 2 4 5
awk ist generell sehr gut im Umgang mit dem Feldbegriff . Wenn Sie mit einer CSV-Datei und nicht mit einer durch Leerzeichen getrennten Datei arbeiten, können Sie sie einfach verwenden
awk -F,
Definieren Sie Ihr Feld als Komma anstelle eines Leerzeichens (dies ist die Standardeinstellung). Es gibt eine Reihe guter awk-Ressourcen online, von denen ich eine unten als Quelle aufführe.
Quelle für # 3
awk
, aber es scheint eine durch Leerzeichen getrennte Ausgabe zu geben, auch wenn das Feldtrennzeichen ,
(das Feldtrennzeichen steuert nur, wie es die Eingabe behandelt)
Dies funktioniert zum Löschen
awk '{$2="";$0=$0;$1=$1}1'
Eingang
a b c d
Ausgabe
a c d