Modifica la larghezza della prima colonna nel file con un numero variabile di campi, usando awk


10

Capisco come usare la funzione printf di awk, ma non voglio specificare tutti i campi.

Ad esempio, supponiamo che questo sia il mio file:

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

Voglio formattarlo in modo che il primo campo di ogni record sia la larghezza di c11 - la cella più lunga nel primo campo:

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

Capisco che potrei specificare:

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

Supponiamo che io sappia cosa voglio che sia la larghezza della prima colonna, ma NON so quanti campi ci sono nel file. Fondamentalmente voglio fare qualcosa del tipo:

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

... e quindi stampare il resto dei campi nel loro formato originale.


Un altro modo per affrontarlo: sed 's/|/'' '' '' |/;s/\(...\) */\1/'(qui aggiungendo virgolette aggiuntive per inserire quei 3 spazi mentre i commenti SE comprimono gli spazi contigui in uno)
Stéphane Chazelas

Risposte:


14

È possibile utilizzare solo sprintfper riformattare $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

Conciso, puoi usare anche la formattazione dinamica con sprintf: ad esawk -vf1=3 'BEGIN{OFS=FS="|"}{$1=sprintf("%-*s",f1,$1)}1' test.txt
A.Danischewski,

@ A.Danischewski - Beh, dang. Ho fatto una vasta programmazione awk per ~ 17 anni e non l'ho mai trovata prima. Pensare a tutte le seccature mi avrebbe salvato.
Paul Sinclair,

6

Per capire la lunghezza più grande / più lunga del primo campo e quindi riformattare i valori nel campo in base a quella lunghezza, dovrai fare due passaggi separati sul file.

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

(nota che il file di input è specificato due volte sulla riga di comando)

Per i dati che presenti, questo produrrebbe

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

Il primo passaggio è gestito dal FNR == NRblocco, che tiene semplicemente traccia del campo più lungo visto finora ( mcontiene la lunghezza massima vista) e passa alla riga successiva.

Il secondo passaggio è gestito dall'ultimo blocco, che riformatta il primo campo usando sprintf(). La stringa di formato %-*ssignifica "una stringa giustificata a sinistra la cui larghezza è data dall'argomento intero prima dell'argomento che contiene la stringa effettiva".

Questo potrebbe ovviamente essere espanso per fare tutte le colonne trasformando lo scalare min un array che contiene la larghezza massima di ogni colonna:

$ 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

1

Il modo intelligente è ciò che Steeldriver ha suggerito . Il modo inutilmente contorto è di scorrere su ogni campo:

$ 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

Ma basta sprintf $1e basta.


1
Ce l'hai un po 'all'indietro, le piccole dichiarazioni concise sono generalmente più contorte. Iterare sui campi è meno contorto.
A.Danischewski,

1

In Awk è possibile utilizzare un "*" per generare una stringa di formato printf dinamica.

Se conosci già la lunghezza, puoi passare la lunghezza del campo per la prima colonna con -v.

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

Nota: se non si conosceva la lunghezza della prima colonna, è possibile memorizzare i valori in un array, quindi trovare la lunghezza massima del col lungo la strada e stamparla tutta nel blocco END.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.