Ändern Sie mit awk die Breite der ersten Spalte in der Datei mit einer variablen Anzahl von Feldern

10

Ich verstehe, wie man die printf-Funktion von awk verwendet, möchte aber nicht jedes Feld angeben.

Angenommen, dies ist meine Datei:

c1|c2|c3|c4|c5
c6|c7|c8|c9|c10
c11|c12|c13|c14|c15

Ich möchte es so formatieren, dass das erste Feld jedes Datensatzes die Breite von c11 hat - die längste Zelle im ersten Feld:

c1 |c2|c3|c4|c5
c6 |c7|c8|c9|c10
c11|c12|c13|c14|c15

Ich verstehe, dass ich angeben könnte:

awk -F"|" '{printf "%-3s%s%s%s%s\n", $1, $2, $3, $4, $5}' file > newfile

Nehmen wir an, ich weiß, wie breit die erste Spalte sein soll, aber ich weiß NICHT, wie viele Felder sich in der Datei befinden. Grundsätzlich möchte ich so etwas machen wie:

... '{printf "%-3s|", $1}'

... und drucken Sie dann die restlichen Felder im Originalformat aus.

Kayli O'Keefe
quelle
Eine andere Möglichkeit, dies zu beheben: sed 's/|/'' '' '' |/;s/\(...\) */\1/'(Hier werden zusätzliche Anführungszeichen hinzugefügt, um diese drei Leerzeichen einzufügen, wenn die SE-Kommentare zusammenhängende Leerzeichen in ein einziges zusammenfassen.)
Stéphane Chazelas

Antworten:

14

Sie können nur sprintfzum Neuformatieren verwenden $1.

Ex.

$ awk 'BEGIN{OFS=FS="|"} {$1 = sprintf("%-3s",$1)} 1' file
c1 |c2|c3|c4|c5
c6 |c7|c8|c9|c10
c11|c12|c13|c14|c15
Steeldriver
quelle
Kurz gesagt, Sie können die dynamische Formatierung auch mit sprintf verwenden: ZBawk -vf1=3 'BEGIN{OFS=FS="|"}{$1=sprintf("%-*s",f1,$1)}1' test.txt
A.Danischewski
@ A.Danischewski - Na, verdammt. Ich mache seit ~ 17 Jahren umfangreiche awk-Programme und bin noch nie auf diese gestoßen. An all die Probleme zu denken, hätte mich gerettet.
Paul Sinclair
6

Um die größte / längste Länge des ersten Felds zu ermitteln und dann die Werte im Feld entsprechend dieser Länge neu zu formatieren, müssen Sie zwei separate Durchgänge über die Datei durchführen.

awk 'BEGIN     { OFS = FS = "|" }
     FNR == NR { if (m < (n=length($1))) m = n; next }
               { $1 = sprintf("%-*s", m, $1); print }' file file

(Beachten Sie, dass die Eingabedatei zweimal in der Befehlszeile angegeben wird.)

Für die Daten, die Sie präsentieren, würde dies erzeugen

c1 |c2|c3|c4|c5
c6 |c7|c8|c9|c10
c11|c12|c13|c14|c15

Der erste Durchgang wird vom FNR == NRBlock verarbeitet, der einfach das längste bisher gesehene Feld verfolgt ( menthält die maximal gesehene Länge) und zur nächsten Zeile springt.

Der zweite Durchgang wird vom letzten Block behandelt, der das erste Feld mit neu formatiert sprintf(). Die Formatzeichenfolge %-*sbedeutet "eine linksbündige Zeichenfolge, deren Breite durch das Ganzzahlargument vor dem Argument angegeben wird, das die tatsächliche Zeichenfolge enthält".

Dies könnte natürlich auf alle Spalten erweitert werden, indem der Skalar min ein Array umgewandelt wird, das die maximale Breite jeder Spalte enthält:

$ awk 'BEGIN     { OFS = FS = "|" }
       FNR == NR { for (i=1; i<=NF; ++i) if (m[i] < (n=length($i))) m[i] = n; next }
                 { for (i=1; i<=NF; ++i) $i = sprintf("%-*s", m[i], $i); print }' file file
c1 |c2 |c3 |c4 |c5
c6 |c7 |c8 |c9 |c10
c11|c12|c13|c14|c15
Kusalananda
quelle
1

Der intelligente Weg ist das, was Steeldriver vorgeschlagen hat . Der unnötig verschlungene Weg besteht darin, über jedes Feld zu iterieren:

$ awk -F'|' '{printf "%-3s|",$1; for(i=2;i<NF;i++){printf "%s|",$i} printf "%s\n", $i}' file
c1 |c2|c3|c4|c5
c6 |c7|c8|c9|c10
c11|c12|c13|c14|c15

Aber einfach sprintf $1und fertig damit.

terdon
quelle
1
Sie haben es ein bisschen rückwärts, kleine prägnante Aussagen sind im Allgemeinen komplizierter. Das Iterieren über die Felder ist weniger kompliziert.
A. Danischewski
1

In Awk können Sie ein "*" verwenden, um eine dynamische Zeichenfolge im printf-Format zu generieren.

Wenn Sie die Länge bereits kennen, können Sie die Feldlänge für die erste Spalte mit -v übergeben.

awk -vcol1=3 'BEGIN{FS="|"}{for(i=1;i<=NF;i++){if(i==1)printf "%*-s%s",col1,$i,FS;else if(i!=NF)printf "%s%s",$i,FS;else printf "%s\n",$i;};}' test.txt

Hinweis: Wenn Sie nicht wissen, wie lang die erste Spalte ist, können Sie die Werte in einem Array speichern und dann die maximale Spaltenlänge ermitteln und alles im END-Block ausdrucken.

A. Danischewski
quelle