Spalten mit awk neu anordnen

12

Ich versuche, die 7. Spalte meiner csv-Datei mit an das Ende zu verschieben

awk -F '{print $1,$2,$3,$4,$5,$6,$8,$9,$10,$11,$7}',OFS= "$file"

Dabei ist $ file eine CSV-Datei in einem Verzeichnis. Die Ausgabe ist jedoch

awk:                          ^ syntax error

Weiß jemand, wie man diesen Fehler behebt?

rmb
quelle
7
Wenn awk-Fehler angezeigt werden, muss das gesamte Objekt angezeigt werden. Das ^gibt den spezifischen Teil des Befehls an, an dem der Fehler aufgetreten ist.
Terdon

Antworten:

10

Die -FOption benötigt ein Argument: -F,zum Beispiel.

Das Ende des awkSkripts muss durch ein (Leerzeichen) mit den restlichen Parametern getrennt werden.

Wenn das Feldtrennzeichen ist ,und Sie es behalten möchten und wenn die Anzahl der Spalten konstant und kleiner oder gleich 11 ist, versuchen Sie Folgendes:

awk -F, '{print $1,$2,$3,$4,$5,$6,$8,$9,$10,$11,$7}' OFS=, "$file"
Jay Jargot
quelle
8
@anuribs sehr wenige Programme erlauben das. Der Standardweg ist command file > newfile && mv newfile file. Das sei gesagt, neuere Version von GNU awkdies zu unterstützen: gawk -i inplace '{blah blah}' file.
Terdon
1
Alternativ können Sie anstelle von mv newfile fileverwenden cat newfile > file ; rm -f newfile- dies bewahrt den Inode und die Berechtigungen von file.
cas
und es ist im Allgemeinen eine gute Idee, mktemptemporäre Dateinamen in Skripten zu verwenden, anstatt sie hart zu codieren. zBtf=$(mktemp) ; command file > "$tf" ; cat "$tf" > file ; rm -f "$tf"
cas
7

Kürzere Lösung wäre

awk -F',+' -v OFS=, '{$(NF+1)=$7; $7=""; $0=$0; $1=$1}1' file

Ich bin mir nicht sicher, ob ,+es in allen awkVersionen funktionieren wird, aber es funktioniert zumindest in GNU awk, auch im -cKompatibilitätsmodus.

Erläuterung:

  • $(NF+1)=$7: Zuerst fügen wir das 7. Feld am Ende der Zeile hinzu (könnte $12=$7in diesem Fall sein)
  • $7="": im nächsten Schritt wird das 7. Feld gelöscht (die umgebenden Begrenzer bleiben jedoch erhalten)
  • zu entfernen Trennzeichen müssen wir neu eingestellt gesamten Datensatz (via $0=$0) mehrere Kommas getrennt behandeln (dies über getan wird -F',+', hier +bedeutet ein oder mehrere Male) und auch aktuelle Datensatz über neu ordnen , $1=$1um Kraft den Wiederaufbau der Linie mit zuvor eingestellten Ausgabefeld Trennzeichen (durch eine Option gesetzt -v OFS=,)
  • Nachdem das Mischen abgeschlossen ist, können wir das Ergebnis mit ausdrucken 1

Beispiel Eingabe:

1,2,3,4,5,6,7,8,9,10,11

Ausgabe

1,2,3,4,5,6,8,9,10,11,7
jimmij
quelle
Was ist, wenn andere Spalten leer sind? Aber ja, FS ist ein regulärer Ausdruck in POSIX (wenn er mehrere Zeichen enthält), ,+sollte also funktionieren.
Random832
(1) Ich verstehe, dass es ein schwieriger Teil dieses Problems ist, die siebte Spalte der Eingabedaten "verschwinden" zu lassen und nicht nur auf Null zu setzen. Aber, wie Random832 sagt, verstopft Ihre Lösung leere Spalten (zum Beispiel all,ball,call,,,fallall,ball,call,fall). (2)  $(NF+1)=$7ist ein kluger Ansatz. IMHO $0 = $0 OFS $7ist ein bisschen klarer, nur ein paar Zeichen länger und es scheint dasselbe zu tun. Können Sie sich eine Situation vorstellen, in der $0 = $0 OFS $7nicht das Gleiche wie in Ihrem Code geschieht?
G-Man sagt, dass Monica
@ Random832 @ G-Man ja, einige Randfälle wie leere Felder, leere Zeilen oder NF <7 sollten separat behandelt werden, oder man sollte den Code neu anordnen. Dies ist nur eine Idee, keine "vollständige Lösung" für alle allgemeinen Fälle, die klar sein sollte. $0=$0 OFS $7ist wahrscheinlich identisch mit $(NF+1)=$7, aber nur mit dem Rest des Codes unverändert, nicht im Allgemeinen.
Jimmy
5

Wenn Sie mit drucken OFS=, also ohne Trennzeichen zwischen den Feldern, können Sie einfach den Wert von $7in einer Variablen speichern , $7auf leer setzen und die Zeile und die Variable direkt drucken. Sie müssen nicht alle Felder angeben:

$ cat file
1,2,3,4,5,6,7,8
$ awk -F, -vOFS= '{k=$7; $7=""; print $0,k}' file 
12345687
terdon
quelle
3

Sie meinen wahrscheinlich:

awk -F, -v OFS='' '{print $1,$2,$3,$4,$5,$6,$8,$9,$10,$11,$7}' "$file"
Michael Vehrs
quelle
Sie wissen, dass awknie die einfachen Anführungszeichen in sieht OFS='', nicht wahr? Sie können genauso gut einfach tippen OFS=; es ist genau das gleiche.
Wildcard
1
Ja, das merke ich. Ich mag es jedoch nicht, Aufgaben zu erledigen.
Michael Vehrs
3

Sie haben nicht ausdrücklich angegeben, dass Sie awk verwenden möchten, und Sie haben angegeben, dass Sie die von bereitgestellte In-Place-Bearbeitung verwenden möchten. Daher sed -ihier eine sed -iVariante. Normalerweise awkist es besser, mit Spalten zu arbeiten, aber in diesem Fall bevorzuge ich sed, weil es natürlich eine beliebige Anzahl von Spalten verarbeitet.

MOVECOL=7
N=$((MOVECOL-1))
sed -r -e "s/^(([^,]*,){$N})([^,]*),(.*)/\1\4,\3/" -i test.csv

Erläuterung:

  • -r wählt erweiterte reguläre Ausdrücke aus, um viele umgekehrte Schrägstriche zu vermeiden
  • Die erste Gruppe besteht aus $ N Wiederholungen von durch Kommas abgeschlossenen Zeichenfolgen, dh den Spalten vor derjenigen, die wir verschieben möchten, mit einem letzten Komma
  • Die zweite Gruppe ist die $ N-te Wiederholung, wir vergessen sie
  • Die dritte Gruppe ist die Spalte, die wir verschieben möchten, ohne das letzte Komma
  • Die vierte Gruppe besteht aus allen Spalten nach derjenigen, die wir verschieben möchten, ohne Komma
  • Wir ersetzen durch die erste Gruppe, die letzte Gruppe und die Spalte, die wir extrahiert haben, und fügen das Komma nach Bedarf ein.

Natürlich funktioniert dies nicht mit Dateien, die Kommas in Anführungszeichen verbergen (oder, schlimmer noch, sie maskieren), aber awk wird das auch nicht ohne ernsthafte Akrobatik schaffen. Wenn Sie dieses Problem haben, sind Sie mit dem perlModul Text:CSVoder dem pythonModul besser dran csv.

Law29
quelle
2

Einige awkVarianten (vorausgesetzt, Ihre Datei befindet sich in der Variablen $file)

  • Hier können Sie die gesamte Spalte durchlaufen, mit dem Feldtrennzeichen (OFS) drucken und das Satzendezeichen (ORS) am Ende der Zeile drucken.

    awk  -F',' -v OFS=,                                \
    '{for(i=1;i<=NF;i++) if (i!=7) printf "%s",$i OFS; \
    printf "%s",$7;printf ORS}' "$file"
  • Hier mit einem regulären Ausdruck und der gensub()Funktion

    gawk -F',+' -v OFS=, '{$0=gensub(/\s*\S+/,"",7) OFS $7}1' "$file"

    Tötung der 7 - ten Feld und es am Ende der Zeile zu drucken.

    • $0 ist der ganze Rekord
    • $nist der n- te Rekord
    • NF ist die Anzahl der Felder der aktuellen Zeile
    • OFS das Ausgabefeld Trennzeichen
    • ORS das Ausgabesatz-Abschlusszeichen
    • 1ist der Trick, um awk zu sagen trueund den default ( $0) zu drucken .

Aktualisieren ...

Ich habe fast vergessen, dass es möglich ist, alle Spalten nach der siebten zu verschieben.

awk  -F',' -v OFS=, '{tmp=$7; for(i=7;i<=NF;i++) $i=$(i+1); $NF=tmp}1 ' "$file"
Hastur
quelle
(1) Wäre OFS $7wohl robuster als "," $7. (2) Ich halte das ", " $7für falsch, sofern aus der Frage hervorgeht, dass das OP keine Leerzeichen nach den Kommas will. (Und wenn die Eingabedaten Leerzeichen nach den Kommas enthielten, $7würde dies bereits mit einem Leerzeichen beginnen, und Sie würden ein zusätzliches hinzufügen.)
G-Man sagt 'Reinstate Monica',
@ G-Man Es ging hauptsächlich darum, einige Ideen, einige Varianten vorzuschlagen. Vielen Dank für den Spot, dem ich zustimme OFS $7, nicht nur robuster, sondern auch allgemeiner ( "Eile macht Verschwendung" )
Hastur